@import url("font.css");

/* ==========================================================================
   ChaCha Bo — on-chain call scanner
   Palette, type and motion are all deliberate. Notes inline where a choice
   would otherwise look arbitrary.
   ========================================================================== */

:root {
    /* Surfaces. Never #000: pure black clips on OLED and makes hairlines
       impossible to tune. */
    --bg-0: #04070d;
    --bg-1: #080d16;
    --bg-2: #0a1220;
    --bg-3: #0d1726;

    --hairline: rgba(255, 255, 255, .055);
    --hairline-soft: rgba(255, 255, 255, .03);
    --hairline-hi: rgba(34, 211, 238, .16);

    /* Cyan is data and state. Gold is only clickable things and conviction
       >= 75. Nothing else in the UI gets a hue. */
    --cyan: #22d3ee;
    --gold: #f5b81a;

    /* Never pure white on near-black: the halation is what makes dark UI
       feel cheap. */
    --text-0: #e6ecf5;
    --text-1: #8b99b0;
    --text-2: #4d5a6e;

    /* Conviction tiers mirror scanner.py::score_label so the site and the
       Telegram alerts can never disagree about the same token. */
    --tier-weak: #5a6b82;
    --tier-spec: #4d8fa5;
    --tier-mid: #22d3ee;
    --tier-high: #f5b81a;
    --tier-none: #3a4354;

    /* Returns. Lime and ember rather than stock #22c55e/#ef4444, which is a
       generator default, and deliberately distinct from cyan and gold so a
       green score can never be confused with a green gain. */
    --up: #9be564;
    --down: #ff5c3a;
    --flat: #6b7a90;

    /* Verified brand marks. Base moved off #0052FF in its 2025 rebrand.
       Robinhood Chain is Robin Neon, not the legacy Robinhood green.
       The -t variants are lightened for text, since #0000FF on #0a1220 is
       unreadable. */
    --sol: #9945ff;
    --base: #0000ff;
    --bsc: #f0b90b;
    --eth: #627eea;
    --rbh: #ccff00;

    /* Token symbols include CJK; platform fallbacks prevent tofu. */
    --cjk: "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB",
        "Noto Sans CJK SC", sans-serif;
    --font-display: "Archivo", "Helvetica Neue", Arial, var(--cjk);
    --font-ui: "Archivo", -apple-system, "Segoe UI", var(--cjk);
    --font-data: "IBM Plex Mono", ui-monospace, "Cascadia Code", var(--cjk);

    /* No bare `ease` anywhere. */
    --e-out: cubic-bezier(0, 0, .2, 1);
    --t-fast: 120ms;

    /* One variable governs the whole column system, so the header and every
       row can never drift apart. */
    --col-token: 250px;
    --col-score: 96px;
    --col-now: 108px;
    --col-peak: 100px;
    --col-calls: 88px;
    --col-liq: 106px;
    --col-mc: 106px;
    --col-entry: 120px;
    --col-social: 90px;
    --col-flow: 106px;
    --col-age: 74px;
    --col-act: 78px;

    --shell: min(1560px, 100vw - 48px);
}

*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    background: var(--bg-0);
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    background: var(--bg-0);
    color: var(--text-0);
    font-family: var(--font-ui);
    font-size: 15px;
    line-height: 1.55;
    font-synthesis-weight: none;
    -webkit-font-smoothing: antialiased;
    overflow-x: clip;
}

a {
    color: inherit;
    text-decoration: none;
}

::selection {
    background: rgba(34, 211, 238, .26);
    color: #fff;
}

:focus-visible {
    outline: 1px solid var(--cyan);
    outline-offset: 2px;
}

.num {
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums slashed-zero;
    font-feature-settings: "tnum" 1, "zero" 1;
    font-variant-ligatures: none;
    letter-spacing: -.005em;
}

/* Missing metric. An em dash, never N/A, never 0, never a shimmer: those rows
   have no market cap and never will, and an eternal skeleton reads as a hung
   request. */
.nil {
    color: var(--text-2);
    opacity: .55;
}

.shell {
    width: var(--shell);
    margin-inline: auto;
}

/* -------------------------------------------------------------------- bar - */

.bar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin: 14px 0 12px;
}

.feed__controls {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

/* Desktop sorts by column header; the select only shows once the thead is gone. */
.sortsel {
    display: none;
    align-items: center;
    gap: 6px;
    margin-left: auto;
    font-family: var(--font-data);
    font-size: 11px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text-2);
}

