/* ============================================================
   AI Stock Analyzer — "precision instrument" fintech UI
   Dark-first, mobile-first. Numbers are the hero: everything
   monetary uses tabular mono so digits line up down a column.
   ============================================================ */

/* Three families, each doing one job.
 *
 * Noto Sans Thai for Thai, because it is the face with the most complete tone
 * mark positioning at UI sizes and the app is Thai-first. Inter for Latin
 * interface text. IBM Plex Mono for every figure — the brief allows JetBrains
 * Mono or Plex, and Plex is already what forty rules in this file ask for, so
 * changing it would have meant touching all of them to gain nothing a reader
 * could see.
 *
 * The families are listed in one request so the browser opens one connection,
 * and display=swap means Thai text renders in Leelawadee immediately rather
 * than sitting invisible while a webfont downloads over a phone connection. */
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+Thai:wght@400;500;600;700&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600;700&display=swap');

/* ============================================================================
 * Emerald & champagne — the palette the mascot brought with her.
 *
 * Every name in here already existed and is kept, because the chart code reads
 * --chart-bg / --chart-grid / --chart-text / --up / --down / --accent straight
 * off this block (static/js/01-state-theme.js), and roughly forty inline styles
 * across the feature files resolve var(--up), var(--down), var(--warn) at render
 * time. Renaming a token would silently grey out a chart or a P/L figure with
 * nothing in the console to say why. New colours are added alongside rather
 * than in place of anything.
 *
 * The old canvas was blue-black with a neon mint accent. This one is near-black
 * with a green undertone, a deeper emerald that reads as a decision rather than
 * a highlighter, and champagne gold reserved for things that are genuinely
 * premium — never as a third body colour.
 * ========================================================================== */
:root {
  /* canvas: near-black with a green undertone, lifted in layers */
  --bg: #070B0A;
  --bg-2: #0B1110;
  /* two glows and a grid. Kept faint on purpose: this sits behind live prices
     all day, and anything with presence here competes with the numbers. */
  --bg-glow:
    radial-gradient(1100px 620px at 50% -12%, rgba(15, 175, 122, .10), transparent 62%),
    radial-gradient(760px 420px at 88% 4%, rgba(214, 168, 75, .045), transparent 60%),
    linear-gradient(rgba(132, 144, 140, .022) 1px, transparent 1px),
    linear-gradient(90deg, rgba(132, 144, 140, .022) 1px, transparent 1px);
  --bg-grid-size: 100% 100%, 100% 100%, 44px 44px, 44px 44px;
  --surface: #101715;          /* card */
  --surface-2: #151D1A;        /* elevated: inputs, nav strip, chips */
  --surface-3: #1B2421;
  /* Hairlines carry a gold cast rather than plain white. At a tenth of an
     alpha it never reads as "gold border", only as warmer than grey — which is
     most of the difference between this and a generic dark dashboard. */
  --line: rgba(214, 168, 75, .11);
  --line-strong: rgba(214, 168, 75, .24);
  --line-cool: rgba(255, 255, 255, .07);
  --top-sheen: rgba(255, 255, 255, .03);

  --text: #E8EFEA;
  --text-dim: #A7B3AD;
  --muted: #84908C;

  /* one signal colour, used sparingly, so it always means something */
  --accent: #19D99A;                       /* bright emerald — text, links, active */
  --accent-strong: #0FAF7A;                /* primary emerald — fills */
  --accent-deep: #075C43;
  --accent-soft: #48E0AE;
  --accent-dim: rgba(25, 217, 154, .13);
  --accent-line: rgba(25, 217, 154, .34);
  --accent-grad: linear-gradient(135deg, #0B3D30, #0E5A43);

  --up: #20D99A;
  --down: #FF5F7E;
  --warn: #F7B84B;
  --info: #62A9FF;

  --gold: #D6A84B;
  --gold-soft: #F0C96B;
  --gold-pale: #FFE29A;
  --gold-deep: #8E6725;
  --gold-dim: rgba(214, 168, 75, .12);
  --gold-line: rgba(214, 168, 75, .30);

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .45);
  --shadow: 0 6px 20px -8px rgba(0, 0, 0, .6);
  --shadow-lg: 0 22px 48px -16px rgba(0, 0, 0, .75);
  --glow-emerald: 0 0 0 1px var(--accent-line), 0 8px 26px -14px rgba(25, 217, 154, .5);

  --r-sm: 10px;
  --r: 16px;
  --r-lg: 20px;

  --nav-h: 62px;

  /* 4 / 8 / 12 / 16 / 24 / 32 / 48 — one ladder, so nothing lands between steps */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s6: 24px; --s8: 32px; --s12: 48px;

  --ease: cubic-bezier(.22, 1, .36, 1);
  --t-fast: 150ms;
  --t: 220ms;

  --chart-bg: #0B1110;
  --chart-grid: rgba(132, 144, 140, .13);
  --chart-text: #84908C;
}

/* The light theme is not the brand — the toggle exists and has to keep working,
   so it gets the same emerald and champagne rather than being left on the old
   blue-mint. Contrast is pushed harder here: gold on white is far weaker than
   gold on near-black, so the gold tokens darken rather than simply inverting. */
