/* Palette + tokens (validated default palette from the dataviz skill) */
:root {
  color-scheme: light;
  --surface-1:      #fcfcfb;
  --surface-2:      #f3f2ee;
  --page:           #f9f9f7;
  --text-primary:   #0b0b0b;
  --text-secondary: #52514e;
  --text-muted:     #898781;
  --gridline:       #e1e0d9;
  --border:         rgba(11,11,11,0.10);
  --series-blue:    #2a78d6;
  --series-blue-soft: rgba(42,120,214,0.10);
  --status-good:     #0ca30c;
  --status-warning:  #fab219;
  --status-serious:  #ec835a;
  --status-critical: #d03b3b;
  --shadow-sm: 0 1px 2px rgba(11,11,11,0.06);
  --shadow-md: 0 6px 18px rgba(11,11,11,0.08);
  /* Brass/value accent (redesign, 2026-09-23) - a second, deliberately
     narrow-use color for the masthead moment (hero, primary buttons) so
     the whole app isn't just "blue everywhere". Chosen specifically to
     read as "a coin / a gain", the same instinct as a ticker flashing
     green, but warm rather than another green (green is already spoken
     for by .status-good/.range-return.positive) and NOT the generic
     AI-cliche terracotta (#D97757) or a flat gold gradient. Used only on
     the hero illustration and the primary call-to-action buttons - see
     the redesign README section for why everything else stays quiet. */
  --accent-gold: #b9861c;
  --accent-gold-soft: rgba(185,134,28,0.14);
  /* Two-role type system (redesign, 2026-09-23): one body/UI face used
     everywhere data needs to stay legible and dense, one display face
     reserved for exactly two moments (the landing hero title, the
     login/signup mastheads) so it reads as a deliberate signature rather
     than a generic "serif everywhere" reskin. Both are Hebrew-native
     Google Fonts (system-ui has no real personality in Hebrew, and most
     Latin display faces silently fall back to a system font for Hebrew
     glyphs entirely). */
  --font-body: 'Assistant', system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
  --font-display: 'Frank Ruhl Libre', Georgia, "Times New Roman", serif;
  /* Brand-identity duo (redesign v2, 2026-09-23) - deliberately separate
     from --series-blue, which stays untouched because it's the validated
     dataviz color actually used to draw price-chart lines and other data
     encodings elsewhere; changing a validated data color for a purely
     cosmetic refresh would be the wrong trade. These two live only in
     "this is the app's identity" chrome: the header ticker tape, the
     hero banner, and the menu tiles' category coding - never in a chart. */
  --brand-indigo: #372f8a;
  --brand-violet: #7c3aed;
  /* Menu-tile category coding (redesign v2) - four purposeful colors
     instead of one blue icon everywhere, each tied to what the section
     actually is (market data / personal money / research & analysis /
     dates) rather than picked for decoration - see the README section. */
  --cat-market: var(--series-blue);
  --cat-market-soft: var(--series-blue-soft);
  --cat-personal: var(--accent-gold);
  --cat-personal-soft: var(--accent-gold-soft);
  --cat-research: var(--brand-violet);
  --cat-research-soft: rgba(124,58,237,0.12);
  --cat-calendar: #0f8f7c;
  --cat-calendar-soft: rgba(15,143,124,0.12);
}
/* Dark palette, shared by two selectors on purpose (added 2026-09-15 with
   the manual toggle below): the `@media` block still auto-applies dark
   when the OS prefers it AND the person hasn't overridden that choice
   (:not([data-theme="light"])) - `:root[data-theme="dark"]` then wins
   regardless of OS preference once someone actually clicks the toggle.
   Keeping both means "system" still works for anyone who never touches
   the button at all (unchanged behavior from before this feature). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --surface-1:      #1a1a19;
    --surface-2:      #232322;
    --page:           #0d0d0d;
    --text-primary:   #ffffff;
    --text-secondary: #c3c2b7;
    --text-muted:     #898781;
    --gridline:       #2c2c2a;
    --border:         rgba(255,255,255,0.10);
    --series-blue:    #3987e5;
    --series-blue-soft: rgba(57,135,229,0.16);
    --status-good:     #0ca30c;
    --status-warning:  #fab219;
    --status-serious:  #ec835a;
    --status-critical: #d03b3b;
    --shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
    --shadow-md: 0 10px 26px rgba(0,0,0,0.4);
    --accent-gold: #d9a53a;
    --accent-gold-soft: rgba(217,165,58,0.18);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --surface-1:      #1a1a19;
  --surface-2:      #232322;
  --page:           #0d0d0d;
  --text-primary:   #ffffff;
  --text-secondary: #c3c2b7;
  --text-muted:     #898781;
  --gridline:       #2c2c2a;
  --border:         rgba(255,255,255,0.10);
  --series-blue:    #3987e5;
  --series-blue-soft: rgba(57,135,229,0.16);
  --status-good:     #0ca30c;
  --status-warning:  #fab219;
  --status-serious:  #ec835a;
  --status-critical: #d03b3b;
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
  --shadow-md: 0 10px 26px rgba(0,0,0,0.4);
  --accent-gold: #d9a53a;
  --accent-gold-soft: rgba(217,165,58,0.18);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  direction: rtl;
  font-family: var(--font-body);
  background: var(--page);
  color: var(--text-primary);
  line-height: 1.5;
  animation: pageFade .35s ease;
  position: relative;
}

@keyframes pageFade { from { opacity: 0; } to { opacity: 1; } }

/* Sitewide ambient background (redesign v2b, 2026-09-23) - direct request
   to extend the hero/login's colored, animated background feel to the
   REST of the app too, not just the two entry pages. Fixed behind
   everything, very low-opacity so it never fights table/text contrast
   (every card/table sits on its own fully opaque --surface-1, so this
   only shows through in the page margins around them), and slowly drifts
   rather than sitting static. Excluded on login/signup, which already
   have their own much stronger version of the same idea (.login-body). */