.sortsel select {
    height: 28px;
    padding: 0 6px;
    border: 1px solid var(--hairline);
    border-radius: 2px;
    background: var(--bg-2);
    color: var(--text-0);
    font: inherit;
}

/* Outline chips, never filled pills: filled reads as toy. */
.chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 28px;
    padding: 0 10px;
    border: 1px solid var(--hairline);
    border-radius: 2px;
    background: transparent;
    color: var(--text-1);
    font-family: var(--font-data);
    font-size: 11px;
    letter-spacing: .08em;
    text-transform: uppercase;
    cursor: pointer;
    transition: color var(--t-fast) var(--e-out), border-color var(--t-fast) var(--e-out);
}

.chip:hover {
    color: var(--text-0);
    border-color: var(--hairline-hi);
}

.chip[aria-pressed="true"] {
    color: var(--bg-0);
    background: var(--cyan);
    border-color: var(--cyan);
}

.chip svg {
    width: 13px;
    height: 13px;
}

.chip__n {
    color: var(--text-2);
}

.chip[aria-pressed="true"] .chip__n {
    color: rgba(4, 7, 13, .6);
}

.tablewrap {
    border: 1px solid var(--hairline);
    border-radius: 3px;
    background: var(--bg-2);
    /* The table is its own scroll viewport. `overflow: auto` already makes this
       element the scroll container, so a sticky thead resolves against it
       rather than the page — without a height cap the header would scroll away
       with the document. Capping it also makes content-visibility pay off. */
    max-height: min(78vh, 920px);
    overflow: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: #1b2740 transparent;
}

.tablewrap::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

.tablewrap::-webkit-scrollbar-track {
    background: var(--bg-1);
}

.tablewrap::-webkit-scrollbar-thumb {
    background: #1b2740;
    border: 2px solid var(--bg-1);
    border-radius: 6px;
}

.tablewrap::-webkit-scrollbar-thumb:hover {
    background: #29405f;
}

table.calls {
    width: 100%;
    /* Fixed layout means columns are sized from the first row instead of
       measuring all 50 rows by 12 columns. */
    table-layout: fixed;
    border-collapse: separate;
    border-spacing: 0;
    min-width: 1280px;
}

table.calls thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    height: 36px;
    padding: 0 12px;
    background: var(--bg-1);
    color: var(--text-2);
    font-family: var(--font-display);
    font-weight: 600;
    font-stretch: 88%;
    font-size: 11px;
    letter-spacing: .08em;
    text-transform: uppercase;
    text-align: right;
    white-space: nowrap;
    /* Borders do not paint reliably on sticky cells; inset shadow does. */
    box-shadow: inset 0 -1px 0 var(--hairline);
}

table.calls thead th:first-child {
    /* Must stick on both axes. Sticking only the body cells leaves the header
       sliding under the frozen token column when scrolled sideways. */
    left: 0;
    z-index: 3;
    text-align: left;
}

th.sortable {
    cursor: pointer;
    user-select: none;
    transition: color var(--t-fast) var(--e-out);
}

th.sortable:hover {
    color: var(--text-0);
}

/* Sortable columns must look sortable, or a reader hunting the top score
   scans the whole column by eye. */
th.sortable::after {
    content: "";
    display: inline-block;
    width: 0;
    height: 0;
    margin-left: 6px;
    vertical-align: 2px;
    border-left: 3.5px solid transparent;
    border-right: 3.5px solid transparent;
    border-top: 4px solid currentColor;
    opacity: .3;
}

th.sortable[aria-sort]::after {
    opacity: 1;
}

th.sortable[aria-sort] {
    color: var(--cyan);
}

table.calls tbody td {
    height: 44px;
    padding: 0 12px;
    border-bottom: 1px solid var(--hairline-soft);
    color: var(--text-0);
    font-size: 13px;
    text-align: right;
    white-space: nowrap;
    overflow: hidden;
    /* background-color, not the `background` shorthand: the shorthand would
       reset background-image and kill the score and mention micro-bars. */
    background-color: var(--bg-2);
    transition: background-color var(--t-fast) linear, border-color var(--t-fast) linear;
}

table.calls tbody tr {
    /* Cheap offscreen skipping without a virtual scroller. */
    content-visibility: auto;
    contain-intrinsic-size: auto 44px;
}

table.calls tbody td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    text-align: left;
    /* Must be opaque or rows smear through while scrolling sideways. */
    background-color: var(--bg-2);
    /* 2px chain rail, coloured per row. Free — one inset shadow. */
    box-shadow: inset 2px 0 0 var(--chain, transparent);
}

/* Hover changes background and border-color together. That simultaneity is
   what makes a row read as lit rather than merely tinted. */
