/* ==========================================================================
   Ticker Lab — shared design system
   ==========================================================================
   Vibe archetype ...... Ethereal Glass (OLED black, mesh aura, glass chrome)

   This file holds everything that is the same on every page: the tokens, the
   atmosphere layers, the double-bezel primitive, buttons, table base and the
   row hover card. Page-specific layout stays in that page's <style> block,
   which loads after this file and therefore wins on any overlap.

   Rules of the house:
     - backdrop-filter only on fixed/sticky elements, never a scrolling one
     - motion uses --ease / --ease-soft only, never linear or ease-in-out
     - --surface must stay opaque: frozen table cells paint on it
   ========================================================================== */

:root {
  color-scheme: dark;

  /* surfaces */
  --void: #050505;
  --surface: #0a0c11;          /* inner core — MUST stay opaque: frozen table cells sit on it */
  --surface-2: #0d1017;
  --shell: rgba(255, 255, 255, 0.028);
  --hair: rgba(255, 255, 255, 0.07);
  --hair-2: rgba(255, 255, 255, 0.13);
  --inset: inset 0 1px 0 rgba(255, 255, 255, 0.055);

  /* ink — contrast measured against --surface (#0a0c11), the card interior.
     Both greys clear WCAG AA (4.5:1). The previous values were 6.4:1 and
     3.1:1, and the failing one was painting column headers, hover-card field
     names and every em-dash placeholder. */
  --text: #e9ecf2;             /* 17.2:1 */
  --muted: #9aa3b2;            /*  7.7:1 */
  --faint: #7d8797;            /*  5.4:1 */

  /* signal */
  --green: #34d399;
  --red: #fb7185;
  --amber: #fbbf24;
  --accent: #7c9cff;
  --accent-2: #a78bfa;

  /* aliases kept so long-standing rules and inline styles keep resolving */
  --bg: var(--void);
  --panel: #141822;            /* opaque raised / row hover */
  --border: var(--hair);

  /* motion — spring-ish curves only, never linear or ease-in-out */
  --ease: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-soft: cubic-bezier(0.16, 1, 0.3, 1);

  /* systemic layers */
  --z-sheet: 45;
  --z-nav: 46;
  --z-tip: 55;
  --z-grain: 60;

  --r-shell: 26px;
  --r-core: 20px;              /* r-shell − bezel padding, for concentric curves */

  --sans: 'Geist', ui-sans-serif, system-ui, 'Segoe UI Variable Text', sans-serif;
  --mono: 'Geist Mono', ui-monospace, 'Cascadia Mono', 'SF Mono', monospace;
}

/* ==========================================================================
   Base
   ========================================================================== */
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; height: 100%; }
body {
  margin: 0;
  background: var(--void);
  color: var(--text);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  letter-spacing: -0.01em;
  -webkit-font-smoothing: antialiased;
}

/* ==========================================================================
   Atmosphere — fixed mesh aura + film grain, never attached to a scroller.
   A page may restate .aura's background to tint itself; the mechanics
   (position, drift, pointer transparency) live here.
   ========================================================================== */
