/*
    The site's own look, on top of Radzen's dark base.

    The palette, the type pairing and the motion below are not invented here: they come from the
    ui-ux-pro-max skill, asked for a "playful cartoon arcade game hub, dark, vivid, hex, jelly" -
    style "Modern Dark", Fredoka over Nunito, red/blue/green on deep navy. They were chosen
    because the game already looks like that: a dark board with red attack tiles, blue effect
    tiles and green healing tiles. The site should read as the same product, not as an admin
    panel that happens to mention it.

    Two rules here are load-bearing rather than decorative, and predate all the styling.
    #blazor-error-ui carries `display: none` - without it the error bar sits on every page as
    unstyled text, which reads as a permanent unhandled exception and cost an evening once.
    .loading-progress is what turns the bare <svg> in index.html into a spinner rather than two
    stray circles.
*/

:root {
    /* Design tokens. Everything below refers to these and never to a literal colour. */
    --hop-bg: #0f172a;
    --hop-surface: #1f1829;
    --hop-primary: #dc2626;
    --hop-secondary: #2563eb;
    --hop-accent: #22c55e;
    --hop-text: #ffffff;
    --hop-muted: rgba(255, 255, 255, 0.62);
    --hop-faint: rgba(255, 255, 255, 0.38);
    --hop-border: rgba(255, 255, 255, 0.08);
    --hop-border-strong: rgba(255, 255, 255, 0.16);

    --hop-radius: 16px;
    --hop-radius-sm: 10px;

    /* The skill's easing. Slightly past the target and back - the same overshoot the game's own
       jumps have, which is part of why the two feel related. */
    --hop-ease: cubic-bezier(0.16, 1, 0.3, 1);

    --hop-font-display: "Fredoka", system-ui, -apple-system, "Segoe UI", sans-serif;
    --hop-font-body: "Nunito", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

    /* Radzen's own variables, pointed at ours. Overriding these rather than fighting each
       component's rules is what keeps this file short. */
    --rz-body-background-color: var(--hop-bg);
    --rz-base-background-color: var(--hop-bg);
    --rz-text-color: var(--hop-text);
    --rz-primary: var(--hop-primary);
    --rz-secondary: var(--hop-secondary);
    --rz-success: var(--hop-accent);
    --rz-border-color: var(--hop-border);
    --rz-body-font-family: var(--hop-font-body);
}

html, body {
    margin: 0;
    font-family: var(--hop-font-body);
    color: var(--hop-text);
    background: var(--hop-bg);
    -webkit-font-smoothing: antialiased;
}

/*
    Two soft lights behind everything, fixed to the viewport.

    This is the whole of the "cinematic" part of the style: without them a dark page is just a
    grey rectangle. Fixed rather than scrolling so the page moves over the light instead of
    dragging it along, and pointer-events: none so they can never swallow a click.
*/
body::before {
    content: "";
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 0;
    background:
        radial-gradient(60rem 40rem at 12% -10%, rgba(37, 99, 235, 0.22), transparent 60%),
        radial-gradient(50rem 36rem at 92% 8%, rgba(220, 38, 38, 0.18), transparent 62%);
}

body > * {
    position: relative;
    z-index: 1;
}

/* ---------- Typography ---------- */

h1, h2, h3, h4, h5, h6, .hop-display {
    font-family: var(--hop-font-display);
    letter-spacing: -0.01em;
}

/* ---------- The header ---------- */

.hop-hero {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1.25rem;
    padding: 2rem 0 0.5rem;
}

.hop-wordmark {
    margin: 0;
    font-family: var(--hop-font-display);
    font-weight: 700;
    font-size: clamp(2.1rem, 6vw, 3.2rem);
    line-height: 1;
    letter-spacing: -0.02em;

    /* The board's own two attack colours, in the name itself. */
    background: linear-gradient(96deg, #ffffff 12%, #ff6b6b 55%, #7aa2ff 92%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.hop-tagline {
    margin: 0.35rem 0 0;
    color: var(--hop-muted);
    font-size: 0.95rem;
}

.hop-versions {
    margin: 0.5rem 0 0;
    color: var(--hop-faint);
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
}

.hop-wordmark-link { text-decoration: none; display: inline-block; }

.hop-hero-right {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.75rem;
}

/* ---------- Navigation ---------- */

.hop-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    padding: 0.3rem;
    border: 1px solid var(--hop-border);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.035);
    align-self: flex-start;
}