body:not(.login-body)::before {
  content: ''; position: fixed; inset: -10%; z-index: -1; pointer-events: none;
  background:
    radial-gradient(circle at 12% 18%, color-mix(in srgb, var(--brand-violet) 12%, transparent), transparent 32%),
    radial-gradient(circle at 88% 12%, color-mix(in srgb, var(--cat-calendar) 10%, transparent), transparent 30%),
    radial-gradient(circle at 80% 88%, color-mix(in srgb, var(--brand-indigo) 12%, transparent), transparent 34%);
  animation: bgDrift 40s ease-in-out infinite alternate;
}
@keyframes bgDrift {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(-2%, 2%) scale(1.08); }
}
@keyframes fadeUp { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
@keyframes popIn { from { opacity: 0; transform: scale(.85); } to { opacity: 1; transform: scale(1); } }
@keyframes pulseDot { 0% { box-shadow: 0 0 0 0 currentColor; } 70% { box-shadow: 0 0 0 6px transparent; } 100% { box-shadow: 0 0 0 0 transparent; } }
@keyframes shake {
  10%, 90% { transform: translateX(-1px); }
  20%, 80% { transform: translateX(2px); }
  30%, 50%, 70% { transform: translateX(-4px); }
  40%, 60% { transform: translateX(4px); }
}

/* Respect a person's OS-level "reduce motion" setting (redesign, 2026-09-23)
   - applies to every animation/transition in this file at once, including
   the pre-existing fadeUp/popIn/pulseDot ones, rather than special-casing
   only the new hero reveal. Content that an animation fades in from
   opacity:0 (hero-title/subtitle/art, .card, tbody tr, ...) still ends up
   fully visible - it just gets there in ~0ms instead of animating there,
   so nothing is left invisible for someone with this preference on. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

header.site {
  position: sticky; top: 0; z-index: 30;
  background: color-mix(in srgb, var(--surface-1) 90%, transparent);
  backdrop-filter: blur(10px) saturate(1.4);
  -webkit-backdrop-filter: blur(10px) saturate(1.4);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 1px 0 var(--border), 0 6px 20px rgba(11,11,11,0.05);
  padding: 12px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 14px;
}
/* Thin accent line under the header - a small "brand mark" touch common on
   professional finance dashboards (Bloomberg/TradingView-style), subtle
   enough not to compete with the content below it. */
header.site::after {
  content: ''; position: absolute; inset-inline: 0; bottom: -1px; height: 2px;
  background: linear-gradient(90deg, var(--series-blue), color-mix(in srgb, var(--series-blue) 15%, transparent) 65%, transparent);
  pointer-events: none;
}
.brand { position: relative; display: flex; align-items: center; gap: 10px; color: inherit; text-decoration: none; }
.brand:hover { text-decoration: none; }
.brand-icon {
  display: flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 10px;
  background: linear-gradient(135deg, color-mix(in srgb, var(--series-blue) 80%, white 20%), var(--series-blue));
  color: #fff;
  box-shadow: 0 3px 10px var(--series-blue-soft), inset 0 1px 0 rgba(255,255,255,.25);
  flex: none;
}
.brand-icon svg { width: 20px; height: 20px; }
.brand-text { display: flex; flex-direction: column; line-height: 1.25; }
.brand-title { font-size: 16px; font-weight: 700; }
header.site .subtitle { color: var(--text-secondary); font-size: 12px; }

/* Section dividers give the toolbar a "grouped" structure - brand / nav /
   search / account each read as a distinct block instead of one loose row
   of items, which is the bulk of what makes a header read as organized. */
.header-divider { width: 1px; height: 24px; background: var(--border); flex: none; }
@media (max-width: 860px) { .header-divider { display: none; } }

/* Below ~640px, `space-between` on a wrapped flex row leaves an oddly
   placed empty gap next to whichever group lands alone on its own line -
   stacking everything into a single column instead reads cleanly at phone
   widths. Each stacked group still wraps its OWN children (see .site-nav
   and .header-accessories below) rather than forcing a fixed width, which
   is what used to make the header wider than the phone screen and push the
   whole page into horizontal scroll (position: sticky made that scrolled-
   off state especially visible, since it sat pinned at the top). */
@media (max-width: 640px) {
  header.site { flex-direction: column; align-items: stretch; justify-content: flex-start; }
  .site-nav { align-self: stretch; }
  .site-search { width: 100%; max-width: none; }
}

/* Nav reads as a segmented control (a defined, bordered group with an
   active-item highlight) rather than a loose row of links - this is the
   pattern professional dashboards use for primary section switching, and
   it makes "where am I" immediately legible. On a phone, 5 labeled items
   are wider than the screen - min-width:0 lets this flex item actually
   shrink instead of forcing header.site (and the whole page, since the
   header is position:sticky) to overflow horizontally, and flex-wrap lets
   the pills reflow onto a second row rather than needing a horizontal
   swipe to reach the last item. */
.site-nav {
  display: flex; align-items: center; flex-wrap: wrap; gap: 2px;
  padding: 3px; border-radius: 10px; min-width: 0; max-width: 100%;
  background: var(--surface-2); border: 1px solid var(--border);
}
.nav-link {
  display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600;
  padding: 7px 13px; border-radius: 8px; color: var(--text-secondary);
  white-space: nowrap;
  transition: background .18s ease, color .18s ease, box-shadow .18s ease;
}
.nav-link svg { width: 15px; height: 15px; opacity: .8; transition: opacity .18s ease; }
.nav-link:hover { text-decoration: none; color: var(--text-primary); background: color-mix(in srgb, var(--surface-1) 65%, transparent); }
.nav-link.active {
  color: #fff; background: var(--series-blue);
  box-shadow: 0 2px 8px var(--series-blue-soft), inset 0 1px 0 rgba(255,255,255,.2);
}
.nav-link.active svg { opacity: 1; }
.nav-link.active:hover { background: var(--series-blue); }

.site-search { position: relative; display: flex; align-items: center; width: 220px; max-width: 40vw; }
.site-search-icon { position: absolute; inset-inline-start: 11px; width: 15px; height: 15px; color: var(--text-muted); pointer-events: none; }
.site-search input {
  width: 100%; padding: 7px 12px 7px 12px; padding-inline-start: 32px; padding-inline-end: 30px;
  border-radius: 9px;
  border: 1px solid var(--border); background: var(--surface-2); color: var(--text-primary);
  font-size: 13px; font-family: inherit; transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
}
.site-search input:focus { outline: none; border-color: var(--series-blue); background: var(--surface-1); box-shadow: 0 0 0 3px var(--series-blue-soft); }
.site-search-shortcut {
  position: absolute; inset-inline-end: 9px; font-size: 10.5px; font-weight: 700; line-height: 1;
  color: var(--text-muted); background: var(--surface-1); border: 1px solid var(--border);
  border-radius: 5px; padding: 3px 6px; pointer-events: none; font-family: inherit;
}
.site-search-results {
  position: absolute; top: calc(100% + 6px); inset-inline: 0; z-index: 20;
  background: var(--surface-1); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: var(--shadow-md); overflow: hidden; min-width: 260px;
}
.site-search-item {
  display: flex; align-items: center; gap: 8px; padding: 9px 12px; font-size: 13px;
  color: var(--text-primary); border-bottom: 1px solid var(--gridline);
  transition: background .12s ease;
}
.site-search-item:last-child { border-bottom: none; }
.site-search-item:hover { text-decoration: none; background: var(--surface-2); }
.site-search-item-name { flex: 1; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.current-username {
  font-size: 13px; font-weight: 600; color: var(--text-secondary);
  padding: 7px 4px;
}

.logout-btn {
  display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600;
  padding: 7px 13px; border-radius: 9px; border: 1px solid var(--border);
  color: var(--text-secondary); background: var(--surface-2);
  transition: background .15s ease, color .15s ease, border-color .15s ease, transform .1s ease, box-shadow .15s ease;
}
.logout-btn svg { width: 15px; height: 15px; }
.logout-btn:hover {
  text-decoration: none; color: var(--status-critical);
  border-color: var(--status-critical); background: color-mix(in srgb, var(--status-critical) 8%, var(--surface-2));
  box-shadow: var(--shadow-sm); transform: translateY(-1px);
}
.logout-btn:active { transform: translateY(0) scale(.96); }

/* Groups the market-status badges, online-count badge, admin link,
   username and logout button into ONE cluster that wraps together - on a
   phone screen (where header.site itself stacks into a column - see the
   @media(max-width:640px) rule above), this reads as a couple of compact
   wrapped rows of small pills instead of each item becoming its own
   full-width stacked row, which used to make the header very tall. */
.header-account { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px; }

/* Feature: market open/closed status badges (header, every page) - reuses
   the same "colored dot + label" language as .status/.live-badge elsewhere
   rather than inventing a new visual vocabulary for "is it open". */
.market-status-group { display: flex; align-items: center; gap: 6px; }
.market-status-badge {
  display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 600;
  padding: 6px 10px; border-radius: 999px; border: 1px solid var(--border);
  color: var(--text-secondary); background: var(--surface-2); cursor: default;
}
.market-status-badge .dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--status-critical); }
.market-status-badge.open { color: var(--status-good); border-color: color-mix(in srgb, var(--status-good) 35%, var(--border)); }
.market-status-badge.open .dot { background: var(--status-good); animation: pulseDot 1.8s infinite; }
.market-status-badge.closed .dot { background: var(--text-muted); }