.aura {
  position: fixed; inset: -10%; z-index: 0; pointer-events: none;
  background:
    radial-gradient(46vw 40vw at 6% -8%, rgba(52, 211, 153, 0.13), transparent 62%),
    radial-gradient(52vw 44vw at 98% 2%, rgba(124, 156, 255, 0.14), transparent 64%),
    radial-gradient(58vw 48vw at 48% 112%, rgba(167, 139, 250, 0.10), transparent 62%);
  animation: drift 44s cubic-bezier(0.45, 0, 0.55, 1) infinite alternate;
  will-change: transform;
}
@keyframes drift {
  from { transform: translate3d(-1.5%, -1%, 0) scale(1); }
  to   { transform: translate3d(2%, 1.5%, 0) scale(1.06); }
}
.grain {
  position: fixed; inset: 0; z-index: var(--z-grain); pointer-events: none;
  opacity: 0.038; mix-blend-mode: overlay; background-size: 170px 170px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='170' height='170'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='170' height='170' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ==========================================================================
   Icons — ultra-light strokes, currentColor
   ========================================================================== */
.ic { width: 16px; height: 16px; flex: none; stroke: currentColor; fill: none;
      stroke-width: 1.35; stroke-linecap: round; stroke-linejoin: round; }
.ic-sm { width: 13px; height: 13px; stroke-width: 1.5; }
#sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ==========================================================================
   Double-bezel primitive
   ========================================================================== */
.bezel {
  background: var(--shell);
  border: 1px solid var(--hair);
  border-radius: var(--r-shell);
  padding: 6px;
  box-shadow: 0 30px 80px -40px rgba(0, 0, 0, 0.95);
}
.bezel > .core {
  background: var(--surface);
  border-radius: var(--r-core);
  box-shadow: var(--inset);
  overflow: hidden;
  min-width: 0;
}

/* ==========================================================================
   Buttons — pills, magnetic hover, button-in-button trailing nub
   ========================================================================== */
button, select, input, a.btn { font-family: var(--sans); }
.btn {
  position: relative; display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 15px; border-radius: 999px;
  border: 1px solid var(--hair); background: rgba(255, 255, 255, 0.04);
  color: var(--text); font-family: var(--sans);
  font-size: 12.5px; font-weight: 500; letter-spacing: -0.01em;
  line-height: 1; white-space: nowrap; cursor: pointer; text-decoration: none;
  box-shadow: var(--inset);
  transition: background 0.55s var(--ease), border-color 0.55s var(--ease),
              color 0.55s var(--ease), transform 0.4s var(--ease), box-shadow 0.55s var(--ease);
}
.btn:hover { background: rgba(255, 255, 255, 0.085); border-color: var(--hair-2); color: #fff; }
.btn:active { transform: scale(0.975); }
.btn:disabled { opacity: 0.35; cursor: default; transform: none; }
.btn:disabled:hover { background: rgba(255, 255, 255, 0.04); border-color: var(--hair); color: var(--text); }
.btn:focus-visible { outline: none; box-shadow: var(--inset), 0 0 0 3px rgba(124, 156, 255, 0.35); }
.btn.icon { padding: 0; width: 34px; height: 34px; justify-content: center; gap: 0; }
.btn.quiet { background: none; border-color: transparent; color: var(--muted); }
.btn.quiet:hover { background: rgba(255, 255, 255, 0.05); color: var(--text); }
.btn.danger:hover { color: var(--red); border-color: rgba(251, 113, 133, 0.4); background: rgba(251, 113, 133, 0.1); }

/* primary CTA: the trailing icon sits in its own circle, flush to the inner padding */
.btn-primary {
  padding: 5px 5px 5px 16px; gap: 10px;
  background: linear-gradient(180deg, #ffffff, #dee3ed);
  color: #07090d; border-color: transparent; font-weight: 600;
  box-shadow: 0 14px 34px -14px rgba(255, 255, 255, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
.btn-primary:hover { background: linear-gradient(180deg, #ffffff, #e9edf5); color: #07090d; }
.btn-primary .nub {
  width: 26px; height: 26px; border-radius: 999px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(7, 9, 13, 0.08);
  transition: transform 0.55s var(--ease), background 0.55s var(--ease);
}
.btn-primary:hover .nub { transform: translate(2px, -1px) scale(1.07); background: rgba(7, 9, 13, 0.16); }

/* ==========================================================================
   Identity + page furniture
   ========================================================================== */
.mark { display: inline-flex; align-items: center; gap: 9px; }
.mark .wordmark { font-size: 13.5px; font-weight: 600; letter-spacing: -0.02em; white-space: nowrap; }
.divider { width: 1px; height: 20px; background: var(--hair); flex: none; margin: 0 2px; }

/* the second word of a wordmark fades away — used by .brand and h1 alike */
.brand .w2, h1 .w2 {
  background: linear-gradient(100deg, #9aa4b6 10%, #5d6474 92%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

.lede { color: var(--muted); font-size: 12.5px; line-height: 1.6; }
.note { color: var(--faint); font-size: 11.5px; }
.empty { text-align: center; color: var(--muted); padding: 56px 24px; font-size: 13px;
         font-family: var(--sans); line-height: 1.7; }

/* ==========================================================================
   Signal colours
   ========================================================================== */
.sym { font-weight: 600; }
/* A company name that links to its page. Inherits the row's colour and only
   underlines on hover — 69 rows of blue underlines would wreck a table. */
.namelink { color: inherit; text-decoration: none; }
.namelink:hover { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }
.pos { color: var(--green); }
.neg { color: var(--red); }
.na { color: var(--faint); }
.warn { color: var(--amber); }
.badge {
  display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 999px;
  border: 1px solid; font-size: 10.5px; font-weight: 500; white-space: nowrap;
  font-family: var(--sans);
}

/* ==========================================================================
   Table base — density and alignment stay with each page, since the screener,
   the analysis lists and the visitor log are three different tables.
   ========================================================================== */
table { width: 100%; border-collapse: collapse; }
th, td { font-variant-numeric: tabular-nums; }
th {
  color: var(--muted); font-weight: 500; text-transform: uppercase;
  /* 10.5px at 0.1em tracking: uppercase micro-type needs the size, and loses
     nothing from the tighter tracking. The old 9.5px/0.14em in --faint was
     the least legible text in the app. */
  font-size: 10.5px; letter-spacing: 0.1em;
}
tbody tr { transition: background 0.4s var(--ease); }
tbody tr:hover { background: var(--panel); }
@keyframes rowIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

.table-wrap { overflow: auto; }
.table-wrap::-webkit-scrollbar { width: 10px; height: 10px; }
.table-wrap::-webkit-scrollbar-track { background: transparent; }
.table-wrap::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.1); border-radius: 999px;
                                       border: 3px solid transparent; background-clip: content-box; }

/* ==========================================================================
   Row detail — the hover card, and the stock page, which shows the same
   sections at full size. Markup comes from rowcard.js in both cases, so the
   .rc-* classes are deliberately unscoped; only the floating container below
   is tied to #rowcard.
   ========================================================================== */
#rowcard {
  position: fixed; display: none; z-index: var(--z-tip);
  width: 660px; max-width: calc(100vw - 24px);
  padding: 6px; border-radius: 20px;
  background: rgba(255, 255, 255, 0.03); border: 1px solid var(--hair);
  backdrop-filter: blur(26px) saturate(160%); -webkit-backdrop-filter: blur(26px) saturate(160%);
  box-shadow: 0 40px 80px -28px rgba(0, 0, 0, 0.95);
  font-size: 11.5px; color: var(--text);
}
.rc-core { background: rgba(10, 12, 17, 0.92); border-radius: 15px;
                    padding: 14px 16px; box-shadow: var(--inset); }
.rc-head { display: flex; align-items: baseline; gap: 10px;
                    padding-bottom: 10px; margin-bottom: 10px; border-bottom: 1px solid var(--hair); }
.rc-sym { font-family: var(--mono); font-size: 14px; font-weight: 600; letter-spacing: 0.02em; }
.rc-name { color: var(--muted); font-size: 12px; min-width: 0;
                    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rc-price { margin-left: auto; font-family: var(--mono); font-size: 13px; white-space: nowrap; }
/* price history — drawn from the bar archive, fetched on hover and cached */
.rc-chart {
  position: relative; height: 104px; margin: 0 0 12px;
  border-radius: 10px; background: rgba(255, 255, 255, 0.022);
  border: 1px solid var(--hair); overflow: hidden;
}
.rc-chart svg { display: block; width: 100%; height: 100%; }
.rc-chart .ln { fill: none; stroke-width: 1.6; vector-effect: non-scaling-stroke; }
.rc-chart .dot { stroke: none; }
/* Sparklines in the table. Neutral on purpose: the five percentage columns
   immediately to the right are already green and red, and a sixth coloured
   element there would be noise. The shape carries the information; colour is
   left to the numbers. */
svg.spark { display: block; width: 100%; height: 20px; overflow: visible; }
svg.spark path { fill: none; stroke: var(--muted); stroke-width: 1.25;
                 stroke-linejoin: round; vector-effect: non-scaling-stroke; }
tbody tr:hover svg.spark path { stroke: var(--text); }

/* volume bars sit in their own band under the price; green on an up session,
   red on a down one, dimmed so they never compete with the price line */
.rc-chart .vb { stroke: none; opacity: 0.55; }
.rc-chart .vb.vu { fill: var(--green); }
.rc-chart .vb.vd { fill: var(--red); }
/* the flat line marking where the period opened, so the shape reads against it */
.rc-chart .base { stroke: var(--hair-2); stroke-width: 1; stroke-dasharray: 2 3; }
/* overlays sit in a neutral colour so they read as a second series rather than
   as part of the price, which is already green or red by direction */
.rc-chart .ov { fill: none; stroke-width: 1.2; opacity: 0.9;
                vector-effect: non-scaling-stroke; }
.rc-chart .ov.ma50 { stroke: var(--accent); }
.rc-chart .ov.ma200 { stroke: var(--amber); }
/* stroke and fill kept apart on purpose: grouping them set fill on the line
   path too, which closed it back to its start and drew a filled wedge. The
   area path hid that until it was removed. */
.rc-chart.up .ln { stroke: var(--green); }
.rc-chart.up .dot { fill: var(--green); stroke: none; }
.rc-chart.down .ln { stroke: var(--red); }
.rc-chart.down .dot { fill: var(--red); stroke: none; }
/* price gridlines — present enough to read a level against, faint enough that
   the line stays the subject */
.rc-chart .grid { stroke: rgba(255, 255, 255, 0.055); stroke-width: 1;
                  vector-effect: non-scaling-stroke; }
.rc-chart .cap {
  position: absolute; font-family: var(--mono); font-size: 9.5px; color: var(--faint);
  font-variant-numeric: tabular-nums; pointer-events: none;
  /* the labels sit on top of the area fill, so they need their own ground —
     the same legibility problem the greys had, in miniature */
  text-shadow: 0 0 4px rgba(10, 12, 17, 0.95), 0 0 2px rgba(10, 12, 17, 0.95);
}
.rc-chart .cap.hi { top: 6px; right: 8px; }
.rc-chart .cap.lo { bottom: 6px; right: 8px; }
.rc-chart .cap.from { bottom: 6px; left: 9px; }
.rc-chart .cap.chg { top: 6px; left: 9px; font-size: 10.5px; }
.rc-chart.up .cap.chg { color: var(--green); }
.rc-chart.down .cap.chg { color: var(--red); }
.rc-chart .msg {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: var(--faint); font-size: 11px;
}

.rc-cols { column-count: 3; column-gap: 20px; }
.rc-sec { break-inside: avoid; margin-bottom: 11px; }
.rc-sec-h { font-size: 9.5px; font-weight: 500; text-transform: uppercase;
                     letter-spacing: 0.14em; margin-bottom: 4px;
                     display: flex; align-items: center; gap: 6px; }
.rc-sec-h i { width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex: none; }
.rc-row { display: flex; align-items: baseline; gap: 8px; line-height: 1.75; }
.rc-k { color: var(--faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rc-v { margin-left: auto; font-family: var(--mono); font-size: 11px;
                 white-space: nowrap; font-variant-numeric: tabular-nums; }
.rc-foot { margin-top: 4px; padding-top: 9px; border-top: 1px solid var(--hair);
                    color: var(--faint); font-size: 10.5px;
                    display: flex; align-items: center; gap: 12px; }
.rc-acts { margin-left: auto; display: flex; gap: 6px; flex: none; }
.rc-act {
  padding: 6px 11px; border-radius: 999px; cursor: pointer; line-height: 1;
  border: 1px solid var(--hair); background: rgba(255, 255, 255, 0.04);
  color: var(--muted); font-family: var(--sans); font-size: 11px; white-space: nowrap;
  transition: background 0.4s var(--ease), color 0.4s var(--ease), border-color 0.4s var(--ease);
}
.rc-act:hover { background: rgba(255, 255, 255, 0.09); color: var(--text); }
.rc-act.danger:hover { color: var(--red); border-color: rgba(251, 113, 133, 0.4);
                                background: rgba(251, 113, 133, 0.12); }

/* ==========================================================================
   Scroll-in reveal
   ========================================================================== */
.reveal {
  opacity: 0; transform: translateY(46px); filter: blur(10px);
  transition: opacity 0.9s var(--ease-soft), transform 0.9s var(--ease-soft), filter 0.9s var(--ease-soft);
  transition-delay: var(--d, 0ms);
}
.reveal.in { opacity: 1; transform: none; filter: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .reveal { opacity: 1; transform: none; filter: none; }
}
