/* ══════════════════════════════════════════════════════════════════════════
   MAD TERMINAL — POLISH LAYER
   The single authority for tokens, motion and craft. Loaded LAST so it wins.

   THESIS
   In a trading terminal the data is the content. Everything here exists to
   make a number faster to read and act on: tabular figures so prices do not
   jitter as they tick, colour reserved for signal, and surfaces that recede
   to hairlines so nothing competes with the figure it contains.

   WHY THIS FILE EXISTS
   The app was running three design systems at once — index.html's Terminal
   Noir tokens, a runtime <style> from mad-theme.js that redefined :root with
   a different purple, and polish.css which was never linked at all. This
   consolidates them into one.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. TOKENS ────────────────────────────────────────────────────────────
   Terminal Noir, kept. The only additions are a real type scale and a
   motion vocabulary, both of which were implied but never written down. */
:root{
  /* surface — four steps from void to raised, no more */
  --bg:#050509; --bg2:#08070d; --panel:#0e0c16; --panel2:#151225;

  /* hairlines, never borders. A 1px line at 4.5% is a seam; at 20% it is a box. */
  --line:rgba(255,255,255,.045); --line2:rgba(255,255,255,.09);

  /* text — three weights of attention */
  --txt:#f3f1fa; --dim:#9c94b8; --faint:#5c5578;

  /* brand */
  --mad:#8b5cf6; --mad2:#b49aff; --madDeep:#6d28d9; --madGlow:rgba(139,92,246,.4);

  /* SIGNAL ONLY. If it is green it made money. If it is red it lost money.
     These two colours are never used for decoration anywhere in the app. */
  --green:#33e2a0; --greenDim:#0e7350; --red:#ff5470; --redDim:#7f2440;
  --gold:#e8b64f;

  --r:20px; --r2:14px; --r3:10px;

  --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,monospace;
  --disp:'Space Grotesk',system-ui,-apple-system,sans-serif;
  --head:'Unbounded','Space Grotesk',system-ui,sans-serif;

  --sp1:4px; --sp2:8px; --sp3:12px; --sp4:16px; --sp5:24px; --sp6:32px; --sp7:40px; --sp8:48px;

  /* type scale — 1.25 minor third, capped so nothing shouts on a phone */
  --t-micro:10px; --t-cap:11px; --t-sm:13px; --t-base:15px;
  --t-lg:18px; --t-xl:23px; --t-2xl:29px; --t-3xl:36px; --t-4xl:46px;

  /* motion — two curves, and that is deliberate. More vocabularies than this
     and the app stops feeling like one object. */
  --ease:cubic-bezier(.22,1,.36,1);        /* surfaces: fast settle, no overshoot */
  --spring:cubic-bezier(.34,1.56,.64,1);   /* actions: slight overshoot, feels alive */
  --dur-1:120ms; --dur-2:200ms; --dur-3:320ms;

  --glass-sheen:linear-gradient(180deg,rgba(255,255,255,.055),rgba(255,255,255,.014) 42%,transparent 72%);
  --glass-base:linear-gradient(165deg,rgba(27,21,46,.66),rgba(9,7,17,.86) 65%);
  --card-grad:var(--glass-sheen),var(--glass-base);

  --e1:0 1px 0 rgba(255,255,255,.035) inset,0 2px 10px -6px rgba(0,0,0,.5);
  --e2:0 1px 0 rgba(255,255,255,.05) inset,0 14px 32px -18px rgba(0,0,0,.7);
  --e3:0 1px 0 rgba(255,255,255,.07) inset,0 22px 48px -18px rgba(0,0,0,.78);
  --e4:0 1px 0 rgba(255,255,255,.09) inset,0 32px 70px -20px rgba(0,0,0,.85);
}