:root[data-theme="light"] {
  --bg: #F5F7F5;
  --bg-2: #EDF1EE;
  --bg-glow:
    radial-gradient(1100px 620px at 50% -12%, rgba(15, 175, 122, .10), transparent 62%),
    radial-gradient(760px 420px at 88% 4%, rgba(214, 168, 75, .07), transparent 60%),
    linear-gradient(rgba(9, 35, 26, .022) 1px, transparent 1px),
    linear-gradient(90deg, rgba(9, 35, 26, .022) 1px, transparent 1px);
  --surface: #FFFFFF;
  --surface-2: #F1F5F2;
  --surface-3: #E5EBE7;
  --line: rgba(120, 92, 30, .16);
  --line-strong: rgba(120, 92, 30, .30);
  --line-cool: rgba(9, 35, 26, .09);
  --top-sheen: rgba(255, 255, 255, .6);

  --text: #08150F;
  --text-dim: #41524A;
  --muted: #6B7A72;

  --accent: #0A8A61;
  --accent-strong: #097A56;
  --accent-deep: #04382A;
  --accent-soft: #0FAF7A;
  --accent-dim: rgba(10, 138, 97, .12);
  --accent-line: rgba(10, 138, 97, .34);
  --accent-grad: linear-gradient(135deg, #D8F0E6, #BFE7D6);

  --up: #0A8A61;
  --down: #D51F45;
  --warn: #A96A08;
  --info: #1F63D6;

  --gold: #8E6725;
  --gold-soft: #A87C2E;
  --gold-pale: #C79A45;
  --gold-deep: #6A4C1B;
  --gold-dim: rgba(142, 103, 37, .10);
  --gold-line: rgba(142, 103, 37, .32);

  --shadow-sm: 0 1px 2px rgba(16, 40, 30, .06);
  --shadow: 0 6px 18px -6px rgba(16, 40, 30, .14);
  --shadow-lg: 0 20px 44px -16px rgba(16, 40, 30, .22);
  --glow-emerald: 0 0 0 1px var(--accent-line), 0 8px 26px -14px rgba(10, 138, 97, .35);

  --chart-bg: #FFFFFF;
  --chart-grid: rgba(9, 35, 26, .07);
  --chart-text: #6B7A72;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* The root size scales across the phone range, so a 320px screen and a 430px
 * one get proportionally sized text and spacing instead of the same fixed
 * pixels. Everything else in this file is already in rem, so it all follows.
 *
 * The ceiling is 1rem — the browser's own base — so anything 480px and wider,
 * desktop included, resolves exactly as it did before. This was meant to fix
 * phones, not to quietly resize a layout that was working.
 *
 * Written in rem rather than px because 1rem means the reader's configured
 * base size: someone who has turned their phone's text up still gets it. */
html {
  -webkit-text-size-adjust: 100%;
  font-size: clamp(0.9375rem, 0.75rem + 0.85vw, 1rem);   /* 15px at 320 → 16px at 480+ */
}

body {
  background: var(--bg);
  background-image: var(--bg-glow);
  /* the two glows fill; the grid tiles. Sizes come from the token so the light
     theme cannot drift out of step with the dark one. */
  background-size: var(--bg-grid-size);
  background-repeat: no-repeat, no-repeat, repeat, repeat;
  background-attachment: fixed;
  color: var(--text);
  font-family: "Inter", "Noto Sans Thai", "Leelawadee UI", system-ui, sans-serif;
  font-size: 1rem;          /* was a fixed 15px while everything else used rem */
  line-height: 1.55;
  /* 100vh on mobile Safari counts the strip behind the collapsing URL bar, so
   * the page ends up taller than what is actually visible. dvh tracks the real
   * height; the vh line stays first as a fallback for older browsers. */
  min-height: 100vh;
  min-height: 100dvh;
  padding-bottom: calc(var(--nav-h) + env(safe-area-inset-bottom, 0px));
  -webkit-font-smoothing: antialiased;
}

/* every figure in the app: same width per digit so columns align */
.num, .val, .gauge-num, #curPrice, #overallScore, td, th,
input[type="number"], .money {
  font-family: "IBM Plex Mono", ui-monospace, "Cascadia Mono", monospace;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

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

::selection { background: var(--accent-dim); color: var(--text); }

/* ---------------- app bar ---------------- */
header {
  position: sticky; top: 0; z-index: 40;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid var(--line);
}
.header-row {
  display: flex; align-items: center; gap: 12px;
  max-width: 1160px; margin: 0 auto; padding: 10px 16px;
  /* Nothing in here may push the page wider than the screen. A phone held
     sideways lands at 812px, which is past the 768px breakpoint, so it gets
     the desktop header - and once the nav grew to five buttons the row needed
     839px and the theme toggle hung 5px off the edge, giving the whole app a
     horizontal scroll in landscape. */
  min-width: 0; overflow: hidden;
}
.brand {
  display: flex; flex-direction: column; line-height: 1.15;
  font-weight: 700; font-size: 1rem; letter-spacing: -.015em; white-space: nowrap;
  /* the title may be clipped before anything is pushed off-screen */
  min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
.brand small {
  font-family: "IBM Plex Mono", monospace;
  font-size: .72rem; font-weight: 400; color: var(--muted);
  letter-spacing: .08em; text-transform: uppercase;
}
/* The mascot badge, replacing the glyph on a gradient plate that stood here.
   No plate now: the art is a cut-out with its own gold ring, and a coloured
   tile behind it would fight that ring for the edge.
   44px rather than the glyph's 30 — compared at true size on this exact
   background, the badge at 30 collapses to a green dot with nothing legible
   in it, and the ring is what earns it a silhouette on a dark bar. */
.brand-mark {
  width: 44px; height: 44px; margin-right: 2px; flex: none;
  object-fit: contain;
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, .5));
}
@media (max-width: 480px) { .brand-mark { width: 36px; height: 36px; } }

/* desktop segmented nav (hidden on phones — bottom bar takes over) */
.mainnav {
  display: none; gap: 2px; margin-left: auto;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: 12px; padding: 3px;
  /* five buttons no longer fit a landscape phone at the desktop breakpoint, so
     the strip scrolls rather than shoving the row off-screen */
  min-width: 0; overflow-x: auto; scrollbar-width: none;
}
.mainnav::-webkit-scrollbar { display: none; }
.mainnav button {
  background: none; border: none; color: var(--text-dim); cursor: pointer;
  padding: 7px 16px; border-radius: 9px; font: inherit; font-size: .88rem; font-weight: 600;
  transition: color .18s, background .18s; white-space: nowrap; flex: none;
}
.mainnav button:hover { color: var(--text); }
/* Active tab: the emerald gradient from the brand, a real 1px emerald edge,
   and a hairline of gold on top. Inactive stays charcoal so the active one is
   the only lit thing in the strip. */
.mainnav button.active {
  background: var(--accent-grad); color: var(--accent-soft);
  border: 1px solid #29D99D;
  box-shadow: inset 0 1px 0 rgba(240, 201, 107, .16), 0 2px 10px -6px var(--accent);
}

.icon-btn {
  background: var(--surface-2); color: var(--text-dim);
  border: 1px solid var(--line); border-radius: 11px;
  width: 38px; height: 38px; flex: none; cursor: pointer; font-size: 1rem;
  display: inline-flex; align-items: center; justify-content: center;
  transition: border-color .18s, color .18s, transform .1s;
}
.icon-btn:hover { color: var(--text); border-color: var(--line-strong); }
.icon-btn:active { transform: scale(.94); }

/* ---------------- live price indicator ---------------- */
.live-badge {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: 999px; padding: 4px 6px 4px 10px;
  font-family: "IBM Plex Mono", monospace; font-size: .72rem;
  letter-spacing: .06em; color: var(--text-dim);
}
.live-dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--up); box-shadow: 0 0 0 0 var(--up);
}
.live-dot.live { animation: pulse 2s ease-out infinite; }
.live-dot.paused { background: var(--muted); box-shadow: none; animation: none; }
.live-dot.stale { background: var(--warn); animation: none; }
/* Three seconds, not two, and it fades rather than snapping — a status light
   that blinks at you is a distraction on a page whose job is numbers. */
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--up) 62%, transparent); }
  70%  { box-shadow: 0 0 0 8px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
.live-dot.live { animation-duration: 3s; }
@media (prefers-reduced-motion: reduce) {
  .live-dot.live { animation: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--up) 22%, transparent); }
}
.live-clock { color: var(--muted); }
.live-toggle {
  background: none; border: none; color: var(--text-dim); cursor: pointer;
  font-size: .7rem; padding: 2px 5px; border-radius: 6px; line-height: 1;
}
.live-toggle:hover { color: var(--accent); background: var(--accent-dim); }

/* a value that just moved gets a brief tint — the app-trader tell that
   something is genuinely live rather than a stale render */
@keyframes flashUp {
  0% { background: color-mix(in srgb, var(--up) 30%, transparent); }
  100% { background: transparent; }
}
@keyframes flashDown {
  0% { background: color-mix(in srgb, var(--down) 30%, transparent); }
  100% { background: transparent; }
}
.flash-up { animation: flashUp 1s ease-out; border-radius: 5px; }
.flash-down { animation: flashDown 1s ease-out; border-radius: 5px; }