.hop-nav-item {
    padding: 0.45rem 1rem;
    border-radius: 999px;
    color: var(--hop-muted);
    font-family: var(--hop-font-display);
    font-weight: 500;
    font-size: 0.9rem;
    text-decoration: none;
    cursor: pointer;
    transition: color 0.16s var(--hop-ease), background-color 0.16s var(--hop-ease);
}

.hop-nav-item:hover { color: var(--hop-text); background: rgba(255, 255, 255, 0.05); }

/*
    NavLink adds `active` to the current page. Without a rule for it the navigation is four
    identical words and says nothing about where you are.
*/
.hop-nav-item.active {
    color: #06210f;
    background: linear-gradient(180deg, #34d76b, var(--hop-accent));
}

/* The way on to the full table. A card that truncates has to say so and where to go. */
.hop-more {
    color: #7aa2ff;
    font-family: var(--hop-font-display);
    font-size: 0.86rem;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    display: inline-block;
    margin-top: 0.6rem;
    transition: color 0.16s var(--hop-ease);
}

.hop-more:hover { color: #a9c4ff; }

.hop-card-head .hop-more { margin-top: 0; }

/* ---------- Cards ---------- */

.hop-card {
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.055), rgba(255, 255, 255, 0.022));
    border: 1px solid var(--hop-border);
    border-radius: var(--hop-radius);
    padding: 1.25rem 1.35rem 1.35rem;
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    box-shadow: 0 18px 40px -28px rgba(0, 0, 0, 0.9);
}

.hop-card-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.6rem;
    margin-bottom: 0.9rem;
}

.hop-card-title {
    margin: 0;
    font-family: var(--hop-font-display);
    font-size: 1.12rem;
    font-weight: 600;
}

.hop-card-note {
    margin: 0;
    color: var(--hop-faint);
    font-size: 0.82rem;
    font-variant-numeric: tabular-nums;
}

/* ---------- Download buttons ---------- */

.hop-downloads {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

.hop-download {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.7rem 1.15rem;
    border-radius: 999px;
    border: 1px solid transparent;
    font-family: var(--hop-font-display);
    font-weight: 600;
    font-size: 0.95rem;
    text-decoration: none;
    color: #06210f;
    background: linear-gradient(180deg, #34d76b, var(--hop-accent));
    box-shadow: 0 10px 26px -14px rgba(34, 197, 94, 0.9);
    cursor: pointer;
    transition: transform 0.18s var(--hop-ease), box-shadow 0.18s var(--hop-ease);
}

.hop-download:hover {
    transform: translateY(-2px);
    box-shadow: 0 16px 32px -14px rgba(34, 197, 94, 0.95);
}

/* The press, matching the 0.97 scale the skill specifies and the game already uses. */
.hop-download:active {
    transform: scale(0.97);
}

.hop-download svg {
    width: 1.05em;
    height: 1.05em;
}

/* ---------- Name field ---------- */

.hop-name {
    display: flex;
    align-items: center;
    gap: 0.55rem;
}

.hop-name label {
    color: var(--hop-faint);
    font-size: 0.82rem;
}

.hop-name input {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid var(--hop-border-strong);
    border-radius: var(--hop-radius-sm);
    color: var(--hop-text);
    font-family: var(--hop-font-body);
    font-size: 0.92rem;
    padding: 0.5rem 0.75rem;
    width: 9.5rem;
}

.hop-name input::placeholder { color: var(--hop-faint); }

/* ---------- Tables ---------- */

/*
    A table wider than the screen scrolls inside this, and the page never does.

    At 375px the first version overflowed the body: three numeric columns at 5.5rem plus a rank
    column plus a name is wider than a phone, so the whole page scrolled sideways and the tagline
    and the right-hand column were simply cut off. The page must never scroll horizontally - only
    the thing that is too wide may.
*/
.hop-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    margin: 0 -0.35rem;
    padding: 0 0.35rem;
}

.hop-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.92rem;
}

.hop-table th {
    text-align: left;
    padding: 0.5rem 0.7rem;
    color: var(--hop-faint);
    font-family: var(--hop-font-display);
    font-weight: 500;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    border-bottom: 1px solid var(--hop-border);
    white-space: nowrap;
}

.hop-table td {
    padding: 0.6rem 0.7rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.045);
}

.hop-table tbody tr:last-child td { border-bottom: 0; }

.hop-table tbody tr { transition: background-color 0.16s var(--hop-ease); }
.hop-table tbody tr:hover { background: rgba(255, 255, 255, 0.035); }

