/* ==========================================================================
   site.css - layout and components

   The card vocabulary mirrors the launcher exactly: a card is a bordered body
   with a title strip sitting proud of it, an optional recessed column-header
   strip, and rows separated by a hairline fainter than the card outline. Three
   surfaces a few points apart is all it takes to read as structure rather than
   as a flat wall of text.
   ========================================================================== */

/* ================================================================= shell == */

.shell {
    width: 100%;
    max-width: var(--shell);
    margin: 0 auto;
    padding: 0 20px;
}

/* ================================================================ topbar == */

.topbar {
    position: sticky;
    top: 0;
    z-index: 40;
    background: var(--c-card);
    border-bottom: 1px solid var(--c-border);
}

.topbar-inner {
    display: flex;
    align-items: center;
    gap: 18px;
    height: 56px;
}

.brand {
    display: flex;
    align-items: center;
    gap: 10px;
    color: var(--c-text);
    font-size: var(--fs-body);
    font-weight: 700;
    letter-spacing: var(--ls-label);
    text-transform: uppercase;
    white-space: nowrap;
}
.brand:hover { color: var(--c-text); }
.brand img { width: 26px; height: 26px; }

/* --- primary nav -------------------------------------------------------- */

.nav {
    display: flex;
    align-items: stretch;
    gap: 2px;
    margin-left: 6px;
    flex: 1;
}

.nav-link {
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 0 14px;
    height: 56px;
    color: var(--c-muted);
    font-size: var(--fs-meta);
    letter-spacing: var(--ls-micro);
    text-transform: uppercase;
    background: none;
    border: 0;
    border-bottom: 2px solid transparent;
    white-space: nowrap;
}
.nav-link:hover { color: var(--c-text); border-bottom-color: var(--c-border); }
.nav-link[aria-current="page"],
.nav-link.is-active { color: var(--c-accent); border-bottom-color: var(--c-accent); }

.nav-group { position: relative; display: flex; }

.nav-menu {
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 232px;
    padding: 5px 0;
    background: var(--c-head);
    border: 1px solid var(--c-border);
    border-radius: var(--r-ctl);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
    display: none;
}
/* Kept open on hover AND on focus-within, so the menu is reachable by keyboard
   and not only by pointer. */
.nav-group:hover .nav-menu,
.nav-group:focus-within .nav-menu { display: block; }

.nav-menu a {
    display: block;
    padding: 7px 14px;
    color: var(--c-muted);
    font-size: var(--fs-meta);
    white-space: nowrap;
}
.nav-menu a:hover { background: var(--c-hover); color: var(--c-text); }

.caret { font-size: 0.6rem; opacity: 0.7; }

/* --- live status pill --------------------------------------------------- */

.pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 5px 11px;
    background: var(--c-bg);
    border: 1px solid var(--c-border);
    border-radius: var(--r-ctl);
    font-size: var(--fs-micro);
    letter-spacing: var(--ls-micro);
    text-transform: uppercase;
    color: var(--c-muted);
    white-space: nowrap;
}
.pill b { color: var(--c-text); font-weight: 600; letter-spacing: 0; }

.dot {
    width: 7px; height: 7px;
    border-radius: 50%;
    background: var(--c-muted);
    flex: none;
}
.dot.is-online  { background: var(--c-online); }
.dot.is-offline { background: var(--c-offline); }

/* --- mobile toggle ------------------------------------------------------ */

.nav-toggle {
    display: none;
    margin-left: auto;
    padding: 7px 11px;
    background: var(--c-bg);
    border: 1px solid var(--c-border);
    border-radius: var(--r-ctl);
    color: var(--c-text);
    font-size: var(--fs-body);
    line-height: 1;
}

/* ================================================================== hero == */

/* No bottom border. The gradient below already lands on exactly --c-bg at 100%,
   so the artwork dissolves into the page the way the launcher's hero does. A
   hairline there would draw the seam the fade exists to hide.

   With the copy removed there is no content to give the band a height, so it is
   held at the artwork's own 1774x441 ratio - the same thing the launcher does in
   ApplyHeroHeight, and for the same reason: at the art's aspect there is nothing
   for the crop to eat, so widening the window scales the banner instead of
   slicing into it. The clamps stop it collapsing on phones or dominating an
   ultrawide. */