/* ---------------- bottom tab bar (mobile) ---------------- */
.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  display: flex; align-items: stretch;
  height: calc(var(--nav-h) + env(safe-area-inset-bottom, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter: saturate(180%) blur(18px);
  border-top: 1px solid var(--line);
}
.tabbar button {
  flex: 1; background: none; border: none; cursor: pointer; font: inherit;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; color: var(--muted); position: relative;
  font-size: .74rem; font-weight: 600; letter-spacing: .01em;
  transition: color .2s;
  -webkit-tap-highlight-color: transparent;
}
.tabbar button .ic { font-size: 1.25rem; line-height: 1; transition: transform .22s cubic-bezier(.34,1.56,.64,1); }
.tabbar button.active { color: var(--accent); }
.tabbar button.active .ic { transform: translateY(-2px) scale(1.1); }
.tabbar button.active::before {
  content: ""; position: absolute; top: 0; left: 50%; translate: -50% 0;
  width: 26px; height: 2.5px; border-radius: 0 0 3px 3px; background: var(--accent);
  box-shadow: 0 0 12px var(--accent);
}

/* ---------------- layout ---------------- */
.view { display: none; }
.view.active { display: block; animation: rise .32s cubic-bezier(.22,1,.36,1); }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

main, .port-wrap, .analyze-bar, .status { max-width: 1160px; margin: 0 auto; }
main, .port-wrap { padding: 16px; }
.analyze-bar { padding: 16px 16px 0; }
.status { padding: 10px 16px; color: var(--text-dim); font-size: .88rem; }
.status.error { color: var(--down); }
.hidden { display: none !important; }

/* One card, everywhere. The top sheen is a single hairline of white at 3%,
   which is what stops a flat fill reading as a grey box — it suggests a lit
   edge without the blur and expense of glassmorphism. Hover lifts 2px and
   warms the border toward emerald; it does not glow, because forty of these
   on a dashboard that all glow is a light show, not a terminal. */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: var(--s4);
  margin-bottom: var(--s3);
  box-shadow: var(--shadow-sm);
  position: relative;
  transition: transform var(--t) var(--ease), border-color var(--t), box-shadow var(--t);
}
.card::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 1px;
  border-radius: var(--r) var(--r) 0 0;
  background: linear-gradient(90deg, transparent, var(--top-sheen) 22%, var(--top-sheen) 78%, transparent);
  pointer-events: none;
}
@media (hover: hover) {
  .card:hover {
    transform: translateY(-2px);
    border-color: var(--accent-line);
    box-shadow: var(--shadow), 0 0 22px -18px var(--accent);
  }
}
/* A card the brand wants to single out — AI health, an insight, a top rank.
   Gold edge, gold corner wash, nothing else. */
.card.premium {
  border-color: var(--gold-line);
  background:
    radial-gradient(120% 80% at 100% 0%, var(--gold-dim), transparent 58%),
    var(--surface);
}
@media (hover: hover) { .card.premium:hover { border-color: var(--gold-soft); } }
.card > h2 {
  font-size: .95rem; font-weight: 700; letter-spacing: -.01em;
  margin-bottom: 12px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
h3 {
  font-size: .78rem; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: .09em; margin: 18px 0 8px;
}

/* ---------------- buttons ---------------- */
button.primary {
  background: linear-gradient(180deg, var(--accent), var(--accent-strong));
  color: #04150F; border: none;
  border-radius: 12px; padding: 10px 18px; cursor: pointer;
  font: inherit; font-weight: 700; font-size: .9rem;
  box-shadow: 0 2px 12px -4px var(--accent-line), inset 0 1px 0 rgba(255, 255, 255, .18);
  transition: filter var(--t-fast), transform .1s, box-shadow var(--t-fast);
}
button.primary:hover { filter: brightness(1.07); box-shadow: 0 4px 16px -5px var(--accent-line); }
/* the one button per screen that should feel expensive: dark emerald, thin gold */
button.premium {
  background: linear-gradient(180deg, #0E5A43, #093F30);
  color: var(--gold-pale); border: 1px solid var(--gold-line);
  border-radius: 12px; padding: 10px 18px; cursor: pointer;
  font: inherit; font-weight: 700; font-size: .9rem;
  transition: border-color var(--t-fast), transform .1s;
}
button.premium:hover { border-color: var(--gold-soft); }
button.premium:active { transform: scale(.97); }
button.primary:active { transform: scale(.97); }
button.primary.small { padding: 8px 14px; font-size: .84rem; }

.row-btn {
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text-dim);
  border-radius: 9px; padding: 6px 12px; font: inherit; font-size: .78rem; font-weight: 600;
  cursor: pointer; transition: all .16s; white-space: nowrap;
}
.row-btn:hover { color: var(--accent); border-color: var(--accent-line); background: var(--accent-dim); }
.row-btn:active { transform: scale(.96); }

/* ---------------- inputs ---------------- */
input, select {
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--line); border-radius: 11px;
  padding: 10px 13px; font: inherit; outline: none;
  /* Never below 16px. Safari on iPhone zooms the page in when a field with
   * smaller text is focused and does not zoom back out, so every entry left
   * the user pinching to recover. All twelve fields were at 14.7px. */
  font-size: max(16px, .92rem);
  transition: border-color .16s, box-shadow .16s;
  min-width: 0;
}
input:focus, select:focus {
  border-color: var(--accent-line);
  box-shadow: 0 0 0 3px var(--accent-dim), 0 0 0 4px var(--gold-dim);
}
/* Keyboard users get the same ring everywhere, not just on fields. :focus-visible
   so a mouse click does not leave a ring behind on every button it touches. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
input::placeholder { color: var(--muted); }

.searchbar { display: flex; gap: 8px; }
.searchbar input { flex: 1; }


.quick { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.quick span {
  border: 1px solid var(--line); background: var(--surface);
  border-radius: 999px; padding: 5px 13px; font-size: .8rem; font-weight: 500;
  color: var(--text-dim); cursor: pointer; transition: all .16s;
}
.quick span:hover { color: var(--accent); border-color: var(--accent-line); background: var(--accent-dim); }

/* ---------------- hero / overview ---------------- */
.overview { display: grid; gap: 12px; }
.score-card { overflow: hidden; }
.score-card::after {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent-line), transparent);
}
.company { font-size: .82rem; color: var(--muted); letter-spacing: .01em; }
.price-row { display: flex; align-items: baseline; gap: 10px; margin: 6px 0 16px; flex-wrap: wrap; }
#curPrice { font-size: 2rem; font-weight: 600; letter-spacing: -.03em; }
#curChange { font-size: 1rem; font-weight: 600; }

.up { color: var(--up); }
.down { color: var(--down); }