/* ── 2. THE NUMBER TREATMENT ──────────────────────────────────────────────
   The single highest-value change in this file.

   Proportional figures have different widths per digit, so a price ticking
   from 0.00119 to 0.00121 visibly shifts every character after the change.
   Across a list of live positions that reads as instability. Tabular figures
   lock every digit to one width: the value changes, the layout does not.

   Slight negative tracking is the second half of it — mono and grotesk both
   set numerals loose at large sizes, which reads as receipt paper rather
   than instrument. */
.mono,[class*="pnl"],[class*="price"],[class*="val"],[class*="mc"],
.stat .v,.td-stat .v,.hero-val,.td-price,.pcard .js-pnl,
.num,.figure,td,.chip{
  font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1,"zero" 1;
}
.hero-val,.td-price,.stat .v{letter-spacing:-.022em}
.mono{letter-spacing:-.01em}

/* Labels are the counterweight: small, wide, quiet. The eye reads the figure
   first and the label only if it needs to. */
.stat .k,.td-stat .k,.hero-lbl,.lbl,.eyebrow{
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--faint);
  font-weight:600;
}

/* ── 3. LIVE VALUE FEEDBACK ───────────────────────────────────────────────
   The risk I am taking, and the reason: this app streams prices, but a number
   that silently changes is easy to miss. Real terminals flash the cell on
   change. One 500ms wash of signal colour behind the figure — no movement, no
   layout shift, gone before it becomes noise. Driven by mad-polish.js. */
@keyframes tick-up{0%{background-color:rgba(51,226,160,.22)}100%{background-color:transparent}}
@keyframes tick-dn{0%{background-color:rgba(255,84,112,.22)}100%{background-color:transparent}}
.tick-up{animation:tick-up 520ms var(--ease);border-radius:6px}
.tick-dn{animation:tick-dn 520ms var(--ease);border-radius:6px}

/* ── 4. PERFORMANCE ───────────────────────────────────────────────────────
   background-attachment:fixed forces a full-viewport repaint on every scroll
   frame in iOS Safari. On a long positions list that is the difference
   between 60fps and visible stutter. A painted gradient on a fixed pseudo
   element gets the identical look for free. */
body{
  background-attachment:scroll !important;
  background-image:none !important;
  background-color:var(--bg);
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overscroll-behavior-y:none;             /* no rubber-band under the sheet */
}
body::after{
  content:"";position:fixed;inset:0;z-index:-2;pointer-events:none;
  background:
    radial-gradient(900px 480px at 50% -12%,rgba(139,92,246,.16),transparent 62%),
    radial-gradient(620px 420px at 100% 18%,rgba(139,92,246,.055),transparent 60%),
    var(--bg);
}

/* Long lists: skip layout and paint for offscreen cards. */
.pcard,.gcard,.call-card,.tk-row{contain:layout style paint}

/* Promote only what actually animates. A blanket will-change costs memory. */
.sheet,.modal,.tabbar{will-change:transform}

/* ── 5. TOUCH ─────────────────────────────────────────────────────────────
   44px is the floor for a thumb. Feedback must land inside 100ms or the tap
   feels dropped — that perception of speed matters more than the real number. */
.btn,.tabbtn,.chip,.btn-icon,.mgr-chip,button,[role="button"]{
  min-height:44px;
  -webkit-tap-highlight-color:transparent;
  touch-action:manipulation;
  transition:transform var(--dur-1) var(--spring),
             background var(--dur-2) var(--ease),
             border-color var(--dur-2) var(--ease),
             box-shadow var(--dur-2) var(--ease);
}
.chip,.btn-icon,.mgr-chip{min-height:32px}
.btn:active,button:active,[role="button"]:active{transform:scale(.965)}
.tabbtn:active{transform:scale(.9)}
.pcard:active,.gcard:active,.call-card:active{transform:scale(.988);transition:transform var(--dur-1) var(--ease)}

/* ── 6. SURFACES ──────────────────────────────────────────────────────────
   One elevation vocabulary. Cards do not glow — glow is reserved for the
   primary action, so that the one purple thing on screen is the thing to tap. */