/* Feature: online session count badge (header, every page) - see
   web/app.py online_count() for exactly what this counts/doesn't count. */
.online-badge {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600;
  padding: 6px 10px; border-radius: 999px; border: 1px solid var(--border);
  color: var(--text-secondary); background: var(--surface-2); cursor: default;
}
.online-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--series-blue); animation: pulseDot 2.2s infinite; }

main { max-width: 1000px; margin: 0 auto; padding: 24px; }

.filters {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 20px;
  margin-bottom: 18px; padding: 14px 16px;
  background: var(--surface-1); border: 1px solid var(--border); border-radius: 12px;
  animation: fadeUp .3s ease;
}
.filter-group { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.filter-label {
  display: inline-flex; align-items: center; gap: 4px;
  color: var(--text-muted); font-size: 12px; font-weight: 600; margin-inline-end: 2px;
}
.filter-icon { width: 13px; height: 13px; flex: none; }
.filter-chip {
  display: inline-block; font-size: 12.5px; font-weight: 600; padding: 6px 13px; border-radius: 8px;
  border: 1px solid var(--border); color: var(--text-secondary);
  background: var(--surface-2);
  transition: background .15s ease, color .15s ease, border-color .15s ease, transform .1s ease, box-shadow .15s ease;
}
.filter-chip:hover { text-decoration: none; border-color: var(--series-blue); color: var(--text-primary); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.filter-chip:active { transform: translateY(0) scale(.97); }
.filter-chip.active {
  background: var(--series-blue); border-color: var(--series-blue); color: #fff; font-weight: 700;
  box-shadow: 0 2px 10px var(--series-blue-soft), inset 0 1px 0 rgba(255,255,255,.22);
}
.filter-meta { display: flex; align-items: center; gap: 10px; margin-inline-start: auto; }
.count-badge {
  font-size: 12px; font-weight: 600; color: var(--series-blue);
  background: var(--series-blue-soft); padding: 4px 10px; border-radius: 999px;
}
.as-of-date { color: var(--text-muted); font-size: 12px; }

.currency-symbol { color: var(--text-muted); font-size: 12px; margin-inline-start: 2px; }
.live-badge {
  display: inline-flex; align-items: center; gap: 4px; font-size: 9.5px; font-weight: 700;
  letter-spacing: .04em; color: var(--status-good); background: color-mix(in srgb, var(--status-good) 12%, transparent);
  border-radius: 999px; padding: 1px 7px 1px 6px; margin-inline-start: 6px; vertical-align: middle;
}
.live-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--status-good); animation: pulseDot 1.6s infinite; }
/* Pre-market/after-hours badge (feature 2026-09) - same shape as
   .live-badge, amber instead of green so it reads as "a real quote, but
   NOT the regular session" at a glance. */
.session-badge {
  display: inline-flex; align-items: center; gap: 4px; font-size: 9.5px; font-weight: 700;
  letter-spacing: .04em; color: var(--status-warning); background: color-mix(in srgb, var(--status-warning) 14%, transparent);
  border-radius: 999px; padding: 1px 7px 1px 6px; margin-inline-start: 6px; vertical-align: middle;
}
.session-badge .live-dot { background: var(--status-warning); }
.price-date { color: var(--text-muted); font-size: 11px; }
.latest-price {
  float: left; text-transform: none; letter-spacing: normal; font-size: 15px;
  font-weight: 700; color: var(--text-primary); font-variant-numeric: tabular-nums;
}

.range-return {
  display: inline-flex; align-items: center; font-size: 12.5px; font-weight: 700;
  padding: 3px 9px; border-radius: 999px; margin-inline-start: 8px;
  font-variant-numeric: tabular-nums; text-transform: none; letter-spacing: normal;
}
.range-return.positive { color: var(--status-good); background: color-mix(in srgb, var(--status-good) 12%, transparent); }
.range-return.negative { color: var(--status-critical); background: color-mix(in srgb, var(--status-critical) 10%, transparent); }
/* A heuristic-scored headline (analysis/news_sentiment.py) that landed in
   the neutral 41-59 band is still a real reading, not a missing one - this
   gives it a plain, uncolored pill instead of no badge at all (added
   2026-09-23 alongside per-headline sentiment - see news.html/company.html). */
.range-return.neutral { color: var(--text-secondary); background: var(--surface-2); }

.logo-badge {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; color: #fff; font-weight: 700; overflow: hidden; flex: none;
  vertical-align: middle;
}
.logo-badge img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
  background: #fff; padding: 2px;
}
.logo-badge-sm { width: 22px; height: 22px; font-size: 10px; }
.logo-badge-lg { width: 34px; height: 34px; font-size: 15px; margin-inline-end: 8px; }
.ticker-cell { display: inline-flex; align-items: center; gap: 8px; }

.disclaimer {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 14px;
  color: var(--text-secondary);
  font-size: 13px;
  margin-bottom: 20px;
}

table { width: 100%; border-collapse: collapse; background: var(--surface-1); border-radius: 12px; overflow: hidden; box-shadow: var(--shadow-sm); }
th, td { padding: 11px 12px; text-align: right; border-bottom: 1px solid var(--gridline); font-size: 14px; }
th { color: var(--text-muted); font-weight: 600; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em; background: var(--surface-2); }
tbody tr {
  cursor: pointer; opacity: 0; animation: fadeUp .3s ease forwards;
  transition: background .15s ease, transform .15s ease, box-shadow .15s ease;
}
tbody tr:hover { background: var(--series-blue-soft); transform: translateY(-1px); box-shadow: var(--shadow-sm); position: relative; }
tbody tr:last-child td { border-bottom: none; }
td.num { font-variant-numeric: tabular-nums; }

.badge-market {
  display: inline-block; font-size: 11px; padding: 2px 8px; border-radius: 999px;
  border: 1px solid var(--border); color: var(--text-secondary);
}

.status { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; font-size: 13px; }
.status .dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.status.good .dot { background: var(--status-good); color: var(--status-good); animation: pulseDot 1.8s infinite; }
.status.warning .dot { background: var(--status-warning); }
.status.serious .dot { background: var(--status-serious); }
.status.critical .dot { background: var(--status-critical); }
.status.good { color: var(--status-good); }
.status.warning { color: color-mix(in srgb, var(--status-warning) 70%, var(--text-primary)); }
.status.serious { color: var(--status-serious); }
.status.critical { color: var(--status-critical); }

.card {
  background: var(--surface-1); border: 1px solid var(--border); border-radius: 12px;
  padding: 18px 20px; margin-bottom: 18px; box-shadow: var(--shadow-sm);
  animation: fadeUp .3s ease;
}
.card h2 { font-size: 14px; color: var(--text-muted); text-transform: uppercase; margin: 0 0 12px; letter-spacing: .02em; }
.company-title { font-size: 18px !important; text-transform: none !important; color: var(--text-primary) !important; letter-spacing: 0 !important; }