.gauge-wrap { display: flex; align-items: center; gap: 14px; }
.gauge {
  flex: 1; height: 7px; border-radius: 99px; overflow: hidden;
  background: var(--surface-3);
  box-shadow: inset 0 1px 2px rgba(0,0,0,.3);
}
.gauge-fill {
  height: 100%; width: 0; border-radius: 99px;
  transition: width .9s cubic-bezier(.22,1,.36,1);
  box-shadow: 0 0 12px -2px currentColor;
}
.gauge-num { font-size: 1.5rem; font-weight: 600; min-width: 58px; text-align: right; letter-spacing: -.02em; }
.verdict { margin-top: 10px; font-size: 1.02rem; font-weight: 700; letter-spacing: -.01em; }
.strong-buy, .buy { color: var(--up); }
.hold { color: var(--warn); }
.caution { color: #FF8A3D; }
.sell { color: var(--down); }

/* timeframe chips */
.tf-summary { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.tf-chip {
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: 12px 8px; text-align: center; cursor: pointer;
  transition: border-color .18s, transform .12s;
}
.tf-chip:hover { border-color: var(--accent-line); transform: translateY(-1px); }
.tf-chip .tf-name { color: var(--muted); font-size: .74rem; letter-spacing: .02em; }
.tf-chip .tf-score {
  font-family: "IBM Plex Mono", monospace; font-variant-numeric: tabular-nums;
  font-size: 1.45rem; font-weight: 600; margin: 3px 0; letter-spacing: -.03em;
}
.tf-chip .tf-verdict { font-size: .72rem; font-weight: 600; }

/* ---------------- sub tabs ---------------- */
.tabs {
  display: flex; gap: 4px; margin: 4px 0 14px; padding-bottom: 2px;
  overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs button {
  background: var(--surface); color: var(--text-dim);
  border: 1px solid var(--line); border-radius: 999px;
  padding: 8px 15px; cursor: pointer; font: inherit; font-size: .84rem; font-weight: 600;
  white-space: nowrap; transition: all .18s;
}
.tabs button:hover { color: var(--text); }
.tabs button.active {
  background: var(--accent-dim); color: var(--accent); border-color: var(--accent-line);
}
.tab-pane { display: none; }
.tab-pane.active { display: block; animation: rise .28s cubic-bezier(.22,1,.36,1); }

/* ---------------- datagrid: table on desktop, cards on phone ---------------- */
.datagrid { display: flex; flex-direction: column; gap: 8px; }
.dg-head { display: none; }                       /* phones: no header row */
.rowcard {
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r); padding: 13px 14px;
  display: grid; gap: 8px;
  transition: border-color .18s, transform .12s;
  animation: rise .3s cubic-bezier(.22,1,.36,1) backwards;
}
@media (hover: hover) {
  .rowcard:hover {
    border-color: var(--accent-line);
    background: color-mix(in srgb, var(--surface-2) 88%, var(--accent-dim));
  }
}
/* Alternating contrast on the desktop table only — on phones each row is
   already its own card and striping them fights the card edges. */
@media (min-width: 768px) {
  .datagrid .rowcard:nth-child(even) { background: color-mix(in srgb, var(--surface-2) 94%, var(--bg)); }
}
.rowcard .cell { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; min-width: 0; }
.rowcard .cell::before {
  content: attr(data-label);
  font-size: .72rem; color: var(--muted); font-weight: 500; flex: none;
  letter-spacing: .01em;
}
.rowcard .cell.no-label::before { content: none; }
.rowcard .cell > * { min-width: 0; }
.cell-v { font-weight: 600; text-align: right; }

/* the identity cell of each card (symbol + name) spans the top */
.rc-title { display: flex; align-items: center; gap: 9px; min-width: 0; }
/* The symbol is what a trader scans for, so it is the only bold thing in the
   row and it is mono — NVDA and AAPL then occupy the same visual width. */
.rc-sym {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-weight: 700; font-size: 1rem; letter-spacing: .01em;
}
.rc-name { font-size: .74rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rc-actions { display: flex; gap: 6px; flex-wrap: wrap; }

/* rank badge for scanner / backtest */
.rank {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; flex: none; border-radius: 8px;
  background: var(--surface-3); color: var(--text-dim);
  font-family: "IBM Plex Mono", monospace; font-size: .78rem; font-weight: 700;
}
.rank.top { background: var(--accent-dim); color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent-line); }
/* Ranks 1-3 in champagne. A tint and a hairline, not a medal — this is a
   ranked list of securities, not a leaderboard. */
.rank.gold, .rank.top:nth-child(-n+3) {
  background: var(--gold-dim); color: var(--gold-soft);
  box-shadow: inset 0 0 0 1px var(--gold-line);
}

/* strength / weight meters */
.meter { height: 6px; border-radius: 99px; background: var(--surface-3); overflow: hidden; flex: 1; min-width: 40px; }
.meter > i { display: block; height: 100%; border-radius: 99px; }
.meter-row { display: flex; align-items: center; gap: 8px; width: 100%; }

/* legacy tables (fundamentals etc.) still readable */
table { width: 100%; border-collapse: collapse; font-size: .86rem; }
th, td { text-align: left; padding: 11px 12px; border-bottom: 1px solid var(--line-cool); }
tbody tr { transition: background var(--t-fast); }
@media (hover: hover) { tbody tr:hover { background: var(--accent-dim); } }
tr:last-child td { border-bottom: none; }
th {
  color: var(--muted); font-weight: 600; font-size: .7rem;
  text-transform: uppercase; letter-spacing: .08em;
  font-family: "Inter", "Noto Sans Thai", sans-serif;
}
.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* correlation heatmap — square-ish cells so a 4-asset matrix fits a phone */
.corr-table { font-size: .78rem; }
.corr-table th, .corr-table td { padding: 7px 6px; text-align: center; border: 1px solid var(--line); }
.corr-table th { font-size: .74rem; color: var(--text-dim); text-transform: none; letter-spacing: 0; white-space: nowrap; }
.corr-table td { font-family: "IBM Plex Mono", monospace; font-variant-numeric: tabular-nums; border-radius: 4px; }

/* ---------------- chips ---------------- */
.sig, .sent, .buy-now, .badge, .method-tag {
  display: inline-flex; align-items: center; gap: 5px;
  border-radius: 999px; padding: 3px 11px;
  font-size: .74rem; font-weight: 600; line-height: 1.5;
  border: 1px solid transparent;
}
.sig.buy   { background: color-mix(in srgb, var(--up) 14%, transparent); color: var(--up); border-color: color-mix(in srgb, var(--up) 30%, transparent); }
.sig.hold  { background: var(--surface-3); color: var(--text-dim); border-color: var(--line); }
.sig.caution { background: color-mix(in srgb, var(--warn) 15%, transparent); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 32%, transparent); }
.sig.sell  { background: color-mix(in srgb, var(--down) 14%, transparent); color: var(--down); border-color: color-mix(in srgb, var(--down) 30%, transparent); }
.sent.positive { background: color-mix(in srgb, var(--up) 13%, transparent); color: var(--up); }
.sent.negative { background: color-mix(in srgb, var(--down) 13%, transparent); color: var(--down); }
.sent.neutral  { background: var(--surface-3); color: var(--muted); }
.badge { background: var(--surface-3); color: var(--text-dim); font-family: "IBM Plex Mono", monospace; }
.badge.gold {
  background: var(--gold-dim); color: var(--gold-soft);
  border-color: var(--gold-line);
}
.method-tag { background: var(--surface-3); color: var(--info); font-size: .74rem; padding: 2px 9px; margin: 1px 2px 1px 0; }
.buy-now.yes { background: color-mix(in srgb, var(--up) 14%, transparent); color: var(--up); }
.buy-now.no  { background: color-mix(in srgb, var(--warn) 14%, transparent); color: var(--warn); }

/* ---------------- entry plan ---------------- */
.entry-card {
  background: linear-gradient(160deg, color-mix(in srgb, var(--up) 8%, var(--surface)), var(--surface) 60%);
  border-color: color-mix(in srgb, var(--up) 26%, transparent);
}
.entry-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 8px; margin-top: 10px; }
.entry-item, .psum, .bt-item, .fund-item, .comp {
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: 11px 13px;
}
.entry-item .lbl, .psum .lbl, .bt-item .lbl, .fund-item .lbl {
  font-size: .7rem; color: var(--muted); letter-spacing: .02em;
}
.entry-item .val, .psum .val, .bt-item .val, .fund-item .val {
  font-size: 1.12rem; font-weight: 600; margin-top: 3px; letter-spacing: -.02em;
}

/* ---------------- portfolio ---------------- */
.port-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.port-head h2 { margin: 0; }
.port-head > div { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.port-form {
  display: grid; gap: 8px; margin: 12px 0; padding: 13px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r);
}
.port-summary { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin: 12px 0; }
.port-summary .psum:first-child {
  grid-column: 1 / -1;
  background: linear-gradient(150deg, var(--surface-3), var(--surface-2));
  border-color: var(--line-strong);
}
.port-summary .psum:first-child .val { font-size: 1.7rem; }
.sym-link { color: var(--text); }
.sym-link:hover { color: var(--accent); text-decoration: none; }

.dca-box { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r); padding: 14px; margin-top: 12px; }
.dca-inputs { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin: 8px 0; }
.dca-result { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 8px; margin-top: 10px; }

/* allocation bar */
.alloc-bar { display: flex; height: 10px; border-radius: 99px; overflow: hidden; margin: 8px 0 10px; background: var(--surface-3); }
.alloc-bar > div { height: 100%; }
.alloc-legend { display: flex; flex-wrap: wrap; gap: 10px; font-size: .76rem; color: var(--text-dim); margin-bottom: 8px; }
.alloc-legend i { display: inline-block; width: 9px; height: 9px; border-radius: 3px; margin-right: 5px; }