/* Consistent depth and one transition vocabulary. Deliberately does NOT set
   background or radius — those are already tuned per component in index.html
   and overriding them here would flatten distinctions that carry meaning. */
.pcard,.gcard,.stat,.td-stat,.mgr-panel,.panel,.master,.call-card{
  box-shadow:var(--e2);
  transition:border-color var(--dur-2) var(--ease),
             box-shadow var(--dur-2) var(--ease),
             transform var(--dur-1) var(--ease);
}
.hero{
  background:
    linear-gradient(135deg,rgba(139,92,246,.18),rgba(139,92,246,.04) 55%,transparent),
    var(--glass-base);
  border:1px solid var(--line2);
  border-radius:var(--r);
  box-shadow:var(--e3),0 10px 34px -20px var(--madGlow);
}
.hero-val{
  font-family:var(--head);font-weight:700;
  font-size:clamp(30px,9vw,var(--t-4xl));
  line-height:1;letter-spacing:-.03em;
  background:linear-gradient(180deg,var(--txt),var(--dim));
  -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;
}

/* ── 7. THE PRIMARY ACTION ────────────────────────────────────────────────
   Exactly one thing on any screen should look like the obvious next move. */
.btn-mad{
  background:linear-gradient(180deg,var(--mad2),var(--mad) 40%,var(--madDeep));
  border:1px solid rgba(180,154,255,.35);
  box-shadow:0 1px 0 rgba(255,255,255,.22) inset,0 10px 26px -10px var(--madGlow);
  font-weight:650;letter-spacing:.005em;
}
.btn-mad:active{box-shadow:0 1px 0 rgba(255,255,255,.22) inset,0 4px 14px -8px var(--madGlow)}
.btn-green{background:linear-gradient(180deg,#4ff0b4,var(--green) 45%,var(--greenDim));color:#04140d;font-weight:700}
.btn-red{background:linear-gradient(180deg,#ff7c92,var(--red) 45%,var(--redDim));color:#fff;font-weight:700}
.btn-ghost{background:rgba(255,255,255,.035);border:1px solid var(--line2);color:var(--txt)}

/* ── 8. TAB BAR ───────────────────────────────────────────────────────────
   The one place backdrop-filter earns its cost, because content scrolls
   under it continuously. Everywhere else it is a solid fill. */
.tabbar{
  background:rgba(6,5,12,.86);
  backdrop-filter:blur(20px) saturate(1.4);
  -webkit-backdrop-filter:blur(20px) saturate(1.4);
  border-top:1px solid var(--line);
  padding-bottom:max(env(safe-area-inset-bottom),8px);
}
.tabbtn{color:var(--faint);font-size:var(--t-micro);letter-spacing:.06em;font-weight:600}
.tabbtn.on{color:var(--mad2)}
.tabbtn.on::before{box-shadow:0 0 12px var(--mad2),0 0 4px var(--mad2)}

/* ── 9. EMPTY AND LOADING ─────────────────────────────────────────────────
   A blank panel reads as broken. A skeleton reads as working. */
@keyframes shimmer{100%{background-position:200% 0}}
.skeleton{
  background:linear-gradient(90deg,rgba(255,255,255,.035) 25%,rgba(255,255,255,.075) 50%,rgba(255,255,255,.035) 75%);
  background-size:200% 100%;
  animation:shimmer 1.4s linear infinite;
  border-radius:var(--r3);color:transparent !important;
}
/* The app's own empty state is already well built (Alf, a title, a line of
   copy). Not rebuilding it — just tightening the type and letting Alf sit
   a little brighter, because a greyed-out mascot reads as an error state. */
.empty .et{color:var(--txt);font-size:var(--t-base);letter-spacing:-.01em}
.empty .es{color:var(--dim);line-height:1.65}
.empty img{opacity:.58;filter:grayscale(.15) drop-shadow(0 10px 26px rgba(0,0,0,.5))}

/* ── 10. ENTRANCE ─────────────────────────────────────────────────────────
   Content arrives rather than appearing. Staggered by position so a list
   resolves as one gesture instead of six simultaneous pops. */
@keyframes rise{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
.tab-body>*{animation:rise var(--dur-3) var(--ease) both}
.tab-body>*:nth-child(1){animation-delay:0ms}
.tab-body>*:nth-child(2){animation-delay:28ms}
.tab-body>*:nth-child(3){animation-delay:56ms}
.tab-body>*:nth-child(4){animation-delay:84ms}
.tab-body>*:nth-child(n+5){animation-delay:104ms}

/* ── 11. CRAFT FLOOR ──────────────────────────────────────────────────────
   Not features. The things whose absence is felt without being noticed. */
:focus-visible{outline:2px solid var(--mad2);outline-offset:2px;border-radius:4px}
:focus:not(:focus-visible){outline:none}

::-webkit-scrollbar{width:0;height:0}
::selection{background:rgba(139,92,246,.35);color:#fff}

input,textarea,select{font-family:var(--disp);font-size:16px}  /* 16px = no iOS zoom */
.mgr-input:focus{border-color:var(--mad2);box-shadow:0 0 0 3px rgba(139,92,246,.16);outline:none}

h1,h2,h3{font-family:var(--head);letter-spacing:-.02em;line-height:1.15}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important;animation-iteration-count:1 !important;
    transition-duration:.01ms !important;scroll-behavior:auto !important;
  }
}

/* Larger phones and tablets: the content column stops growing, because a
   32-character price does not benefit from a 900px line length. */
@media (min-width:560px){
  .wrap,.tab-body{max-width:520px;margin-inline:auto}
}

/* ══════════════════════════════════════════════════════════════════════════
   HERO + CHROME CORRECTIONS
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Crown: removed ───────────────────────────────────────────────────────
   It sat over the eyebrow label and added a third accent colour to a card
   that already carries purple and green. The portfolio value is the hero;
   nothing should compete with it. */
.hero::after{content:none !important}

/* ── Portfolio value: readable ────────────────────────────────────────────
   The gold itself was not the problem — measured against this card it is
   8.94:1, comfortably above the 4.5:1 floor. What made it hard to read was
   everything stacked on top: a glow text-shadow softening every edge, plus
   a gradient-to-transparent fill inherited from the base rule that faded the
   figure toward its own baseline. Two soft treatments on the same numerals
   at 36px is one too many.

   White at 16.07:1 with a hard drop shadow instead of a glow. Gold is kept
   for the decimals, so MAD's accent is still in the figure without carrying
   the legibility. */
.hero-val{
  font-family:var(--mono) !important;
  color:#fff !important;
  -webkit-text-fill-color:#fff !important;
  background:none !important;
  text-shadow:0 1px 2px rgba(0,0,0,.55) !important;
  font-weight:700 !important;
  letter-spacing:-.025em !important;
}
.hero-val .dec{color:var(--gold) !important;-webkit-text-fill-color:var(--gold) !important;opacity:.92}
.hero-lbl{color:rgba(255,255,255,.62) !important}
.hero-sub,.hero-foot{color:rgba(255,255,255,.72) !important}

/* ── SOL / RH switch: legible at 10px ─────────────────────────────────────
   The inactive label used --ink4, which is not defined in this palette, so it
   inherited whatever was nearest — near-invisible in the screenshot. Both
   states now get explicit colour, and the type goes up a point with less
   tracking, because 10.5px at 800 weight with .07em spacing is past the size
   where extra letter-spacing helps rather than hurts. */
.chainsw{background:rgba(0,0,0,.55) !important;border:1px solid rgba(255,255,255,.12) !important}
.chainsw button{
  color:rgba(255,255,255,.55) !important;
  font-size:11.5px !important;
  letter-spacing:.04em !important;
  font-weight:700 !important;
  height:30px !important;
  min-height:0 !important;
}
.chainsw button .lbl{color:inherit !important}
/* White on #8b5cf6 measures 4.23:1 — under the floor for 11.5px text, which
   is not "large" by WCAG even at 700 weight. Starting the gradient a step
   deeper at #7c3aed puts it at 5.70:1 while looking essentially identical. */
.chainsw button.on{
  background:linear-gradient(180deg,#7c3aed,#6d28d9 60%,#5b21b6) !important;
  color:#fff !important;
  box-shadow:0 1px 0 rgba(255,255,255,.26) inset,0 4px 12px -4px var(--madGlow) !important;
}
.chainsw button.on .lbl{color:#fff !important;text-shadow:0 1px 2px rgba(0,0,0,.45)}
.chainsw button:not(.on):active{color:rgba(255,255,255,.85) !important}
.chainsw button:not(.on) .cmk{opacity:.7 !important;filter:none !important}

/* ── Hero chart ───────────────────────────────────────────────────────────
   Rendered at true pixel size by mad-polish.js, so no preserveAspectRatio
   stretching — that was distorting the stroke and squashing the end dot
   into an ellipse. */
.hero-chart{height:68px;margin:6px 0 10px;position:relative;overflow:visible}
.hero-chart svg{display:block;width:100%;height:100%}
.hero-chart .hc-empty{
  display:flex;align-items:center;justify-content:center;height:100%;
  font:600 10px/1 var(--disp),system-ui;letter-spacing:.16em;text-transform:uppercase;
  color:rgba(255,255,255,.32);
}


/* ── RH mode: the selected chain pill ─────────────────────────────────────
   The rule above hard-codes the active pill purple with !important, which
   also wins inside rh-mode — so selecting Robinhood lit a purple pill inside
   a green-tinted topbar. Same treatment, green, at the same contrast bar:
   white on #0d8049 is 5.00:1, so the label clears 4.5:1 at 11.5px. */
body.rh-mode .chainsw button.on{
  background:linear-gradient(180deg,#0d8049,#0b6b3d 60%,#095732) !important;
  box-shadow:0 1px 0 rgba(255,255,255,.24) inset,
             0 4px 12px -4px rgba(22,168,98,.5) !important;
}

/* ── Press states ─────────────────────────────────────────────────────────
   Everything tappable now answers with a haptic and a click, so the surfaces
   that had no visual press left the other two feeling detached from the UI.
   These were the gaps. */

/* The portfolio hero is the largest tap target in the app and opens Wallet,
   and it was the one card with no press response at all. */
.hero{transition:transform .13s var(--spring-soft),box-shadow .2s;cursor:pointer}
.hero:active{transform:scale(.988);box-shadow:0 1px 0 rgba(255,255,255,.07) inset,
  0 10px 26px -18px rgba(139,92,246,.5),0 4px 14px -8px rgba(0,0,0,.6)}

/* A switch that only moves once the round trip lands reads as a dropped tap.
   The knob dips immediately; the travel still waits on the real state. */
.sw:active{transform:scale(.94)}
.sw:active::after{width:24px}
.sw::after{transition:transform .22s var(--spring-soft),width .12s ease,background .22s}

.chip{transition:transform .12s}
.chip:active{transform:scale(.94)}
.seg button{transition:transform .1s,background .16s,border-color .16s,color .16s}
.seg button:active{transform:scale(.97)}
.addr:active,.rh-fold>summary:active{transform:scale(.995)}

/* Long-press on a control should not raise the OS text-selection bubble —
   it fires constantly on a touch UI built out of divs with onclick. Inputs
   and anything explicitly marked selectable keep normal behaviour. */
.btn,.tabbtn,.chip,.sw,.more-card,.rh-tap,.rh-tog,.rh-chips button,
.rh-sells button,.chainsw button,.hero,.pl-row,.seg button,.btn-icon{
  -webkit-user-select:none;user-select:none;-webkit-touch-callout:none;
}