.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 14px; }
.stat-tile { padding: 10px 12px; border-radius: 10px; background: var(--surface-2); transition: transform .15s ease; }
.stat-tile:hover { transform: translateY(-2px); }
.stat-tile .value { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
.stat-tile .label { color: var(--text-secondary); font-size: 12px; margin-top: 2px; }

.notes { font-size: 14px; color: var(--text-secondary); }

.overview-stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 14px;
  margin-bottom: 6px;
}
.overview-stats .stat-tile { background: var(--surface-1); border: 1px solid var(--border); box-shadow: var(--shadow-sm); }
.overview-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 18px; }
.overview-cols .card { margin-bottom: 0; }
/* overflow-x:auto alone isn't enough: the base `table { width: 100% }` rule
   above means the table happily SHRINKS to fit any container instead of
   ever needing to scroll, so narrow columns just wrap their text into an
   unreadable, squished mess on a phone instead of triggering a real
   horizontal scrollbar. min-width forces the table to keep its natural
   layout (still fluid/100% wide on anything roomier than that) and only
   actually scroll below it - confirmed necessary via a real mobile-width
   screenshot of the dashboard table, not just "the CSS looks right". */
.table-scroll { overflow-x: auto; }
.table-scroll table { min-width: 640px; }
/* The generic min-width above is tuned for the BIG multi-column dashboard
   table (confirmed via a real mobile screenshot, see the comment above) -
   it's too wide for the compact 2-3 column tables inside the /overview
   page's paired cards (.overview-cols), where it was forcing each table
   wider than its own card and pushing the last column (the score / %
   change badge) out of view, needing an unlabeled horizontal scroll to
   even see it (found the same way - via a real screenshot, not just
   reading the CSS). These short tables (icon+ticker, name, one badge) never
   need the squish-protection the dashboard table does, so let them shrink
   to their card's actual width instead. */
.overview-cols .card .table-scroll table { min-width: 0; }

.sector-count {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  min-width: 26px; padding: 2px 8px; border-radius: 6px; font-weight: 700; font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
/* Fixed-opacity tinted background (like .live-badge/.range-return elsewhere)
   so the count stays readable no matter how many rows fall in this cell -
   an earlier version scaled the background opacity by the cell's own share
   of the row, which made the text and its background converge to the same
   color and disappear at high counts. .sector-count-zero is a plain muted
   number with no pill, so a sea of "0"s doesn't compete for attention. */
.sector-count.good { color: var(--status-good); background: color-mix(in srgb, var(--status-good) 14%, transparent); }
.sector-count.warning { color: color-mix(in srgb, var(--status-warning) 70%, var(--text-primary)); background: color-mix(in srgb, var(--status-warning) 16%, transparent); }
.sector-count.serious { color: var(--status-serious); background: color-mix(in srgb, var(--status-serious) 13%, transparent); }
.sector-count.critical { color: var(--status-critical); background: color-mix(in srgb, var(--status-critical) 12%, transparent); }
.sector-count-zero { color: var(--text-muted); font-size: 12.5px; font-variant-numeric: tabular-nums; }

/* Slim CTA card on /overview pointing at the dedicated /sector-heatmap page
   (feature added 2026-09-28, moved off this page the same day - see
   web/app.py's sector_heatmap() docstring for why). */
.heatmap-cta-card {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
}
.heatmap-cta-card .btn-primary { flex: none; white-space: nowrap; }

/* Sector heatmap (/sector-heatmap, feature added 2026-09-28, REBUILT the
   same day - see that route's docstring) - a genuine grid of small colored
   SQUARES, one per ticker, grouped under its sector heading. Square
   background/color come inline from heatmap_square_style() in web/app.py
   (explicit computed hex, not a theme-token tint) - this stylesheet only
   lays the grid out and never sets square color itself. Fixed-size squares
   via CSS grid auto-fill (not flex-wrap) so a 90-ticker sector still reads
   as a tidy mosaic instead of ragged wrapped rows. */
.heatmap-page-sector { margin-bottom: 22px; }
.heatmap-page-sector:last-child { margin-bottom: 0; }
.heatmap-sector-head {
  display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px;
}
.heatmap-sector-name { font-weight: 700; font-size: 14px; }
.heatmap-sector-count { color: var(--text-muted); font-size: 12px; }
.heatmap-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(46px, 1fr));
  gap: 3px;
}
.heatmap-square {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  aspect-ratio: 1 / 1; border-radius: 5px; text-decoration: none;
  font-weight: 700; font-size: 10px; line-height: 1.15; overflow: hidden;
  padding: 2px; transition: transform .1s ease, box-shadow .1s ease;
}
.heatmap-square:hover { transform: scale(1.12); box-shadow: var(--shadow-md); z-index: 1; position: relative; }

.investor-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px;
}
.investor-card {
  background: var(--surface-1); border: 1px solid var(--border); border-radius: 12px;
  padding: 16px 18px; color: inherit; text-decoration: none; display: block;
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
  animation: fadeUp .3s ease;
}
.investor-card:hover { text-decoration: none; transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--series-blue); }
.investor-card:active { transform: translateY(0) scale(.99); }
.investor-name { font-size: 15px; font-weight: 700; margin-bottom: 2px; }
.investor-firm { font-size: 12.5px; color: var(--text-secondary); margin-bottom: 10px; }
.investor-stats { display: flex; align-items: baseline; gap: 6px; }
.investor-stats .value { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; }
.investor-stats .label { font-size: 11.5px; color: var(--text-muted); }
.investor-period { margin-top: 8px; font-size: 11.5px; color: var(--text-muted); }
.investor-notes { margin-top: 8px; font-size: 11.5px; color: var(--text-muted); font-style: italic; }
.investor-empty { margin-top: 8px; font-size: 12.5px; color: var(--text-muted); }