/* score components */
.comp-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 8px; }
.comp .comp-head { display: flex; justify-content: space-between; font-weight: 600; margin-bottom: 6px; font-size: .86rem; }
.comp ul { list-style: none; }
.comp li { font-size: .8rem; color: var(--text-dim); padding: 2px 0; line-height: 1.5; }
.comp li::before { content: "▸ "; color: var(--accent); }

.fund-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; margin: 12px 0; }
.fund-item .fnote { font-size: .72rem; color: var(--muted); }
.fund-item .fbar { height: 4px; border-radius: 99px; background: var(--surface-3); margin-top: 7px; overflow: hidden; }
.fund-item .fbar > div { height: 100%; border-radius: 99px; }
.company-info { font-size: .84rem; color: var(--text-dim); line-height: 1.65; margin-top: 8px; }

/* ---------------- multi-factor blend ---------------- */
.factor-summary { display: flex; align-items: center; gap: 14px; margin: 14px 0 4px; }
.factor-summary .fs-num { font-family: "IBM Plex Mono", monospace; font-size: 2.1rem; font-weight: 600; line-height: 1; }
.factor-summary .fs-txt { font-size: .88rem; line-height: 1.5; }
.factor-summary .fs-txt .mini-note { margin: 2px 0 0; }

.factor-caveat {
  font-size: .78rem; color: var(--text-dim); line-height: 1.6;
  background: color-mix(in srgb, var(--warn) 10%, transparent);
  border-left: 3px solid var(--warn); border-radius: 8px; padding: 9px 12px; margin: 10px 0;
}

.factor-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 10px; margin: 12px 0; }
.factor-card { background: var(--surface-2); border: 1px solid var(--line); border-radius: 12px; padding: 12px 13px; }
.factor-card.off { opacity: .62; }
.factor-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.factor-name { font-weight: 600; font-size: .87rem; }
.factor-score { font-family: "IBM Plex Mono", monospace; font-weight: 600; font-size: 1.15rem; }
.factor-card .fbar { height: 4px; border-radius: 99px; background: var(--surface-3); margin: 8px 0 9px; overflow: hidden; }
.factor-card .fbar > div { height: 100%; border-radius: 99px; }
.factor-why { font-size: .69rem; color: var(--muted); line-height: 1.55; }
.factor-rows { margin-top: 9px; border-top: 1px solid var(--line); padding-top: 7px; }
.factor-row { display: grid; grid-template-columns: 1fr auto 34px; gap: 8px; align-items: baseline; padding: 3px 0; font-size: .76rem; }
.factor-row .frl { color: var(--text-dim); }
.factor-row .frv { font-weight: 600; }
.factor-row .frs { text-align: right; font-family: "IBM Plex Mono", monospace; font-size: .72rem; }

.bt-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; margin-top: 8px; }
.bt-item { text-align: center; }

/* news */
.news-item { padding: 12px 0; border-bottom: 1px solid var(--line); }
.news-item:last-child { border-bottom: none; }
.news-item a { color: var(--text); font-weight: 600; font-size: .9rem; line-height: 1.45; }
.news-item a:hover { color: var(--accent); text-decoration: none; }
.news-meta { font-size: .72rem; color: var(--muted); margin-top: 4px; }

/* chart */
.chart-box {
  height: 300px; border-radius: var(--r); overflow: hidden;
  border: 1px solid var(--line); margin-bottom: 8px; background: var(--chart-bg);
}
.legend { font-size: .74rem; color: var(--muted); margin-bottom: 10px; }
.legend .g { color: var(--up); } .legend .r { color: var(--down); }

/* ai panel */
.ai-controls { display: flex; gap: 8px; margin: 12px 0; flex-wrap: wrap; }
.ai-controls input { flex: 1; min-width: 180px; }
.claude-out {
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r); padding: 16px; line-height: 1.8;
  white-space: pre-wrap; font-size: .9rem;
}