.hero {
    position: relative;
    overflow: hidden;
    width: 100%;
    /* Height tracks the artwork's 4:1 ratio off the viewport, clamped exactly
       the way the launcher clamps it in ApplyHeroHeight (200..460).

       Deliberately NOT "aspect-ratio + max-height": when max-height clamps a box
       that carries an aspect-ratio, the used WIDTH shrinks to preserve the
       ratio. On a wide monitor that capped the banner at 460px tall and roughly
       1849px wide, so it stopped short of the right edge instead of spanning it.
       Setting the height directly leaves the width free to fill. */
    height: clamp(260px, 24.9vw, 460px);
}

.hero-art {
    position: absolute;
    inset: 0;
    background-image: var(--hero-src);
    background-size: cover;
    /* Centred on both axes, matching the launcher's UniformToFill. The art is
       4:1 and this band is close to it, so the crop stays small at any width. */
    background-position: center center;
    /* Drifts a few pixels, like the launcher's banner. Transform only, so this
       never touches layout. */
    animation: hero-drift 28s ease-in-out infinite alternate;
}

@keyframes hero-drift {
    from { transform: translate3d(-4px, 0, 0) scale(1.03); }
    to   { transform: translate3d( 4px, 0, 0) scale(1.03); }
}

/* Fades the artwork into the page below rather than cutting it off.

   The scrim is roughly half its former weight, so more of the art comes
   through. The final stop stays at full opacity on purpose: it has to land on
   exactly --c-bg or the seamless join reappears as a visible band. The extra
   middle stop keeps that last push gradual instead of turning it into a hard
   edge across the bottom of the image. */
.hero::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
        to bottom,
        rgba(27, 27, 29, 0.10) 0%,
        rgba(27, 27, 29, 0.22) 45%,
        rgba(27, 27, 29, 0.55) 78%,
        rgba(27, 27, 29, 1.00) 100%);
}

/* Pinned to the bottom-left of the band. Keeping .shell on this element means
   the buttons line up with the page gutter below rather than floating at an
   arbitrary inset. */
.hero-body {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
    padding-bottom: 26px;
}

.hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

/* ============================================================== buttons == */

.btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 11px 22px;
    border: 1px solid transparent;
    border-radius: var(--r-ctl);
    font-size: var(--fs-body);
    font-weight: 600;
    letter-spacing: var(--ls-micro);
    text-transform: uppercase;
    transition: background 0.12s ease, border-color 0.12s ease;
}

.btn-icon { width: 18px; height: 18px; flex: none; }

.btn-cta {
    background: var(--c-cta);
    color: var(--c-on-cta);
    /* Sits on artwork, so it needs to lift off it rather than blend in. */
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
}
.btn-cta:hover  { background: var(--c-cta-hi); color: var(--c-on-cta); }
.btn-cta:active { background: var(--c-cta-lo); }

/* Discord: smoked glass at rest so it reads as clearly secondary and never
   competes with the orange call to action, then resolves to Discord's own
   blurple on hover. A flat outlined button looked weak over the artwork - there
   is nothing behind it for a hairline border to sit against. */
.btn-discord {
    background: rgba(18, 18, 20, 0.66);
    border-color: rgba(255, 255, 255, 0.16);
    color: var(--c-text);
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
    -webkit-backdrop-filter: blur(7px);
    backdrop-filter: blur(7px);
}
.btn-discord:hover {
    background: #5865F2;
    border-color: #5865F2;
    color: #fff;
}
.btn-discord:active { background: #4752C4; border-color: #4752C4; }

/* =========================================================== stat strip == */

.strip {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1px;
    margin: 22px 0;
    background: var(--c-border);       /* shows through the gaps as hairlines */
    border: 1px solid var(--c-border);
    border-radius: var(--r-card);
    overflow: hidden;
}

.strip div {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 14px 18px;
    background: var(--c-card);
}

.strip .label {
    color: var(--c-muted);
    font-size: var(--fs-micro);
    letter-spacing: var(--ls-micro);
    text-transform: uppercase;
}
.strip .value {
    color: var(--c-text);
    font-size: 1.3rem;
    font-weight: 600;
}

/* ================================================================= cards == */

.card {
    background: var(--c-card);
    border: 1px solid var(--c-border);
    border-radius: var(--r-card);
    overflow: hidden;             /* keeps row hovers inside the rounded corner */
}

.card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 11px 14px;
    background: var(--c-head);
    border-bottom: 1px solid var(--c-border);
}