.pct-bar-wrap { display: flex; align-items: center; gap: 8px; min-width: 90px; }
.pct-bar-track { flex: 1; height: 6px; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.pct-bar-fill { height: 100%; border-radius: 999px; background: var(--series-blue); }
.pct-bar-label { font-size: 12px; color: var(--text-secondary); font-variant-numeric: tabular-nums; white-space: nowrap; }

.ticker-pill {
  display: inline-flex; align-items: center; font-size: 12px; font-weight: 700;
  padding: 3px 9px; border-radius: 999px; background: var(--series-blue-soft); color: var(--series-blue);
}
.ticker-pill:hover { text-decoration: none; filter: brightness(1.1); }
.no-match { color: var(--text-muted); font-size: 12px; }

.change-badge {
  display: inline-flex; align-items: center; font-size: 11.5px; font-weight: 700;
  padding: 3px 9px; border-radius: 999px; white-space: nowrap;
}
.change-badge.change-new { color: var(--series-blue); background: var(--series-blue-soft); }
.change-badge.change-increased { color: var(--status-good); background: color-mix(in srgb, var(--status-good) 12%, transparent); }
.change-badge.change-decreased { color: color-mix(in srgb, var(--status-warning) 70%, var(--text-primary)); background: color-mix(in srgb, var(--status-warning) 15%, transparent); }
.change-badge.change-sold { color: var(--status-critical); background: color-mix(in srgb, var(--status-critical) 10%, transparent); }

a { color: var(--series-blue); text-decoration: none; }
a:hover { text-decoration: underline; }

.back-link {
  display: inline-flex; align-items: center; gap: 6px; margin-bottom: 16px; font-size: 13px;
  font-weight: 600;
}
.back-link svg { width: 15px; height: 15px; transition: transform .15s ease; }
.back-link:hover svg { transform: translateX(3px); }
[dir="rtl"] .back-link:hover svg { transform: translateX(-3px); }

.chart-wrap { position: relative; }
.axis-label { font-size: 9px; fill: var(--text-muted); font-family: inherit; }
.chart-line { filter: drop-shadow(0 1px 3px rgba(0,0,0,.18)); }
.chart-tooltip {
  position: absolute; pointer-events: none; background: var(--text-primary); color: var(--surface-1);
  font-size: 12px; padding: 4px 8px; border-radius: 6px; transform: translate(-50%, -120%);
  white-space: nowrap; opacity: 0; transition: opacity .1s;
}
.chart-tooltip.visible { opacity: 1; }

footer { text-align: center; color: var(--text-muted); font-size: 12px; padding: 24px; }

/* ---------- Login page ---------- */
.login-body {
  margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center;
  position: relative; overflow: hidden;
  /* Same vivid identity as the home page hero (redesign v2, 2026-09-23) -
     the entry point to the app should already feel like the destination,
     not a pale, unrelated waiting room. */
  background: linear-gradient(135deg, var(--brand-indigo) 0%, var(--brand-violet) 60%, #a12a92 100%);
  font-family: var(--font-body);
  color: var(--text-primary);
  direction: rtl;
  padding: 20px;
}
.login-body::before, .login-body::after {
  content: ''; position: fixed; border-radius: 50%; filter: blur(70px); z-index: 0; opacity: .45;
  animation: heroBlobFloat 18s ease-in-out infinite alternate;
}
.login-body::before { width: 380px; height: 380px; background: var(--accent-gold); top: -120px; inset-inline-end: -100px; }
.login-body::after  { width: 420px; height: 420px; background: #22d3ee; bottom: -160px; inset-inline-start: -110px; animation-delay: -9s; }
.login-wrap { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; gap: 14px; width: 100%; max-width: 380px; }
/* Faint candlestick watermark behind the login/signup card (redesign,
   2026-09-23) - ties the entry point to the same visual language as the
   dashboard hero (a real candlestick chart, not a generic blob/gradient)
   without competing with the form itself: fixed behind the card, very
   low opacity, ignores clicks. */
.login-bg-art {
  position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none;
}
.login-bg-art svg {
  position: absolute; bottom: -40px; inset-inline-end: -60px; width: 620px; height: 420px;
  opacity: .22; color: #fff;
}
@media (max-width: 640px) { .login-bg-art svg { width: 420px; height: 300px; } }
.login-card {
  width: 100%; background: var(--surface-1); border: 1px solid var(--border); border-radius: 16px;
  padding: 32px 28px 26px; box-shadow: var(--shadow-md);
  display: flex; flex-direction: column; align-items: center; text-align: center;
  animation: fadeUp .35s ease;
}
.login-card.shake { animation: fadeUp .35s ease, shake .4s ease .35s; }
.login-icon {
  width: 56px; height: 56px; border-radius: 50%; background: var(--series-blue-soft); color: var(--series-blue);
  display: flex; align-items: center; justify-content: center; margin-bottom: 14px;
  animation: popIn .4s cubic-bezier(.34,1.56,.64,1);
}
.login-icon svg { width: 28px; height: 28px; }
.login-card h1 { font-family: var(--font-display); font-weight: 700; font-size: 22px; margin: 0 0 4px; }
.login-subtitle { color: var(--text-secondary); font-size: 13px; margin: 0 0 18px; }
.login-error {
  width: 100%; background: color-mix(in srgb, var(--status-critical) 12%, transparent);
  color: var(--status-critical); border: 1px solid color-mix(in srgb, var(--status-critical) 30%, transparent);
  border-radius: 8px; padding: 8px 12px; font-size: 13px; margin-bottom: 14px;
}
.input-field {
  position: relative; width: 100%; display: flex; align-items: center; margin-bottom: 12px;
}
.input-icon {
  position: absolute; inset-inline-start: 12px; width: 17px; height: 17px; color: var(--text-muted);
  pointer-events: none;
}
.input-field input {
  width: 100%; padding: 11px 14px 11px 40px; padding-inline-start: 38px; padding-inline-end: 14px;
  border-radius: 10px; border: 1px solid var(--border); background: var(--surface-2);
  color: var(--text-primary); font-size: 14px; font-family: inherit;
  transition: border-color .15s ease, box-shadow .15s ease;
}
[dir="rtl"] .input-field input { padding-right: 38px; padding-left: 14px; }
.input-field input:focus {
  outline: none; border-color: var(--series-blue); box-shadow: 0 0 0 3px var(--series-blue-soft);
}
.toggle-password {
  position: absolute; inset-inline-end: 10px; background: none; border: none; cursor: pointer;
  color: var(--text-muted); display: flex; align-items: center; padding: 4px;
}
.toggle-password svg { width: 18px; height: 18px; }
.toggle-password.active { color: var(--series-blue); }
.login-submit {
  width: 100%; margin-top: 6px; padding: 11px 16px; border: none; border-radius: 999px;
  background: var(--series-blue); color: #fff; font-size: 14.5px; font-weight: 600; font-family: inherit;
  display: flex; align-items: center; justify-content: center; gap: 8px; cursor: pointer;
  transition: filter .15s ease, transform .1s ease;
}
.login-submit svg { width: 16px; height: 16px; transition: transform .15s ease; }
.login-submit:hover { filter: brightness(1.08); }
.login-submit:hover svg { transform: translateX(-3px); }
[dir="rtl"] .login-submit:hover svg { transform: translateX(3px); }
.login-submit:active { transform: scale(.98); }
/* Sits directly on the vivid gradient body background (outside the white
   card), not on a surface - var(--text-muted) has poor contrast there, so
   this gets its own light, semi-transparent white rather than a token
   meant for card/page surfaces. */
.login-disclaimer { color: rgba(255,255,255,.75); font-size: 12px; text-align: center; }

/* ---------- Forms (portfolio add/edit) ---------- */
.error-banner {
  background: color-mix(in srgb, var(--status-critical) 10%, transparent);
  color: var(--status-critical); border: 1px solid color-mix(in srgb, var(--status-critical) 28%, transparent);
  border-radius: 10px; padding: 10px 14px; font-size: 13.5px; margin-bottom: 16px;
}
.form-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 12px; align-items: end;
}
.form-field { display: flex; flex-direction: column; gap: 5px; }
.form-field label { font-size: 12px; font-weight: 600; color: var(--text-muted); }
.form-field input, .form-field textarea, .form-field select {
  padding: 9px 12px; border-radius: 9px; border: 1px solid var(--border);
  background: var(--surface-2); color: var(--text-primary); font-size: 13.5px;
  font-family: inherit; transition: border-color .15s ease, box-shadow .15s ease;
}
.form-field input:focus, .form-field textarea:focus, .form-field select:focus {
  outline: none; border-color: var(--series-blue); box-shadow: 0 0 0 3px var(--series-blue-soft);
}
/* A checkbox field (screener's "אשכול קניית פנים פעיל בלבד" toggle) doesn't
   want the same block-level input styling as a text/number/select field
   above - just the label and box sitting inline, aligned with its row's
   baseline like the other fields' bottom edge. */
.form-field-checkbox { justify-content: flex-end; }
.form-field-checkbox label { display: flex; align-items: center; gap: 7px; font-size: 13.5px; font-weight: 500; color: var(--text-primary); cursor: pointer; }
.form-field-checkbox input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--series-blue); cursor: pointer; }
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  font-size: 13.5px; font-weight: 600; font-family: inherit; cursor: pointer;
  padding: 9px 18px; border-radius: 999px; border: 1px solid transparent;
  transition: filter .15s ease, transform .1s ease, box-shadow .15s ease;
}
.btn svg { width: 14px; height: 14px; }
.btn:active { transform: scale(.97); }
/* Primary CTA treatment (redesign, 2026-09-23): a two-color gradient
   (brand blue -> brass accent) instead of a flat fill, plus a single
   diagonal sheen that sweeps across on hover. This flourish is spent on
   primary buttons only (not .btn-ghost/.btn-danger/filter chips/menu
   tiles) - "spend your boldness in one place" rather than adding a hover
   effect to everything. */