/*
    Numbers right, and their headings right with them.

    `.hop-table th` sets text-align: left and outranks a bare `.hop-num` on specificity, so the
    first version had every count right-aligned under a left-aligned heading - the heading sat at
    one end of an empty column and its numbers at the other. Narrow columns too: a table with
    four columns across 1040px spreads them so far apart that the eye cannot pair a figure with
    its label.
*/
.hop-num,
.hop-table th.hop-num { text-align: right; font-variant-numeric: tabular-nums; }
.hop-table th.hop-num, .hop-table td.hop-num { width: 5.5rem; }

/* The first column takes the slack instead, so the numbers stay in a readable block. */
.hop-table th:nth-child(2), .hop-table td:nth-child(2) { width: auto; }

.hop-rank { font-family: var(--hop-font-display); font-weight: 600; color: var(--hop-faint); }
.hop-rank-1 { color: #ffd45e; }
.hop-rank-2 { color: #cbd5e1; }
.hop-rank-3 { color: #e6a86b; }

/* ---------- Small pieces ---------- */

.hop-pill {
    display: inline-block;
    padding: 0.16rem 0.6rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    font-family: var(--hop-font-display);
}

.hop-pill-waiting { background: rgba(37, 99, 235, 0.18); color: #93b4ff; }
.hop-pill-playing { background: rgba(34, 197, 94, 0.18); color: #7ee2a4; }

.hop-empty {
    padding: 1.1rem 0.2rem;
    color: var(--hop-faint);
    font-size: 0.9rem;
}

.hop-live {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--hop-faint);
    font-size: 0.82rem;
}

.hop-live i {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--hop-accent);
    box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.6);
    animation: hop-pulse 2.4s infinite;
}

@keyframes hop-pulse {
    70% { box-shadow: 0 0 0 7px rgba(34, 197, 94, 0); }
    100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); }
}

/* ---------- Motion ---------- */

/*
    The stagger the skill recommends, in CSS rather than by pulling in an animation library:
    this page is downloaded by somebody on a phone, and a library for four fading cards is not
    a trade worth making.
*/
.hop-rise { animation: hop-rise 0.42s var(--hop-ease) both; }

.hop-rise:nth-of-type(2) { animation-delay: 0.06s; }
.hop-rise:nth-of-type(3) { animation-delay: 0.12s; }
.hop-rise:nth-of-type(4) { animation-delay: 0.18s; }

@keyframes hop-rise {
    from { opacity: 0; transform: translateY(16px) scale(0.98); }
    to   { opacity: 1; transform: none; }
}

/* On the skill's checklist, and the right thing regardless. */
/*
    The measured page.

    A grid of figures rather than another table. These are six unrelated numbers read one at a time
    to answer one question each, and a table invites reading down a column that has no meaning -
    the rows have nothing in common but the page they are on.
*/
.hop-figures {
    display: grid;
    /* auto-fit, so three fit on a laptop, two on a tablet and one on a phone, with no breakpoints
       naming any of those widths. */
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 0.9rem;
}

.hop-figure {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 1rem 1.1rem;
    border: 1px solid var(--hop-border);
    border-radius: var(--hop-radius-sm);
    background: rgba(255, 255, 255, 0.025);
}

.hop-figure-value {
    font-family: var(--hop-font-display);
    font-size: 1.9rem;
    line-height: 1.1;
    /* Tabular, so a column of figures does not shuffle sideways as the numbers change under a
       poll. */
    font-variant-numeric: tabular-nums;
}

.hop-figure-label {
    font-size: 0.94rem;
    font-weight: 700;
}

.hop-figure-note {
    font-size: 0.8rem;
    color: var(--hop-faint);
}

/* A figure that argues for a change. The page decides from the number, so it is never decorative. */
.hop-figure-loud {
    border-color: rgba(220, 38, 38, 0.55);
    background: rgba(220, 38, 38, 0.09);
}

.hop-figure-loud .hop-figure-value { color: var(--hop-primary); }

/* Two bars on one baseline: what people picked, and what the bots picked. */
.hop-bars {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 5rem;
}

.hop-bar {
    height: 6px;
    border-radius: 3px;
    /* Never vanishes entirely: a tile nobody picked should read as a thin line, not as a missing
       row. */
    min-width: 2px;
    transition: width 0.4s var(--hop-ease);
}

.hop-bar-people { background: var(--hop-secondary); }

.hop-bar-bots { background: var(--hop-border-strong); }

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