.card-title {
    font-size: var(--fs-meta);
    font-weight: 600;
    letter-spacing: var(--ls-label);
    text-transform: uppercase;
    color: var(--c-text);
}

.card-meta {
    font-size: var(--fs-micro);
    letter-spacing: var(--ls-micro);
    text-transform: uppercase;
    color: var(--c-muted);
    white-space: nowrap;
}

/* Column-header strip. Horizontal padding MUST match .row, or the labels stop
   lining up with the values underneath them. */
.thead {
    padding: 6px 14px;
    background: var(--c-subhead);
    border-bottom: 1px solid var(--c-border);
    font-size: var(--fs-micro);
    letter-spacing: var(--ls-micro);
    text-transform: uppercase;
    color: var(--c-muted);
}

.row {
    padding: 10px 14px;
    border-bottom: 1px solid var(--c-rule);
}
.row:last-child { border-bottom: 0; }
a.row, .row.is-link { display: block; color: inherit; }
a.row:hover, .row.is-link:hover { background: var(--c-hover); color: inherit; }
/* The headline picks up the accent on hover so a linked row reads as clickable
   without underlining every entry in the feed. */
a.row:hover .entry-title { color: var(--c-accent); }

.card-foot {
    padding: 10px 14px;
    background: var(--c-subhead);
    border-top: 1px solid var(--c-border);
    text-align: center;
    font-size: var(--fs-micro);
    letter-spacing: var(--ls-micro);
    text-transform: uppercase;
}

.empty {
    padding: 26px 14px;
    text-align: center;
    color: var(--c-muted);
    font-size: var(--fs-meta);
}

/* Spinner shown while a card has nothing real to display yet. */
.loading { padding: 30px 14px; text-align: center; }
.spinner {
    display: inline-block;
    width: 18px; height: 18px;
    border: 2px solid var(--c-active);
    border-top-color: var(--c-accent);
    border-radius: 50%;
    animation: spin 0.75s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ============================================================ home grid == */

.home-cards {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 14px;
    margin-bottom: 22px;
}

.home-card { display: block; color: inherit; }
.home-card:hover { color: inherit; border-color: var(--c-active); }
.home-card .body { padding: 14px; }
.home-card .value {
    margin-top: 2px;
    font-size: 1.15rem;
    font-weight: 600;
    color: var(--c-text);
}
.home-card .sub {
    margin-top: 4px;
    font-size: var(--fs-meta);
    color: var(--c-muted);
}

.grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 348px;
    gap: 14px;
    align-items: start;
    padding-bottom: 40px;
}

.rail { display: grid; gap: 14px; }

/* ============================================================== entries == */

/* A news or patch-notes entry, same shape as the launcher's feed rows. */
.entry-meta {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 7px;
}

.chip {
    padding: 2px 8px;
    min-width: 62px;
    text-align: center;
    background: var(--c-active);
    border-radius: var(--r-chip);
    color: var(--c-accent);
    font-size: var(--fs-micro);
    letter-spacing: var(--ls-micro);
    text-transform: uppercase;
}

.entry-date {
    margin-left: auto;
    color: var(--c-muted);
    font-size: var(--fs-micro);
    letter-spacing: var(--ls-micro);
}

.entry-title {
    font-size: var(--fs-lead);
    font-weight: 600;
    color: var(--c-text);
}

.entry-body {
    margin-top: 5px;
    color: var(--c-muted);
    font-size: var(--fs-meta);
    line-height: 1.6;
    /* Two lines, then ellipsis. Keeps a long post from dominating the column. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ================================================================== kills == */

/* Fixed column template shared by the header strip and every row, so the
   columns actually line up. Sizing each row independently is what makes a
   kill feed look ragged - a "2m" and a "48m" timestamp push everything to a
   different position on every line. */
.kill-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 20px minmax(0, 1fr) 54px;
    align-items: center;
    gap: 8px;
}

.kill-row .sep  { color: var(--c-active); text-align: center; }
.kill-row .when { color: var(--c-muted); font-size: var(--fs-meta); text-align: right; }
.kill-row span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.killer { font-weight: 600; }