.mini-note { color: var(--muted); font-size: .8rem; margin: 4px 0; line-height: 1.55; }
.disclaimer { color: var(--muted); font-size: .72rem; text-align: center; margin: 20px 0 8px; opacity: .85; }
.spin { display: inline-block; animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* stagger the reveal of list items — one orchestrated moment, not noise */
.rowcard:nth-child(1) { animation-delay: .02s }
.rowcard:nth-child(2) { animation-delay: .06s }
.rowcard:nth-child(3) { animation-delay: .10s }
.rowcard:nth-child(4) { animation-delay: .14s }
.rowcard:nth-child(5) { animation-delay: .18s }
.rowcard:nth-child(n+6) { animation-delay: .22s }

/* ============================================================
   TABLET / DESKTOP
   ============================================================ */
@media (min-width: 720px) {
  /* was 15.5px — kept to the same value, but in rem so it stays inside the
     one sizing system instead of being a second, fixed one */
  body { padding-bottom: 0; font-size: .97rem; }
  .tabbar { display: none; }
  .mainnav { display: flex; }
  .header-row { padding: 12px 24px; }
  main, .port-wrap { padding: 20px 24px 40px; }
  .analyze-bar { padding: 20px 24px 0; }
  .card { padding: 20px; border-radius: var(--r-lg); }
  .chart-box { height: 400px; }
  #curPrice { font-size: 2.3rem; }
  .overview { grid-template-columns: 340px 1fr; align-items: start; }
  .port-summary { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
  .port-summary .psum:first-child { grid-column: auto; }
  .port-summary .psum:first-child .val { font-size: 1.12rem; }
  .port-form { grid-auto-flow: column; grid-auto-columns: max-content; align-items: center; }

  /* datagrid becomes a real table: aligned columns + sticky-ish header */
  .datagrid { gap: 0; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
  /* the column template only exists from tablet up — on phones each row stays
     a single-column card, which is why --cols is a variable and not inline CSS */
  .dg-head, .rowcard { grid-template-columns: var(--cols); }
  .dg-head {
    display: grid; gap: 12px; padding: 10px 14px;
    background: var(--surface-2); border-bottom: 1px solid var(--line);
    font-size: .74rem; color: var(--muted); font-weight: 600;
    text-transform: uppercase; letter-spacing: .08em;
  }
  .rowcard {
    align-items: center; gap: 12px;
    border: none; border-bottom: 1px solid var(--line); border-radius: 0;
    background: var(--surface); padding: 12px 14px;
  }
  .rowcard:last-child { border-bottom: none; }
  .rowcard:hover { background: var(--surface-2); }
  .rowcard .cell { display: block; }
  .rowcard .cell::before { content: none; }     /* header row supplies the labels */
  .rowcard .cell:not(.rc-first):not(.rc-sigline) { text-align: right; }
  .rowcard .cell:not(.rc-first):not(.rc-sigline) .cell-v { text-align: right; }
  .dg-head > span:not(:first-child) { text-align: right; }
  .dg-head > span:last-child { text-align: left; }
  /* the signal sub-line sits under its row, full width */
  .rc-sigline { border-top: 1px dashed var(--line); padding-top: 6px; margin-top: 2px; }
  .rowcard .rc-title .sig { margin-left: 8px !important; }
}

@media (min-width: 1000px) {
  .comp-grid { grid-template-columns: repeat(4, 1fr); }
}

/* respect reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ---------------- touch targets ----------------
 * Last in the file on purpose: these are overrides, and several of the rules
 * they correct (.rc-actions, .row-btn) are declared further down than where
 * this block would otherwise sit, so an earlier copy silently lost.
 *
 * Keyed to the input device rather than screen width — a finger needs room
 * whatever size the phone is, and a mouse does not need it on a narrow window.
 * 48px is Android's Material minimum and the larger of the two common
 * guidelines (Apple asks 44), so it satisfies both. Before this block, 15 of
 * 21 controls were under it: the live-price toggle was 21x15px, and "ลบ" sat
 * 6px from "แก้ไข" at 33px tall — a delete button narrower than a thumb. */
@media (pointer: coarse) {
  button, .row-btn, .icon-btn, .live-toggle, input, select, .chip { min-height: 48px; }
  button, .row-btn { padding-block: 11px; }
  .icon-btn { width: 48px; height: 48px; }
  .live-toggle {
    min-width: 48px; display: inline-flex; align-items: center; justify-content: center;
  }
  .rc-actions { gap: 12px; }               /* was 6px, between DCA / แก้ไข / ลบ */
  .row-btn { padding-inline: 14px; }
  .rowcard a { display: inline-block; min-height: 48px; line-height: 2.8; }
  /* Height was already 48px; width was not, so a two-character button like
     "ลบ" came out 41px across and a short ticker link 38px - both below the
     48dp Android asks for in *both* directions, and both of them destructive
     or navigational enough that a missed tap costs something. */
  .row-btn, .rowcard a, .icon-btn, .chip { min-width: 48px; }
}

/* ---------------- narrow phones ----------------
 * The header was a flex row of fixed-width parts: a brand locked to nowrap,
 * a live badge carrying dot + "LIVE" + clock + toggle, and a theme button.
 * Together they measured 429px, so every iPhone from the SE at 320 up to the
 * Pro Max at 430 scrolled sideways on the very first screen. It went unseen
 * because the preview pane is 446px wide — just wide enough to hide it.
 *
 * The word "LIVE" goes first: the dot beside it already carries that state in
 * colour. The clock stays — on a live-price app, knowing how fresh a number
 * is matters more than a label repeating what the dot says. */
@media (max-width: 480px) {
  .header-row { gap: 8px; padding: 10px 12px; }
  .brand {
    font-size: .92rem;
    min-width: 0; overflow: hidden; text-overflow: ellipsis;   /* last-resort truncation */
  }
  .brand small { display: none; }
  #liveText { display: none; }
  .live-badge { padding: 4px 4px 4px 8px; gap: 5px; }
}

/* ---------------- settings page ---------------- */
.set-grid { display: flex; flex-direction: column; gap: 2px; margin: 10px 0 4px; }
.set-row {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 11px 0; border-bottom: 1px solid var(--line); min-width: 0;
}
.set-row:last-child { border-bottom: none; }
.set-row > span:first-child { color: var(--text-dim); font-size: .86rem; flex: 1; min-width: 0; }
.set-row > b { font-weight: 600; text-align: right; word-break: break-word; }
.set-row input[type="number"], .set-row select { max-width: 190px; flex: none; }
.set-row input[type="checkbox"] { width: 22px; height: 22px; accent-color: var(--accent); flex: none; }
.set-row code { font-size: .74rem; word-break: break-all; }

/* ==================== CALENDAR ==================== */
.cal-filters { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 14px; }
.chip {
  background: var(--surface-2); color: var(--text-dim);
  border: 1px solid var(--line); border-radius: 999px;
  padding: 6px 12px; font-size: .8rem; cursor: pointer;
  font-family: inherit; transition: background .15s, color .15s;
}
.chip:hover { color: var(--text); }
.chip.on { background: var(--accent); color: #fff; border-color: var(--accent); }

.cal-day { display: flex; gap: 12px; padding: 10px 0; border-top: 1px solid var(--line); }
.cal-day:first-child { border-top: none; }
.cal-date {
  flex: 0 0 58px; text-align: center; display: flex; flex-direction: column;
  align-items: center; justify-content: flex-start; padding-top: 4px;
}
.cal-dnum { font-size: 1.5rem; font-weight: 700; line-height: 1; }
.cal-dmon { font-size: .74rem; color: var(--text-dim); }
.cal-dwd  { font-size: .7rem; color: var(--muted); margin-top: 2px; }
.cal-items { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 8px; }

.cal-item {
  background: var(--surface-2); border-radius: 8px;
  border-left: 3px solid var(--muted); padding: 8px 11px;
}
.cal-item.past { opacity: .5; }
.cal-item-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 7px; }
.cal-time { font-variant-numeric: tabular-nums; color: var(--text-dim); font-size: .8rem; }
.cal-imp  { color: var(--warn); font-size: .68rem; letter-spacing: 1px; }
.cal-cd   { margin-left: auto; font-size: .76rem; color: var(--accent); font-variant-numeric: tabular-nums; }
.cal-detail { font-size: .8rem; color: var(--text-dim); margin-top: 4px; line-height: 1.5; }
.cal-local  { font-size: .72rem; color: var(--muted); margin-top: 3px; }

/* An estimated date must not be able to pass for a confirmed one at a glance. */
.cal-flag { font-size: .74rem; padding: 1px 7px; border-radius: 999px; white-space: nowrap; }
.cal-flag.ok  { background: color-mix(in srgb, var(--up) 18%, transparent); color: var(--up); }
.cal-flag.est { background: color-mix(in srgb, var(--warn) 20%, transparent); color: var(--warn); }

@media (max-width: 640px) {
  .cal-date { flex-basis: 46px; }
  .cal-dnum { font-size: 1.25rem; }
  .cal-cd { margin-left: 0; flex-basis: 100%; }
}

/* live event results */
.live-box {
  background: var(--surface-2); border-radius: var(--r-sm);
  border-left: 3px solid var(--accent); padding: 11px 13px; margin-bottom: 12px;
}
.live-box.watching { border-left-color: var(--down); }
.live-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; margin-bottom: 9px; }
.live-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(155px, 1fr)); gap: 9px; }
.live-cell { background: var(--surface); border-radius: var(--r-sm); padding: 9px 11px; }
.lc-lbl { font-size: .74rem; color: var(--text-dim); }
.lc-val { font-size: 1.1rem; font-weight: 700; margin: 2px 0; font-variant-numeric: tabular-nums; }
.lc-sub { font-size: .7rem; color: var(--muted); }
.lc-link { font-size: .72rem; color: var(--accent); text-decoration: none; display: inline-block; margin-top: 4px; }
.lc-link:hover { text-decoration: underline; }

/* ============================================================
   TOUCH SCREENS: no pointer, so :hover must not linger
   ============================================================
   A tap on Android matches :hover and the style stays applied until something
   else is tapped, so a button the user pressed a minute ago is still lit and a
   card they opened is still raised. Fifteen rules in this file change colour,
   background or position on hover and none of them were guarded.

   Each override restores the element's own resting value - taken from its base
   rule, not guessed - and every selector that has a selected state excludes it,
   because these rules sit at the end of the file and would otherwise outrank
   .active and .on at equal specificity and strip the highlight from whichever
   tab or chip the user is actually on. */
@media (hover: none) {
  a:hover { text-decoration: none; }
  .mainnav button:hover:not(.active) { color: var(--text-dim); }
  .icon-btn:hover { color: var(--text-dim); border-color: var(--line); }
  .live-toggle:hover { color: var(--text-dim); background: none; }
  button.primary:hover { filter: none; }
  .row-btn:hover { color: var(--text-dim); border-color: var(--line); background: var(--surface-2); }
  .quick span:hover { color: var(--text-dim); border-color: var(--line); background: var(--surface); }
  .tf-chip:hover { border-color: var(--line); transform: none; }
  .tabs button:hover:not(.active) { color: var(--text-dim); }
  .rowcard:hover { border-color: var(--line); }
  .sym-link:hover { color: var(--accent); text-decoration: none; }
  .news-item a:hover { color: var(--accent); }
  .chip:hover:not(.on) { color: var(--text-dim); }
  .lc-link:hover { text-decoration: none; }
}

/* The default tap flash is a light blue square, which on this palette reads as
   a rendering fault rather than feedback. */
* { -webkit-tap-highlight-color: color-mix(in srgb, var(--accent) 22%, transparent); }

/* ============================================================================
 * Mascot & brand surfaces
 *
 * The rule the whole brand hangs on: she appears in three places and nowhere
 * else. Header, one AI insight, one floating button. Repeating her inside every
 * card is what turns a financial terminal into a fan page, and the numbers stop
 * being the thing you look at first.
 * ========================================================================== */