.btn-primary, .login-submit {
  position: relative; overflow: hidden;
  /* A blue-to-blue depth gradient (not a blue-to-gold blend: mixing two
     far-apart hues straight in sRGB desaturates through a muddy grey,
     confirmed by screenshot when this was first tried) plus a crisp,
     unmixed gold rim along the bottom edge for the accent - a trim, not
     a wash. */
  background: linear-gradient(160deg, color-mix(in srgb, var(--series-blue) 82%, white 18%), var(--series-blue));
  color: #fff;
  box-shadow: 0 2px 10px var(--series-blue-soft), inset 0 1px 0 rgba(255,255,255,.25), inset 0 -3px 0 var(--accent-gold);
}
.btn-primary::after, .login-submit::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 40%, rgba(255,255,255,.35) 50%, transparent 60%);
  transform: translateX(-120%); transition: transform .5s ease;
}
.btn-primary:hover::after, .login-submit:hover::after { transform: translateX(120%); }
.btn-primary:hover, .login-submit:hover { filter: brightness(1.06); }
.btn-danger { background: transparent; border-color: var(--border); color: var(--status-critical); }
.btn-danger:hover { background: color-mix(in srgb, var(--status-critical) 8%, transparent); border-color: var(--status-critical); }
.btn-ghost { background: var(--surface-2); border-color: var(--border); color: var(--text-secondary); }
.btn-ghost:hover { color: var(--text-primary); border-color: var(--series-blue); }
.btn-sm { padding: 6px 12px; font-size: 12.5px; }

.row-actions { display: flex; align-items: center; gap: 6px; }
.edit-row td { background: var(--surface-2); padding: 14px 16px; }
.edit-row .form-grid { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }

.pnl-summary {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px;
}
.pnl-summary .stat-tile .value.positive { color: var(--status-good); }
.pnl-summary .stat-tile .value.negative { color: var(--status-critical); }

/* ---------- Dark/light manual toggle (2026-09-15) ---------- */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface-2); color: var(--text-secondary); cursor: pointer; flex: none;
  transition: background .15s ease, color .15s ease, border-color .15s ease, transform .1s ease;
}
.theme-toggle:hover { color: var(--series-blue); border-color: var(--series-blue); }
.theme-toggle:active { transform: scale(.92); }
.theme-toggle svg { width: 17px; height: 17px; }
/* Only one of the two icons shows at a time - which one is decided by
   which data-theme is active (set on <html> by the inline bootstrap
   script in base.html, before first paint, to avoid a flash of the
   wrong icon). */
.theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: none; }
  :root:not([data-theme="light"]) .theme-toggle .icon-sun { display: block; }
}

/* ---------- Header dual clock (Israel + US, 2026-09-15) ---------- */
.header-clocks { display: flex; align-items: center; gap: 6px; }
.clock-pill {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600;
  padding: 6px 10px; border-radius: 999px; border: 1px solid var(--border);
  color: var(--text-secondary); background: var(--surface-2); cursor: default;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.clock-pill .flag { font-size: 13px; line-height: 1; }
.clock-pill .clock-time { color: var(--text-primary); }
.clock-pill .clock-zone { color: var(--text-muted); font-size: 10.5px; }

/* ---------- Global page-transition progress bar (2026-09-15) ---------- */
/* A slim bar fixed above everything (including the sticky header) that
   starts animating on internal link clicks / form submits and on real
   fetch() calls the page kicks off - see base.html's initNavProgress().
   It deliberately never needs to be told "done": a real navigation
   unloads the page (which destroys this element along with everything
   else), and an ajax-only usage calls navProgressDone() explicitly. */
#nav-progress {
  position: fixed; top: 0; inset-inline: 0; height: 3px; z-index: 999;
  background: var(--series-blue); transform-origin: right center;
  transform: scaleX(0); opacity: 0;
  transition: transform .2s ease, opacity .15s ease;
  box-shadow: 0 0 8px var(--series-blue-soft);
}
[dir="rtl"] #nav-progress { transform-origin: right center; }
#nav-progress.active { opacity: 1; animation: navProgressGrow 2.4s ease-out forwards; }
#nav-progress.done { transform: scaleX(1); opacity: 0; transition: transform .15s ease, opacity .3s ease .1s; }
@keyframes navProgressGrow {
  0%   { transform: scaleX(0); }
  20%  { transform: scaleX(.35); }
  60%  { transform: scaleX(.65); }
  100% { transform: scaleX(.88); }
}

/* ---------- Small inline spinner (buttons / inline data loads, 2026-09-15) ---------- */
.spinner {
  display: inline-block; width: 14px; height: 14px; flex: none;
  border-radius: 50%; border: 2px solid currentColor; border-inline-start-color: transparent;
  animation: spin .7s linear infinite; opacity: .85;
}
.spinner-lg { width: 26px; height: 26px; border-width: 3px; }
@keyframes spin { to { transform: rotate(360deg); } }
/* Generic "this card/section is loading data" placeholder, used instead of
   a spinner-per-row when a whole block of content is still being fetched. */
.loading-block {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 28px 16px; color: var(--text-muted); font-size: 13px;
}

/* ---------- Scrolling ticker tape (redesign v2, 2026-09-23) ---------- */
/* Direct request: "עם צבעים תמונות ואנימציות... כמו האתרים המסחריים שיש
   היום" (colors/animation, like today's commercial trading sites) - a
   continuously scrolling strip of real quotes is THE visual signature of
   a trading platform (Robinhood/eToro/eToro-style tickers, exchange
   boards), so it leads the page rather than living in a corner. Built
   from real prices (menu_view -> db.get_ticker_tape_quotes), not fake
   placeholder numbers. */