.nation-aresden { color: var(--c-aresden); }
.nation-elvine  { color: var(--c-elvine); }
.nation-other, .nation-neutral { color: var(--c-neutral); }

/* ================================================================ footer == */

.footer {
    border-top: 1px solid var(--c-border);
    background: var(--c-card);
}
.footer-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 20px 0;
    color: var(--c-muted);
    font-size: var(--fs-meta);
}
.footer-links { display: flex; flex-wrap: wrap; gap: 16px; }

/* ================================================================ error == */

.error-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 62vh;
    padding: 40px 20px;
}
.error-card { max-width: 520px; width: 100%; text-align: center; }
.error-card .body { padding: 34px 26px; }
.error-code {
    font-size: 3.4rem;
    font-weight: 700;
    color: var(--c-accent);
    line-height: 1;
}
.error-card h1 { margin: 12px 0 8px; font-size: 1.25rem; }
.error-card p { color: var(--c-muted); font-size: var(--fs-body); }
.error-card .btn { margin-top: 22px; }

/* =========================================================== responsive == */

@media (max-width: 1080px) {
    .grid { grid-template-columns: minmax(0, 1fr); }
    .rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 860px) {
    .nav-toggle { display: block; }

    /* Collapsed nav becomes a full-width stacked panel below the bar. */
    .nav {
        display: none;
        position: absolute;
        top: 56px;
        left: 0;
        right: 0;
        flex-direction: column;
        gap: 0;
        margin: 0;
        padding: 6px 0;
        background: var(--c-head);
        border-bottom: 1px solid var(--c-border);
        box-shadow: 0 12px 28px rgba(0, 0, 0, 0.5);
    }
    .nav.is-open { display: flex; }

    .nav-link { height: auto; padding: 11px 20px; border-bottom: 0; }
    .nav-group { flex-direction: column; }

    /* Submenus are always expanded on mobile - hover does not exist, and a
       tap-to-open accordion is more machinery than four links justify. */
    .nav-menu {
        display: block;
        position: static;
        min-width: 0;
        border: 0;
        border-radius: 0;
        box-shadow: none;
        background: transparent;
        padding: 0 0 6px;
    }
    .nav-menu a { padding: 8px 20px 8px 34px; }

    .topbar-inner { position: relative; }
    .topbar .pill { margin-left: auto; }

    .rail { grid-template-columns: minmax(0, 1fr); }
    .home-cards { grid-template-columns: minmax(0, 1fr); }
    .hero-body { padding: 44px 0 34px; }
}

@media (max-width: 560px) {
    .shell { padding: 0 14px; }
    .strip { grid-template-columns: minmax(0, 1fr); }
    .btn { width: 100%; justify-content: center; }
    .brand span { display: none; }          /* logo only, keeps the bar usable */
    .topbar .pill .label { display: none; } /* dot + count still visible */
}

/* The account chip and the sign-in modal are CHROME, not page content:
   they appear on every page, including index.html and error.html, which
   deliberately do not load pages.css. Living there is what left the modal
   unstyled - and therefore rendered as a plain block at the foot of the
   document instead of a centred overlay. */

/* ============================================================== account == */

.acct { display: flex; align-items: center; gap: 10px; margin-left: 12px; }

/* A header control, not a page call-to-action. .btn's own 11px/22px and 14px
   type are sized for the hero; at that scale in a 56px bar it dwarfs the nav
   beside it. Matches the launcher's compact SIGN IN chip. */
.acct-signin {
    padding: 5px 13px;
    font-size: var(--fs-micro);
    letter-spacing: var(--ls-micro);
    box-shadow: none;
}

.acct-chip {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 10px 4px 4px;
    border: 1px solid transparent;
    border-radius: var(--r-ctl);
    color: var(--c-text);
}
.acct-chip:hover { background: var(--c-hover); border-color: var(--c-border); color: var(--c-text); }

.acct-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px; height: 24px;
    overflow: hidden;
    border-radius: 50%;
    background: var(--c-active);
    color: var(--c-muted);
    font-size: var(--fs-micro);
    font-weight: 600;
    flex: none;
}
.acct-avatar img { width: 100%; height: 100%; object-fit: cover; }

.acct-name { font-size: var(--fs-meta); white-space: nowrap; }