/* --- header avatar: gold ring, emerald inner glow -------------------------- */
.brand-avatar {
  position: relative; flex: none;
  width: 46px; height: 46px; border-radius: 50%;
  display: grid; place-items: center;
  background: radial-gradient(circle at 50% 40%, rgba(15, 175, 122, .22), transparent 68%), var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--gold-line), 0 2px 12px -6px rgba(0, 0, 0, .8);
  transition: transform var(--t) var(--ease), box-shadow var(--t);
}
.brand-avatar > img {
  width: 100%; height: 100%; border-radius: 50%; object-fit: cover;
  /* the source is a circular badge on white; clipping to the ring keeps the
     white plate from showing as a square behind the gold */
  object-position: center 42%;
}
@media (hover: hover) {
  .brand-avatar:hover {
    transform: scale(1.03);
    box-shadow: inset 0 0 0 1px var(--gold-soft), 0 4px 18px -8px rgba(214, 168, 75, .55);
  }
}
@media (max-width: 480px) { .brand-avatar { width: 38px; height: 38px; } }

/* --- AI insight: her one speaking part ------------------------------------ */
.ai-insight {
  display: flex; gap: var(--s3); align-items: flex-start;
  border: 1px solid var(--gold-line); border-radius: var(--r);
  background:
    radial-gradient(140% 110% at 0% 0%, rgba(15, 175, 122, .10), transparent 55%),
    var(--surface);
  padding: var(--s4);
  margin-bottom: var(--s3);
}
.ai-insight > img {
  width: 52px; height: 52px; flex: none; border-radius: 50%;
  object-fit: cover; object-position: center 22%;
  box-shadow: inset 0 0 0 1px var(--gold-line);
  background: var(--surface-2);
}
.ai-insight .ai-body { min-width: 0; }
.ai-insight .ai-kicker {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: "IBM Plex Mono", monospace; font-size: .68rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--gold-soft);
  margin-bottom: 4px;
}
.ai-insight .ai-text { font-size: .92rem; color: var(--text); line-height: 1.5; }
.ai-insight .ai-text b { color: var(--accent); }

/* --- floating assistant ---------------------------------------------------- */
.ai-fab {
  position: fixed; right: 18px; z-index: 45;
  bottom: calc(var(--nav-h) + 18px + env(safe-area-inset-bottom, 0px));
  width: 52px; height: 52px; border-radius: 50%; border: 1px solid var(--gold-line);
  padding: 0; cursor: pointer; overflow: hidden;
  background: var(--surface-2);
  box-shadow: var(--shadow), 0 0 0 4px rgba(7, 11, 10, .55);
  transition: transform var(--t) var(--ease), box-shadow var(--t);
}
.ai-fab > img { width: 100%; height: 100%; object-fit: cover; object-position: center 20%; }
.ai-fab::after {
  content: "Ask AI";
  position: absolute; right: calc(100% + 10px); top: 50%; translate: 0 -50%;
  background: var(--surface-3); color: var(--text); border: 1px solid var(--line);
  border-radius: 8px; padding: 5px 10px; font-size: .76rem; font-weight: 600;
  white-space: nowrap; opacity: 0; pointer-events: none;
  transition: opacity var(--t-fast);
}
@media (hover: hover) {
  .ai-fab:hover { transform: scale(1.06); box-shadow: var(--shadow), 0 0 20px -8px var(--gold); }
  .ai-fab:hover::after { opacity: 1; }
}
@media (min-width: 768px) { .ai-fab { bottom: 22px; } }

/* Two or three pixels of drift, once every nine seconds. Enough to read as
   alive when you happen to look at her, not enough to pull your eye off a
   price while you are reading one. */
@keyframes mascotFloat {
  0%, 88%, 100% { transform: translateY(0); }
  94%           { transform: translateY(-3px); }
}
@media (prefers-reduced-motion: no-preference) {
  .ai-insight > img, .ai-fab > img { animation: mascotFloat 9s ease-in-out infinite; }
}

/* --- score dial: the AI portfolio health number --------------------------- */
.score-dial {
  --dial: 0;
  position: relative; width: 132px; height: 132px; flex: none;
  border-radius: 50%;
  background:
    conic-gradient(var(--accent-strong) 0, var(--accent) calc(var(--dial) * 1%), transparent 0),
    radial-gradient(closest-side, var(--surface) 78%, transparent 79%);
  display: grid; place-items: center;
}
.score-dial::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  box-shadow: inset 0 0 0 1px var(--gold-line);
  pointer-events: none;
}
.score-dial .dial-num {
  font-family: "IBM Plex Mono", monospace; font-variant-numeric: tabular-nums;
  font-size: 1.9rem; font-weight: 700; line-height: 1;
}
.score-dial .dial-cap {
  font-size: .68rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}

/* --- skeletons ------------------------------------------------------------- */
@keyframes shimmer { to { background-position: 200% 0; } }
.skeleton {
  border-radius: var(--r-sm); min-height: 14px;
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
}
@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; } }

/* --- desktop width ceiling -------------------------------------------------
 * Financial tables stretched across a 27-inch monitor put the symbol and its
 * P/L a forearm apart. 1320 is wide enough for the widest table here and no
 * wider. */
@media (min-width: 1400px) {
  main, .port-wrap, .analyze-bar, .status, .header-row { max-width: 1320px; }
}

/* Motion is decoration everywhere in this file; anyone who has asked for less
   of it gets the layout and none of the movement. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

/* ============================================================================
 * Phase two — the surfaces the brief singled out.
 *
 * All of it reached through existing class names. Not one id or class is
 * renamed, because the feature scripts write into #portHealth, #tfSummary,
 * #gaugeFill and the rest by name, and a rename here would empty a panel with
 * nothing in the console to say why.
 * ========================================================================== */

/* --- the score gauge, shared by the analyser and portfolio health ----------
 * Was a 7px bar. Now 10px with an emerald gradient and a champagne tip: the
 * end of the fill is the number you are actually reading, so it is the part
 * that gets the gold. The tip is a pseudo-element on the fill, so it tracks
 * the width animation for free. */
.gauge { height: 10px; background: var(--surface-3); box-shadow: inset 0 1px 3px rgba(0, 0, 0, .45); }
.gauge-fill {
  position: relative;
  background-image: linear-gradient(90deg, color-mix(in srgb, currentColor 55%, transparent), currentColor);
  box-shadow: 0 0 14px -4px currentColor;
}
.gauge-fill::after {
  content: ""; position: absolute; right: 0; top: 50%; translate: 50% -50%;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--gold-pale);
  box-shadow: 0 0 10px -1px var(--gold), 0 0 0 2px color-mix(in srgb, var(--bg) 70%, transparent);
}
.gauge-num { font-size: 2rem; min-width: 74px; letter-spacing: -.03em; }

/* The health panel is a headline, not a row in a list. */
#portHealthCard {
  border-color: var(--gold-line);
  background:
    radial-gradient(130% 90% at 100% 0%, var(--gold-dim), transparent 56%),
    var(--surface);
}
#portHealthCard > h2::after {
  content: "♛"; color: var(--gold-soft); font-size: .9rem; margin-left: 2px;
}

/* --- timeframe score cards: short / medium / long -------------------------
 * The brief wants these to read as three verdicts rather than three numbers.
 * The colour already arrives inline from scoreColor() in 02-views.js, so the
 * score keeps its own hue and the card only supplies structure: a bigger
 * figure, a quieter label, and a top rule that picks up whatever colour the
 * score resolved to. */