table.calls tbody tr:hover td {
    background-color: var(--bg-3);
    border-bottom-color: var(--hairline-hi);
}

table.calls tbody tr:hover td:first-child {
    box-shadow: inset 2px 0 0 var(--cyan);
}

/* ------------------------------------------------------------ token cell - */

.tok {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

.tok__icon {
    flex: none;
    width: 18px;
    height: 18px;
}

.tok__body {
    min-width: 0;
}

.tok__sym {
    display: block;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 14px;
    letter-spacing: -.005em;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tok__meta {
    display: flex;
    align-items: center;
    gap: 6px;
    line-height: 1.2;
}

.tok__addr {
    font-family: var(--font-data);
    font-size: 11.5px;
    font-variant-numeric: slashed-zero;
    color: var(--text-2);
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    transition: color var(--t-fast) var(--e-out);
}

.tok__addr:hover {
    color: var(--cyan);
}

.tok__addr.copied {
    color: var(--up);
}

/* 9px outline origin badge. Five of seven Solana calls are pump.fun launches,
   which is worth surfacing rather than leaving as a meaningless address tail. */
.badge {
    flex: none;
    padding: 0 3px;
    border: 1px solid currentColor;
    border-radius: 2px;
    font-family: var(--font-data);
    font-size: 9px;
    line-height: 13px;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-2);
}

.badge--pump {
    color: #7d6ba8;
}

.badge--fresh {
    color: var(--down);
}

.tok__risk,
.tok__call {
    display: none;
}

/* ------------------------------------------------------------ score cell - */

/* The number itself carries the ramp, and the 2px bar under it is painted as a
   background gradient — no extra element, no layout, 50 rows for free. */
table.calls tbody td.c-score {
    background-image: linear-gradient(var(--tier), var(--tier));
    background-repeat: no-repeat;
    background-origin: content-box;
    background-clip: content-box;
    /* Right-anchored against a fixed 44px track so the bar reads as attached
       to the right-aligned number. A percentage width would span the whole
       column and read as a stray rule. */
    background-position: right bottom 9px;
    background-size: calc(var(--pct, 0) * .44px) 2px;
}

.score {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 15px;
    font-variant-numeric: tabular-nums;
    color: var(--tier);
}

.score__of {
    color: var(--text-2);
    font-weight: 400;
    font-size: 11px;
}

tr[data-tier="high"] td.c-score .score {
    text-shadow: 0 0 10px rgba(245, 184, 26, .35);
}

/* ------------------------------------------------------------ return cell - */

.ret {
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    font-weight: 500;
}

.ret--up {
    color: var(--up);
}

.ret--down {
    color: var(--down);
}

.ret--flat {
    color: var(--flat);
}

/* Sign and arrow as well as hue, so the direction survives colour-vision
   deficiency and greyscale. */
.ret__ar {
    font-size: 9px;
    opacity: .8;
}

/* ------------------------------------------------------------- calls cell - */

/* Micro bar, not a sparkline: there is only a count here, no time series, and
   bars encode magnitude where sparklines encode change. */
table.calls tbody td.c-calls {
    background-image: linear-gradient(rgba(34, 211, 238, .34), rgba(34, 211, 238, .34));
    background-repeat: no-repeat;
    background-origin: content-box;
    background-clip: content-box;
    background-position: right bottom 9px;
    background-size: calc(var(--pct, 0) * .3px) 2px;
}

/* --------------------------------------------------------------- money ---- */

.money__u {
    color: var(--text-2);
    opacity: .8;
}

.price sub {
    font-size: .7em;
    vertical-align: -.25em;
    color: var(--text-1);
}

/* -------------------------------------------------------------- age cell -- */

.age {
    font-family: var(--font-data);
    font-size: 12.5px;
    color: var(--text-1);
    font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------ action cell - */

.acts {
    display: flex;
    justify-content: flex-end;
    gap: 4px;
}

.iconbtn {
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    border: 1px solid transparent;
    border-radius: 2px;
    background: transparent;
    color: var(--text-2);
    cursor: pointer;
    transition: color var(--t-fast) var(--e-out), border-color var(--t-fast) var(--e-out);
}

.iconbtn:hover {
    color: var(--cyan);
    border-color: var(--hairline-hi);
}

.iconbtn svg {
    width: 14px;
    height: 14px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.25;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* ------------------------------------------------------------- pagination - */

.pager {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 20px 0 0;
    flex-wrap: wrap;
}

.pager__info {
    font-family: var(--font-data);
    font-size: 11.5px;
    color: var(--text-2);
    letter-spacing: .04em;
}

.pager__spacer {
    margin-left: auto;
}

.pager button[disabled] {
    opacity: .35;
    cursor: default;
}

.pager button[disabled]:hover {
    color: var(--text-1);
    border-color: var(--hairline);
}

/* Busy state: opacity only, no shimmer sweeping across 50 rows. */
.tablewrap[data-busy="1"] {
    opacity: .55;
    transition: opacity var(--t-fast) linear;
}

.notice {
    margin: 18px 0 0;
    padding: 12px 14px;
    border: 1px solid rgba(245, 184, 26, .28);
    border-radius: 3px;
    background: rgba(245, 184, 26, .05);
    color: var(--gold);
    font-family: var(--font-data);
    font-size: 12px;
}


/* ------------------------------------------------------------ responsive -- */

@media (max-width: 1180px) {
    :root {
        --shell: min(1560px, 100vw - 32px);
    }
}

@media (max-width: 700px) {
    :root {
        --shell: calc(100vw - 16px);
    }

    .bar {
        align-items: center;
        flex-wrap: nowrap;
        margin: 8px 0;
    }

    .feed__controls {
        flex: 1;
        flex-wrap: nowrap;
        gap: 5px;
        min-width: 0;
        overflow-x: auto;
        scrollbar-width: none;
    }

    .feed__controls::-webkit-scrollbar {
        display: none;
    }

    .chip {
        height: 26px;
        padding: 0 7px;
        font-size: 10px;
    }

    .chip__n {
        display: none;
    }

    .sortsel {
        display: inline-flex;
    }

    .sortsel > span {
        display: none;
    }

    .tablewrap {
        max-height: none;
        overflow: visible;
        border-radius: 2px;
    }

    table.calls,
    table.calls tbody {
        display: block;
        min-width: 0;
    }

    table.calls colgroup,
    table.calls thead,
    table.calls tbody td:nth-child(n + 6):nth-child(-n + 11) {
        display: none;
    }

    table.calls tbody tr {
        display: grid;
        grid-template-columns: repeat(4, minmax(0, 1fr));
        gap: 7px 8px;
        padding: 9px 10px;
        border-bottom: 1px solid var(--hairline-soft);
        background-color: var(--bg-2);
        box-shadow: inset 2px 0 0 var(--chain, transparent);
        contain-intrinsic-size: auto 88px;
    }

    table.calls tbody tr:hover {
        background-color: var(--bg-3);
    }

    table.calls tbody td,
    table.calls tbody td:first-child,
    table.calls tbody tr:hover td,
    table.calls tbody tr:hover td:first-child {
        position: static;
        height: auto;
        padding: 0;
        border: 0;
        background-color: transparent;
        box-shadow: none;
        text-align: left;
    }

    table.calls tbody td:first-child { grid-area: 1 / 1 / 2 / 4; }
    table.calls tbody td:nth-child(2) { grid-area: 2 / 1; }
    table.calls tbody td:nth-child(3) { grid-area: 2 / 2; }
    table.calls tbody td:nth-child(4) { grid-area: 2 / 3; }
    table.calls tbody td:nth-child(5) { grid-area: 2 / 4; }

    table.calls tbody td:nth-child(12) {
        grid-area: 1 / 4;
        justify-self: end;
        align-self: center;
    }

    table.calls tbody td:nth-child(n + 2):nth-child(-n + 5)::before {
        display: block;
        margin-bottom: 1px;
        font-family: var(--font-display);
        font-weight: 600;
        font-size: 9px;
        letter-spacing: .08em;
        text-transform: uppercase;
        color: var(--text-2);
    }

    table.calls tbody td:nth-child(2)::before { content: "Score"; }
    table.calls tbody td:nth-child(3)::before { content: "Now"; }
    table.calls tbody td:nth-child(4)::before { content: "Peak"; }
    table.calls tbody td:nth-child(5)::before { content: "Mentions"; }

    .tok {
        gap: 7px;
    }

    .tok__sym {
        font-size: 13px;
    }

    .tok__meta {
        gap: 4px;
    }

    .tok__addr {
        display: none;
    }

    .tok__risk,
    .tok__call {
        display: inline;
        color: var(--text-2);
        font-family: var(--font-data);
        font-size: 9px;
    }

    .badge {
        font-size: 8px;
        line-height: 11px;
    }

    table.calls tbody td.c-score,
    table.calls tbody td.c-calls {
        background-position: left bottom;
    }

    .pager {
        gap: 8px;
        margin-top: 10px;
        padding-bottom: 10px;
    }
}