.ticker-tape {
  position: relative; overflow: hidden;
  background: linear-gradient(90deg, var(--brand-indigo), var(--brand-violet));
  border-radius: 12px; margin-bottom: 16px;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
}
.ticker-tape-track {
  display: flex; width: max-content; gap: 30px; padding: 10px 24px;
  animation: tapeScroll 38s linear infinite;
}
.ticker-tape:hover .ticker-tape-track { animation-play-state: paused; }
@keyframes tapeScroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.ticker-item {
  display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 700;
  color: #fff; white-space: nowrap; font-variant-numeric: tabular-nums;
}
.ticker-item .tk-sym { color: rgba(255,255,255,.72); font-weight: 700; }
.ticker-item .tk-chg { font-weight: 700; }
.ticker-item .tk-chg.positive { color: #7CFFB2; }
.ticker-item .tk-chg.negative { color: #FF9B9B; }
.ticker-item .tk-dot { width: 5px; height: 5px; border-radius: 50%; background: rgba(255,255,255,.35); }

/* ---------- Landing menu hero (redesign v2, 2026-09-23) ---------- */
/* A vivid, animated multi-stop gradient with two slow-drifting blurred
   "blob" lights (a genuinely common device on commercial fintech/trading
   marketing pages - Stripe, Robinhood, exchange landing pages all use
   this) replaces the flatter navy-blue hero from the previous pass, which
   read as too close to "the same site" - direct feedback from the client. */
.hero {
  position: relative; overflow: hidden; border-radius: 22px; margin-bottom: 18px;
  padding: 44px 32px; color: #fff; isolation: isolate;
  background: linear-gradient(135deg, var(--brand-indigo) 0%, var(--brand-violet) 60%, #a12a92 100%);
  box-shadow: 0 24px 60px -12px color-mix(in srgb, var(--brand-violet) 45%, transparent), var(--shadow-md);
  animation: fadeUp .35s ease;
}
.hero::before, .hero::after {
  content: ''; position: absolute; border-radius: 50%; filter: blur(60px); z-index: -1; opacity: .5;
  animation: heroBlobFloat 16s ease-in-out infinite alternate;
}
.hero::before { width: 300px; height: 300px; background: var(--accent-gold); top: -100px; inset-inline-end: -70px; }
.hero::after  { width: 340px; height: 340px; background: #22d3ee; bottom: -140px; inset-inline-start: -90px; animation-delay: -8s; }
@keyframes heroBlobFloat { from { transform: translate(0,0) scale(1); } to { transform: translate(-24px, 18px) scale(1.18); } }

.hero-eyebrow {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 700;
  padding: 5px 12px; border-radius: 999px; margin-bottom: 12px;
  background: rgba(255,255,255,.14); border: 1px solid rgba(255,255,255,.22);
  opacity: 0; animation: fadeUp .5s ease 0s forwards;
}
.hero-eyebrow .live-dot { background: #7CFFB2; }
/* One deliberate reveal for the ONE hero on the site - eyebrow, then
   title, then subtitle, then the stat chips - rather than the generic
   "fade every card in the same way" treatment used elsewhere (.card/
   .menu-tile/etc all just get a flat fadeUp). */
.hero-title {
  font-family: var(--font-display); font-weight: 700; font-size: 34px; margin: 0 0 10px;
  opacity: 0; animation: fadeUp .5s ease .1s forwards;
}
.hero-subtitle {
  font-size: 15px; color: rgba(255,255,255,0.88); max-width: 580px; margin: 0 0 22px;
  opacity: 0; animation: fadeUp .5s ease .22s forwards;
}
/* Glass ("frosted") stat chips - the one glassmorphism moment on the
   page, floating over the gradient the way a commercial fintech hero
   would surface its headline numbers. */
.hero-stats { display: flex; flex-wrap: wrap; gap: 10px; opacity: 0; animation: fadeUp .5s ease .34s forwards; }
.hero-stat {
  display: flex; flex-direction: column; gap: 2px; padding: 10px 16px; border-radius: 14px;
  background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.2);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.hero-stat .hero-stat-value { font-size: 18px; font-weight: 800; font-variant-numeric: tabular-nums; }
.hero-stat .hero-stat-label { font-size: 11px; color: rgba(255,255,255,.75); }

/* Hero background imagery + floating particles (redesign v2b, 2026-09-23,
   direct request to add background images and animation). A skyline
   silhouette (the financial-district cliche, but a real "image" rather
   than a flat gradient) sits low and quiet behind the text; small glowing
   particles drift upward through the whole banner for continuous ambient
   motion, on top of the blob-lights already in .hero::before/::after. */
.hero-skyline {
  position: absolute; inset-inline: 0; bottom: 0; height: 90px;
  opacity: .16; pointer-events: none; z-index: 0;
}
.hero-skyline svg { width: 100%; height: 100%; display: block; }
.hero-eyebrow, .hero-title, .hero-subtitle, .hero-stats { position: relative; z-index: 1; }
.hero-particles { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.hero-particle {
  position: absolute; bottom: -10px; width: 5px; height: 5px; border-radius: 50%;
  background: #fff; opacity: 0;
  animation: particleRise linear infinite;
}
@keyframes particleRise {
  0%   { transform: translateY(0) scale(.6); opacity: 0; }
  15%  { opacity: .8; }
  85%  { opacity: .5; }
  100% { transform: translateY(-220px) scale(1); opacity: 0; }
}

.menu-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px;
}
@media (max-width: 900px) { .menu-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .menu-grid { grid-template-columns: 1fr; } }
/* Bento sizing - the flagship destination (ranking) gets a bigger cell so
   the grid isn't a wall of uniform tiles (the "SaaS-identical-cards"
   pattern) - the rest stay a normal, denser size. */
.menu-tile-lg { grid-column: span 2; }
@media (max-width: 560px) { .menu-tile-lg { grid-column: span 1; } }

.menu-tile {
  position: relative; display: flex; flex-direction: column; gap: 10px;
  background: var(--surface-1); border: 1px solid var(--border); border-radius: 16px;
  padding: 18px; color: inherit; text-decoration: none;
  box-shadow: var(--shadow-sm); overflow: hidden;
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
  animation: fadeUp .3s ease both;
}
/* A thin top edge in the tile's own category color - encodes "what kind
   of thing is this" (per the design plan's categories below) rather than
   decorating for its own sake. */
.menu-tile::before {
  content: ''; position: absolute; inset-inline: 0; top: 0; height: 3px;
  background: var(--tile-accent, var(--series-blue));
}
.menu-tile:hover {
  text-decoration: none; transform: translateY(-4px);
  box-shadow: 0 14px 30px -10px var(--tile-glow, var(--series-blue-soft));
  border-color: var(--tile-accent, var(--series-blue));
}
.menu-tile:active { transform: translateY(-1px) scale(.99); }
.menu-tile-icon {
  display: flex; align-items: center; justify-content: center;
  width: 42px; height: 42px; border-radius: 12px;
  background: var(--tile-soft, var(--series-blue-soft)); color: var(--tile-accent, var(--series-blue)); flex: none;
}
.menu-tile-icon svg { width: 21px; height: 21px; }
.menu-tile-title { font-size: 14.5px; font-weight: 700; }
.menu-tile-desc { font-size: 12.5px; color: var(--text-secondary); }
/* Category coding (redesign v2) - four groups, four colors: market data,
   personal money, research/analysis, dates - see README for the mapping. */
.menu-tile.cat-market   { --tile-accent: var(--cat-market);   --tile-soft: var(--cat-market-soft);   --tile-glow: var(--cat-market-soft); }
.menu-tile.cat-personal { --tile-accent: var(--cat-personal); --tile-soft: var(--cat-personal-soft); --tile-glow: var(--cat-personal-soft); }
.menu-tile.cat-research { --tile-accent: var(--cat-research); --tile-soft: var(--cat-research-soft); --tile-glow: var(--cat-research-soft); }
.menu-tile.cat-calendar { --tile-accent: var(--cat-calendar); --tile-soft: var(--cat-calendar-soft); --tile-glow: var(--cat-calendar-soft); }

/* ---------- News page item + ticker badge (2026-09-15) ---------- */
.news-item {
  display: flex; gap: 12px; padding: 14px 0;
  border-top: 1px solid var(--gridline);
  animation: fadeUp .25s ease both;
}
.news-item:first-child { border-top: none; padding-top: 4px; }
.news-item-body { flex: 1; min-width: 0; }
.news-item-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 4px; }

/* ==========================================================================
   Content-depth features (2026-09-23, direct user request): insider
   cluster-buying detection, per-headline news sentiment trend, analyst
   rating trend over time, and the daily digest page. Grouped in one
   section since they shipped together, even though each is used on a
   different page - see README's matching section for the full narrative.
   ========================================================================== */

/* ---------- Insider cluster-buying (db.get_insider_cluster_buys) ---------- */
.cluster-alert {
  display: flex; align-items: flex-start; gap: 10px;
  background: color-mix(in srgb, var(--status-good) 10%, var(--surface-1));
  border: 1px solid color-mix(in srgb, var(--status-good) 35%, var(--border));
  border-radius: 10px; padding: 12px 14px; margin-bottom: 14px; font-size: 13.5px;
}
.cluster-alert-icon { flex: none; font-size: 17px; line-height: 1.3; }

.cluster-banner h2 { margin-bottom: 4px; }
.cluster-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 12px; margin-top: 12px;
}
.cluster-tile {
  display: flex; align-items: center; gap: 10px; padding: 12px;
  border: 1px solid var(--border); border-radius: 12px; background: var(--surface-1);
  text-decoration: none; color: inherit;
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.cluster-tile:hover {
  transform: translateY(-2px); box-shadow: var(--shadow-sm);
  border-color: var(--status-good); text-decoration: none;
}
.cluster-tile-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cluster-tile-body .notes { font-size: 12px; }

/* ---------- News sentiment trend sparkline (db.get_news_sentiment_trend) ---------- */
.sentiment-trend {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--gridline);
}
.sentiment-trend svg { display: block; flex: none; }
.sentiment-trend-label { font-size: 13.5px; color: var(--text-secondary); }

/* ---------- Analyst actions trend (db.get_analyst_actions_trend) ---------- */
.analyst-trend-summary {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  flex-wrap: wrap; padding: 10px 12px; border-radius: 10px;
  background: var(--surface-2); font-size: 13.5px; margin-top: 10px;
}
.analyst-trend-dots { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.analyst-trend-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; cursor: default; }
.analyst-trend-dot.up { background: var(--status-good); }
.analyst-trend-dot.down { background: var(--status-critical); }
.analyst-trend-dot.flat { background: var(--text-muted); opacity: .55; }

/* ---------- Daily digest page ("מה השתנה היום" - /digest) ---------- */
/* A row-based layout, not <table> (the project's usual choice for list-like
   data) - this page's four cards sit 3-4 across at desktop width (see
   .digest-grid below), too narrow for a multi-column table without either
   squishing text into an unreadable wrap or forcing horizontal scroll on
   every card - confirmed by an actual screenshot of the table-based first
   draft, where dates and share counts wrapped mid-character. A flexible
   row that stacks its secondary line naturally has no such failure mode. */
.digest-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 18px;
}
.digest-section-empty { color: var(--text-muted); font-size: 13.5px; padding: 6px 0; }
.digest-list { display: flex; flex-direction: column; }
.digest-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 2px; text-decoration: none; color: inherit;
  border-bottom: 1px solid var(--gridline);
  transition: background .15s ease;
}
.digest-list .digest-row:last-child { border-bottom: none; }
.digest-row:hover { background: var(--series-blue-soft); text-decoration: none; border-radius: 8px; }
.digest-row-detail {
  display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
  text-align: end; font-size: 13.5px; flex: none; white-space: nowrap;
}
.digest-score-delta { font-weight: 700; font-variant-numeric: tabular-nums; }
.digest-score-delta.up { color: var(--status-good); }
.digest-score-delta.down { color: var(--status-critical); }

/* ==========================================================================
   "Raise the odds" content round 2 (2026-09-23): fundamental quality score
   (Piotroski-adapted), EPS-surprise history, P/E-vs-own-history percentile,
   and the "similar past cases" signal backtest context card.
   ========================================================================== */

/* ---------- Quality score (analysis/quality_score.py) ---------- */
.quality-score-head {
  display: flex; align-items: center; gap: 14px; margin-bottom: 10px;
}
.quality-score-fraction {
  font-size: 28px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.quality-criteria-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.quality-criteria-list li { display: flex; align-items: center; gap: 8px; font-size: 13.5px; }
.quality-criteria-icon { flex: none; width: 18px; text-align: center; font-weight: 700; }
.quality-criteria-list li.passed .quality-criteria-icon { color: var(--status-good); }
.quality-criteria-list li.failed .quality-criteria-icon { color: var(--status-critical); }
.quality-criteria-list li.failed { color: var(--text-secondary); }

/* ---------- P/E-vs-own-history percentile (analysis/valuation_history.py) --- */
.pe-percentile-row {
  margin-top: 10px; padding: 10px 12px; border-radius: 10px;
  background: var(--surface-2); font-size: 13.5px;
}

/* ---------- "Similar past cases" signal backtest (db.signal_backtest_*) --- */
.signal-backtest-card { border: 1px solid color-mix(in srgb, var(--series-blue) 30%, var(--border)); }
.signal-backtest-block { padding: 10px 0; border-bottom: 1px solid var(--gridline); }
.signal-backtest-block:last-of-type { border-bottom: none; }
.signal-backtest-title { font-weight: 700; margin-bottom: 4px; }

/* ==========================================================================
   "Concrete tools" round (2026-09-24): seasonality patterns on the company
   page, and the saved stock screener (/screener).
   ========================================================================== */

/* ---------- Seasonality (analysis/seasonality.py) ----------
   A plain div grid, deliberately NOT a <table> - the CSS-Grid-narrow-card
   lesson from the digest page above applies here too (12 tiles at 3-4
   across on a phone would garble a table's cells the same way; a flexible
   tile has no such failure mode). */
.seasonality-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 10px;
}
.seasonality-tile {
  padding: 10px 10px; border-radius: 10px; background: var(--surface-2);
  border: 1px solid transparent; transition: transform .15s ease;
}
.seasonality-tile:hover { transform: translateY(-2px); }
.seasonality-tile.current { border-color: var(--series-blue); background: var(--series-blue-soft); }
.seasonality-month { font-size: 12px; font-weight: 600; color: var(--text-muted); display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
.seasonality-current-tag {
  font-size: 9.5px; font-weight: 700; color: var(--series-blue);
  background: color-mix(in srgb, var(--series-blue) 16%, transparent);
  padding: 1px 6px; border-radius: 999px; text-transform: none;
}
.seasonality-value { font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; margin-top: 4px; }
.seasonality-value.positive { color: var(--status-good); }
.seasonality-value.negative { color: var(--status-critical); }
.seasonality-value.muted { color: var(--text-muted); }
.seasonality-meta { font-size: 11px; color: var(--text-muted); margin-top: 2px; }

/* ---------- Saved stock screener (/screener) ---------- */
.screener-save-form { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 14px; }
.saved-screens-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.saved-screen-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 2px; border-bottom: 1px solid var(--gridline);
}
.saved-screens-list .saved-screen-row:last-child { border-bottom: none; }
.saved-screen-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.saved-screen-name { font-weight: 700; font-size: 13.5px; }
.saved-screen-summary { font-size: 12px; color: var(--text-muted); }
.saved-screen-actions { display: flex; align-items: center; gap: 6px; flex: none; }
.saved-screen-actions form { display: inline; }

/* ==========================================================================
   Statistical/ML predictor (2026-09-24 request: "give me an algorithm that
   can predict the market") - analysis/ml_predictor.py.
   ========================================================================== */
.ml-prediction-card { border: 1px solid color-mix(in srgb, var(--accent-gold) 35%, var(--border)); }
.ml-prediction-head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 8px; flex-wrap: wrap; }
.ml-prediction-value {
  font-size: 34px; font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--series-blue);
}
.ml-prediction-label { color: var(--text-secondary); font-size: 13px; max-width: 320px; }
.ml-track-record { margin-top: 10px; }