.tf-chip {
  padding: var(--s4) var(--s3);
  border-radius: var(--r);
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
}
.tf-chip::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: var(--line-strong);
}
.tf-chip .tf-name {
  font-size: .68rem; letter-spacing: .1em; text-transform: uppercase; font-weight: 600;
}
.tf-chip .tf-score { font-size: 2rem; margin: 4px 0 2px; }
.tf-chip .tf-verdict {
  font-size: .72rem; font-weight: 700; letter-spacing: .04em;
  padding: 3px 10px; border-radius: 999px;
  background: color-mix(in srgb, currentColor 13%, transparent);
  border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
}
@media (hover: hover) {
  .tf-chip:hover { transform: translateY(-2px); border-color: var(--accent-line); }
}
@media (min-width: 768px) {
  /* stacked under the score card on desktop, where .overview is 340px + 1fr,
     the three used to stretch into letterboxes */
  .tf-summary { gap: var(--s3); align-content: start; }
}

/* --- support / resistance tags -------------------------------------------- */
.meter { height: 7px; }
.meter > i { box-shadow: 0 0 8px -3px currentColor; }

/* --- calendar events as a timeline ---------------------------------------- */
.cal-item, .cal-row, .calendar-item {
  border-left: 2px solid var(--line-strong);
  padding-left: var(--s3);
  transition: border-color var(--t);
}
.cal-item.high, .cal-row.high, .calendar-item.high {
  border-left-color: var(--gold);
  background: linear-gradient(90deg, var(--gold-dim), transparent 42%);
}

/* --- settings: sections read as groups, not a config dump ------------------ */
#view-settings h3 {
  color: var(--gold-soft); font-size: .7rem; letter-spacing: .14em;
  padding-bottom: 6px; border-bottom: 1px solid var(--line);
  margin: var(--s6) 0 var(--s3);
}
#view-settings .card { margin-bottom: var(--s4); }

/* --- destructive actions --------------------------------------------------
 * Anything that deletes should never be the same shape as anything that
 * saves. Rose, outlined, and it fills only when you are already on it. */
.row-btn.danger, button.danger {
  color: var(--down); border-color: color-mix(in srgb, var(--down) 34%, transparent);
  background: color-mix(in srgb, var(--down) 8%, transparent);
}
.row-btn.danger:hover, button.danger:hover {
  color: #fff; background: var(--down); border-color: var(--down);
}

/* --- tabs: the sub-navigation inside the analyser ------------------------- */
.tabs button.active {
  color: var(--accent-soft);
  border-color: #29D99D;
  background: var(--accent-grad);
}

/* The observation list lost its ⚠️ when emoji came out of the financial
   components. A rule-and-dot marker carries the same "read this" weight
   without a picture of a road sign inside a portfolio panel. */
.note-line {
  position: relative; padding: 3px 0 3px 16px;
}
.note-line::before {
  content: ""; position: absolute; left: 2px; top: .78em;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--warn);
  box-shadow: 0 0 8px -2px var(--warn);
}

/* ============================================================================
 * Phase three — icons, and the four screens the brief called out by name.
 * ========================================================================== */

/* --- the icon set ---------------------------------------------------------
 * Inline SVG taking currentColor, so an icon is the same colour as the text it
 * sits beside without anyone having to say so twice. Sized in em rather than
 * px: a label that scales on a small phone takes its icon with it. */
.ic-svg { flex: none; vertical-align: -.15em; width: 1em; height: 1em; }
.psum .lbl .ic-svg, .card > h2 .ic-svg { opacity: .75; }

/* --- portfolio summary: five figures with a clear pecking order ------------
 * The label shrinks and goes upper-case, the number grows and goes mono. The
 * point is that the eye lands on the figure first and reads the label only to
 * confirm what it just saw. */
.psum .lbl {
  display: flex; align-items: center; gap: 7px;
  font-size: .7rem; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
}
.psum .val {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 1.32rem; font-weight: 600; letter-spacing: -.02em; margin-top: 3px;
}
.psum .val.up { text-shadow: 0 0 18px color-mix(in srgb, var(--up) 30%, transparent); }
.psum .val.down { text-shadow: 0 0 18px color-mix(in srgb, var(--down) 22%, transparent); }
@media (min-width: 768px) { .psum .val { font-size: 1.5rem; } }

/* --- bottom navigation ----------------------------------------------------
 * The glyphs were ◧ ◎ ◆ ◈ ◷ ⚙, which are six different typographic ideas
 * pretending to be a set. Now one stroke family, with the Thai label doing
 * the naming — colour alone never carries which tab is active. */
.tabbar button .ic { display: grid; place-items: center; width: 22px; height: 22px; }
.tabbar button .ic .ic-svg { width: 21px; height: 21px; }
.tabbar button.active .ic { transform: translateY(-2px); }

/* --- market scanner -------------------------------------------------------
 * A score bar rather than a medal. Gold at the left of the gradient into
 * emerald at the right, so a high score finishes green and a poor one never
 * gets there. */
.opp-bar {
  height: 8px; border-radius: 99px; background: var(--surface-3); overflow: hidden;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, .4);
}
.opp-bar > i {
  display: block; height: 100%; border-radius: 99px;
  background: linear-gradient(90deg, var(--gold), var(--accent));
  transition: width .8s var(--ease);
}

/* --- calendar: day block, then the event ---------------------------------- */
.cal-day {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-width: 52px; padding: 6px 8px; flex: none;
  border-radius: var(--r-sm); background: var(--surface-2);
  border: 1px solid var(--line);
}
.cal-day .d {
  font-family: "IBM Plex Mono", monospace; font-variant-numeric: tabular-nums;
  font-size: 1.35rem; font-weight: 700; line-height: 1;
}
.cal-day .m {
  font-size: .62rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}
/* Importance as one, two or three dots — countable, so it does not depend on
   telling three shades of the same colour apart. */
.imp { display: inline-flex; gap: 3px; align-items: center; }
.imp i { width: 5px; height: 5px; border-radius: 50%; background: var(--muted); }
.imp.lv2 i:nth-child(-n+2), .imp.lv1 i:nth-child(1) { background: var(--warn); }
.imp.lv3 i { background: var(--gold-soft); box-shadow: 0 0 6px -1px var(--gold); }

/* --- settings: six groups, not one long dump ------------------------------ */
#view-settings .card > h2 {
  padding-bottom: var(--s2); border-bottom: 1px solid var(--line);
  margin-bottom: var(--s4);
}

/* --- support / resistance tags -------------------------------------------- */
.lvl-tag {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: "IBM Plex Mono", monospace; font-size: .72rem; font-weight: 700;
  padding: 2px 8px; border-radius: 7px; letter-spacing: .04em;
}
.lvl-tag.s { background: color-mix(in srgb, var(--up) 14%, transparent); color: var(--up);
             border: 1px solid color-mix(in srgb, var(--up) 30%, transparent); }
.lvl-tag.r { background: color-mix(in srgb, var(--down) 13%, transparent); color: var(--down);
             border: 1px solid color-mix(in srgb, var(--down) 28%, transparent); }

/* --- chart ---------------------------------------------------------------- */
.chart-box {
  border-radius: var(--r); overflow: hidden;
  border: 1px solid var(--line); background: var(--chart-bg);
  animation: chartIn .4s var(--ease) backwards;
}
@keyframes chartIn { from { opacity: 0; } to { opacity: 1; } }

/* --- tooltips ------------------------------------------------------------- */
[data-tip] { position: relative; }
[data-tip]::after {
  content: attr(data-tip);
  position: absolute; bottom: calc(100% + 8px); left: 50%; translate: -50% 0;
  background: var(--surface-3); color: var(--text);
  border: 1px solid var(--line); border-radius: 8px;
  padding: 6px 10px; font-size: .74rem; font-weight: 500; white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity var(--t-fast);
  box-shadow: var(--shadow); z-index: 60;
}
@media (hover: hover) { [data-tip]:hover::after { opacity: 1; } }