.acct-out {
    padding: 0;
    background: none;
    border: 0;
    color: var(--c-muted);
    font-size: var(--fs-micro);
    letter-spacing: var(--ls-micro);
    text-transform: uppercase;
    white-space: nowrap;
}
.acct-out:hover { color: var(--c-text); }

/* --- header coin balance -------------------------------------------------- */
/*
   Shares .pill with the world-status widget so the two read as one row of
   header chips rather than two unrelated controls. It is an <a> to the cash
   shop, so it gets a hover state the status pill does not need.
*/

.coin-pill {
    gap: 6px;
    text-decoration: none;
    color: var(--c-text);
}

.coin-pill:hover { background: var(--c-hover); border-color: var(--c-border); }

.coin-mark {
    width: 14px;
    height: 14px;
    /* Sits on the text baseline rather than the line box, so the number and
       the coin do not drift apart at different font sizes. */
    display: block;
    flex: 0 0 auto;
}

/* hidden is overridden by .pill's display, so restore it explicitly. */
.coin-pill[hidden] { display: none; }

/* --- floor the footer ----------------------------------------------------- */
/*
   On a short page - error, sign-in, a stats table with three rows - the footer
   would otherwise sit directly under the content with dead space beneath it,
   which reads as a broken page rather than a short one.

   Flex column with only <main> allowed to grow, rather than a grid with fixed
   rows: the pages do not share one child list. index.html has a <section
   class="hero"> between the header and main, most pages open with a skip link,
   and profile.html appends <dialog> elements to the body at runtime. Positional
   grid rows would mis-assign all of those; "main takes the slack" holds
   whatever the siblings happen to be.
*/

body {
    min-height: 100vh;
    /* dvh accounts for mobile browser chrome that appears and disappears;
       the vh line above stays as the fallback for anything that lacks it. */
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
}

body > main { flex: 1 0 auto; }

/* Everything else keeps its natural height. Without this the hero - which has
   a clamped height - would be a candidate for shrinking when content is tall. */
body > .topbar,
body > .hero,
body > .footer { flex-shrink: 0; }

/* --- cash shop promo -----------------------------------------------------
   Lives in site.css, NOT pages.css: index.html loads only theme.css and
   site.css, so a promo styled in pages.css renders unstyled on the one page
   it exists for. Same trap the account modal hit.
-------------------------------------------------------------------------- */

.promo {
    display: flex;
    align-items: center;
    gap: 16px;
    /* 18px horizontal matches .strip, the other full-width bar in this column -
       not .card-head's 14px, which belongs to the narrower stacked panels. */
    padding: 16px 18px;
    /* Every other block on the home page carries its own vertical separation
       (.home-cards margin-bottom, .strip margin) but .grid below has none, so
       without this the promo sits flush against the News panel with a zero gap
       while every other pair is 22px apart. That missing gap - not the padding -
       is what made this panel look wrong. */
    margin-bottom: 22px;
}

.promo-mark { width: 40px; height: 40px; flex: 0 0 auto; }

.promo-body { flex: 1 1 auto; min-width: 0; }
.promo-title { font-size: var(--fs-lead); font-weight: 600; }
.promo-sub { color: var(--c-muted); font-size: var(--fs-meta); margin-top: 2px; }
.promo-action { flex: 0 0 auto; }

@media (max-width: 560px) {
    .promo { flex-wrap: wrap; }
    .promo-action, .promo-action .btn { width: 100%; }
}

/* ---------------------------------------------------------------------------
   [hidden] must actually hide.

   The browser's default stylesheet gives [hidden] display:none, but that is the
   weakest possible rule: ANY author rule that sets display beats it. So an
   element written as <div class="thing" hidden> stays visible the moment
   .thing gets display:flex, and the markup reads as though it is hidden.

   This bit us on the sign-in page: the "Signing in to the launcher" banner is
   marked hidden and only revealed for a real launcher redirect, but
   .auth-banner{display:flex} meant every ordinary visitor saw it and was told
   they were signing in to something they had not opened.

   One rule here rather than a :not([hidden]) guard on each component, because
   the failure is silent and the next component to hit it will not be noticed
   either. !important is warranted: hidden is an assertion about whether the
   element exists for the user, not a style suggestion.
   --------------------------------------------------------------------------- */
[hidden] { display: none !important; }