/* Keyboard focus has to be visible; a hover state alone leaves keyboard users lost. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible {
    outline: 2px solid #7aa2ff;
    outline-offset: 2px;
    border-radius: var(--hop-radius-sm);
}

/* ---------- Layout ---------- */

/*
    On a wide screen the content sits on the left and the chat on the right.

    It was the last card in a single column, so on a desktop you scrolled the whole page past the
    tables to reach something there was room for the entire time. Below the breakpoint the grid
    collapses to one column and the chat falls underneath, which is the right order on a phone:
    the standings are what people came for and the chat is not.
*/
.hop-split {
    display: grid;
    gap: 1.15rem;
    align-items: start;
}

.hop-split-main,
.hop-split-side {
    display: flex;
    flex-direction: column;
    gap: 1.15rem;
    min-width: 0;
}

@media (min-width: 1000px) {
    /* A fixed share for the chat rather than a fraction of the remainder: a chat that grows with
       the window ends up mostly empty, and the tables are what should take the space. */
    .hop-split { grid-template-columns: minmax(0, 1fr) 23rem; }

    /* Sticky, so it stays beside whichever table has been scrolled to. */
    .hop-split-side { position: sticky; top: 1.15rem; }
}


/*
    Flex items refuse to shrink below their own min-content unless told otherwise, and one
    stubborn child then sets the width of the whole page. That is what happened at 375px: the
    chat component has a minimum width of its own, the page grew to fit it, and every card
    above stretched with it - so the tagline and the right-hand column were cut off by a
    component three screens further down. `min-width: 0` is the fix; `overflow-x: clip` on the
    chat's own card is the belt to the braces.
*/
.hop-page > *,
.hop-hero > * { min-width: 0; }

.hop-card-chat { overflow-x: clip; }

.hop-card-chat .rz-chat,
.hop-card-chat [class^="rz-chat"] {
    min-width: 0;
    max-width: 100%;
}

@media (max-width: 560px) {
    /* The participant list is what needs the width, and on a phone it is the least of it. */
    .hop-card-chat .rz-chat-users { display: none; }
}

.hop-page {
    max-width: 1040px;
    margin: 0 auto;
    padding: 0 1.25rem 3rem;
    display: flex;
    flex-direction: column;
    gap: 1.15rem;
}

@media (max-width: 560px) {
    .hop-hero { padding-top: 1.4rem; }

    /* The header stacks, so the right-hand column aligns with the left instead of the edge. */
    .hop-hero-right { align-items: stretch; width: 100%; }
    .hop-nav { align-self: stretch; justify-content: center; }
    .hop-nav-item { flex: 1 1 auto; text-align: center; padding-inline: 0.7rem; }
    .hop-card { padding: 1rem 1rem 1.1rem; }
    .hop-downloads { width: 100%; }
    .hop-download { flex: 1 1 auto; justify-content: center; }

    /* Below this width the leaderboard's later columns cost more room than they earn. */
    .hop-hide-narrow { display: none; }

    /* The widest cell on the page is a full timestamp. Two lines beat a sideways scroll. */
    .hop-when { white-space: normal; word-spacing: 100vw; }

    .hop-table { font-size: 0.86rem; }
    .hop-table th, .hop-table td { padding-inline: 0.45rem; }
}

/* ---------- Blazor's own elements ---------- */

#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}

.blazor-error-boundary {
    background: #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

.loading-progress {
    position: relative;
    display: block;
    width: 8rem;
    height: 8rem;
    margin: 24vh auto 1rem auto;
}

.loading-progress circle {
    fill: none;
    stroke: rgba(255, 255, 255, 0.1);
    stroke-width: 0.5rem;
    transform-origin: 50% 50%;
    transform: rotate(-90deg);
}

.loading-progress circle:last-child {
    stroke: var(--hop-accent);
    stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
    transition: stroke-dasharray 0.05s ease-in-out;
}

.loading-progress-text {
    position: absolute;
    text-align: center;
    font-family: var(--hop-font-display);
    font-weight: 600;
    color: var(--hop-muted);
    inset: calc(24vh + 3.25rem) 0 auto 0.2rem;
}

.loading-progress-text:after {
    content: var(--blazor-load-percentage-text, "Loading");
}

/* A worn title, beside a name (JB-217).
   Small, quiet and never the same weight as the name: a title is something somebody chose or won,
   and a title that shouted would make the leaderboard a list of titles with names attached. */
.hop-title {
    margin-left: 0.5rem;
    font-size: 0.75em;
    font-weight: 500;
    letter-spacing: 0.02em;
    opacity: 0.72;
}
