/* Self-hosted fonts (docs/perf-plans/03): variable woff2, latin + latin-ext
   subsets, immutable-cached under ?v= like every other asset. No third-party
   origin on the critical path. */
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:400 700;font-display:swap;src:url('/fonts/jbmono-var-latin.woff2?v=6cc86e08f0986daf') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:400 700;font-display:swap;src:url('/fonts/jbmono-var-latin-ext.woff2?v=6cc86e08f0986daf') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'Space Grotesk';font-style:normal;font-weight:400 700;font-display:swap;src:url('/fonts/sgrotesk-var-latin.woff2?v=6cc86e08f0986daf') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Space Grotesk';font-style:normal;font-weight:400 700;font-display:swap;src:url('/fonts/sgrotesk-var-latin-ext.woff2?v=6cc86e08f0986daf') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
:root{--purple:#9C82F2;--purple-soft:#B9A6F7;--bg-0:#0B0913;--bg-1:#121020;--text-hi:#F3F1FA;--text-mid:#ABA3C4;--text-low:#6F6890}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
/* overflow-x:clip (not hidden) contains horizontal overflow WITHOUT forcing
   overflow-y to compute to auto — which would make <body> the scroll container
   and break the sticky header (it would stick to body's scrollport, not the
   viewport). clip keeps the viewport as the scroller so position:sticky works. */
body{background:var(--bg-0);color:#d4dae3;font-family:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;font-size:14px;min-height:100vh;padding:0 0 48px;overflow-x:clip}
/* NO overflow property here — ever. overflow-x:hidden forced overflow-y to
   auto (clipping the search dropdown + mobile menu), and its replacement
   overflow-x:clip did the same thing on WebKit < 17.4 (every iOS browser),
   which clipped BOTH axes and made the burger menu invisible on iPhones.
   Horizontal containment is handled at the children instead: .site-nav
   scrolls itself, .player-search-dropdown is max-width-capped below. */
.site-header{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:nowrap;padding:16px 24px;border-bottom:1px solid #283041;background:#12161f;position:sticky;top:0;z-index:200;min-height:58px;box-shadow:0 2px 10px rgba(0,0,0,.35)}
.site-title{color:var(--purple);font-size:1.2rem;letter-spacing:.08em;white-space:nowrap;text-decoration:none;flex-shrink:0}
.site-nav{display:flex;gap:4px;flex-wrap:nowrap;flex-shrink:1;min-width:0;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}
.site-nav::-webkit-scrollbar{display:none}
.site-nav a{position:relative;overflow:hidden;color:#8b93a3;text-decoration:none;font-size:.8rem;letter-spacing:.05em;padding:5px 10px;border-radius:4px;border:1px solid transparent;white-space:nowrap}
.site-nav a::after{content:"";position:absolute;bottom:0;left:0;width:100%;height:2px;background:var(--purple);transform:scaleX(0);transform-origin:left;transition:transform .2s ease}
.site-nav a:hover{color:#d4dae3;background:#1b2130}
.site-nav a:hover::after,.site-nav a.active::after{transform:scaleX(1)}
.site-nav a.active{color:var(--purple);border-color:#283041;background:#0b0e14}
.rating-toggle{background:none;border:1px solid #283041;border-radius:4px;color:#8b93a3;
  cursor:pointer;font-family:'Space Grotesk',ui-sans-serif,system-ui,sans-serif;
  font-size:.72rem;letter-spacing:.08em;padding:4px 10px;white-space:nowrap;flex-shrink:0}
.rating-toggle:hover{color:#d4dae3;background:#1b2130}
.rating-toggle.on{color:#0b0e14;background:#eab308;border-color:#eab308;font-weight:700}
.rating-banner{background:#2a2412;border-bottom:1px solid #eab308;color:#eab308;
  font-family:'JetBrains Mono',ui-monospace,monospace;font-size:.72rem;
  letter-spacing:.05em;padding:6px 24px;text-align:center}
.rd{color:#6b7484;font-size:.75em;margin-left:2px}
.col-elo.provisional,.provisional{opacity:.55}
.hamburger{display:none;background:none;border:none;cursor:pointer;padding:4px 8px;color:#8b93a3;font-size:1.4rem;line-height:1}
.mobile-menu{display:none;flex-direction:column;background:#12161f;border-top:1px solid #283041;position:absolute;top:100%;left:0;right:0;z-index:100;box-shadow:0 14px 28px rgba(0,0,0,.5)}
.mobile-menu a{display:block;padding:13px 20px;color:#d4dae3;text-decoration:none;font-size:.85rem;letter-spacing:.1em;border-bottom:1px solid #283041}
.hamburger,.mobile-menu a,.site-nav a,.rating-toggle,.seg-btn{-webkit-tap-highlight-color:transparent}
.mobile-menu a:hover,.mobile-menu a.active{color:var(--purple);background:#181d29}
.mobile-menu.open{display:flex}
/* First menu row = the phone's auth surface (skin.js renders /api/me into the
   server-rendered #mmenu-auth slot; the desktop .steam-widget is hidden ≤880).
   Signed out: a sign-in row (inherits the .mobile-menu a row look, accented).
   Signed in: avatar + name (→ player page) with a bordered SIGN OUT chip. */
.mobile-menu a.mm-signin{color:var(--purple-soft)}
.mm-user{display:flex;align-items:center;gap:12px;padding:9px 20px;border-bottom:1px solid #283041;background:#0e1219}
.mm-user .auth-avatar{width:30px;height:30px;border-radius:4px;flex-shrink:0}
.mm-user .mm-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#f3f1fa;font-size:.85rem;letter-spacing:.05em;font-family:'Space Grotesk',ui-sans-serif,system-ui,-apple-system,sans-serif}
.mobile-menu .mm-user a{display:inline-block;padding:0;border-bottom:none}
.mobile-menu .mm-user a.mm-out{flex-shrink:0;color:#8b93a3;font-size:.68rem;letter-spacing:.08em;border:1px solid #283041;border-radius:4px;padding:8px 12px}
.mobile-menu .mm-user a.mm-out:hover{color:#d4dae3;background:#1b2130}
.player-search-wrap{position:relative;flex-shrink:0}
.player-search-input{background:#1b2130;border:1px solid #283041;border-radius:4px;color:#d4dae3;cursor:text;font-family:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8rem;letter-spacing:.04em;padding:5px 10px;outline:none;width:170px}
.player-search-input:focus{border-color:var(--purple)}
.player-search-input::placeholder{color:#4a5160}
/* max-width cap (not header overflow clipping) keeps long player names from
   pushing the dropdown past the viewport edge; items ellipsize. */
.player-search-dropdown{display:none;position:absolute;top:calc(100% + 4px);right:0;min-width:100%;max-width:calc(100vw - 24px);background:#1b2130;border:1px solid #283041;border-radius:4px;z-index:101;max-height:264px;overflow-y:auto}
.player-search-dropdown.open{display:block}
.player-search-item{padding:6px 10px;cursor:pointer;color:#d4dae3;font-size:.8rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.player-search-item:hover{background:#181d29}
/* Mobile header (≤880, where the nav collapses to the burger): reflow to two
   rows — row 1 is [logo … S3 ☰] (title's auto right-margin pushes the actions
   to the edge), row 2 is a full-width search. This stops the placeholder from
   truncating in a cramped 96px input and reads like a modern app top-bar.
   Touch sizing: ☰ and S3 get ≥44px hit areas; the search input is 16px so
   iOS doesn't auto-zoom the page when it's focused. */
@media(max-width:880px){
  .hamburger{display:flex;align-items:center;justify-content:center;min-width:44px;min-height:44px;margin:-7px -8px -7px 0}
  .site-nav{display:none !important}
  .site-header{flex-wrap:wrap;column-gap:10px;row-gap:10px;padding:10px 14px}
  .site-title{font-size:1.05rem;margin-right:auto}
  .rating-toggle{padding:8px 12px}
  .player-search-wrap{order:5;flex-basis:100%}
  .player-search-input{width:100%;font-size:16px;padding:8px 12px}
}
@media(max-width:400px){.site-title{font-size:.95rem}}
/* Admin sub-nav — the mod-only second strip under the header on /admin* and
   /submit (built by skin.js once /api/me resolves a moderator/admin role). */
.admin-subnav{display:flex;gap:2px;align-items:center;flex-wrap:wrap;padding:7px 24px;background:#0e1219;border-bottom:1px solid #283041;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}
.admin-subnav::-webkit-scrollbar{display:none}
.admin-subnav a{color:#8b93a3;text-decoration:none;font-size:.72rem;letter-spacing:.06em;padding:4px 10px;border-radius:4px;white-space:nowrap;font-family:'Space Grotesk',ui-sans-serif,system-ui,-apple-system,sans-serif}
.admin-subnav a:hover{color:#d4dae3;background:#1b2130}
.admin-subnav a.active{color:var(--purple);background:#13301e}
.subnav-badge{display:none;font-size:.66rem;font-weight:700;color:#0b0e14;background:#eab308;border-radius:9px;padding:0 6px;margin-left:6px;vertical-align:middle}
.subnav-badge.on{display:inline-block}
@media(max-width:640px){.admin-subnav{padding:6px 14px}}
/* shared data-table base — one consistent look for every standings/results table.
   Structural props only (!important to win over legacy per-page rules without
   removing them); each table keeps its own column alignment + cell colours. */
table.dtable{width:100%;border-collapse:collapse}
table.dtable th{color:#8b93a3 !important;font-weight:normal !important;font-size:.71rem !important;letter-spacing:.04em !important;padding:6px 9px !important;border-bottom:1px solid #1b2130 !important;white-space:nowrap}
table.dtable td{font-size:.81rem !important;padding:6px 9px !important;border-bottom:1px solid #1b2130 !important}
table.dtable tbody tr:hover{background:#181d29 !important}
table.dtable tbody tr:last-child td{border-bottom:none !important}
/*  Responsive tables — a two-stage collapse so a list is never
    hamburger-nav-narrow yet still desktop-column-dense:
      • .col-tertiary  drops at the nav breakpoint (≤880, the hamburger point) —
        the deep/rare columns (per-set damage, top-3%, event counts, …).
      • .col-secondary drops on phones (≤600), leaving the identity column plus
        the one headline stat (ELO / W%).  */
@media(max-width:880px){
  .col-tertiary{display:none !important}
}
@media(max-width:600px){
  .col-secondary{display:none !important}
  table.dtable td,table.dtable th{padding:5px 6px !important;font-size:.75rem !important}
  /* phones: reclaim panel gutter width, grow the seg toggles to thumb size */
  .panel{padding:14px 10px}
  .seg-btn{padding:8px 13px}
}
/*  Touch tooltip popup  */
.touch-tip{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:#1b2130;border:1px solid var(--purple);border-radius:6px;color:#d4dae3;font-size:.8rem;font-family:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;padding:8px 14px;z-index:9999;max-width:90vw;text-align:center;pointer-events:none;opacity:0;transition:opacity .15s}
.touch-tip.show{opacity:1}
/*  Collapsible section header  */
.coll-hdr{display:flex;align-items:center;justify-content:space-between;cursor:pointer;padding:8px 0;user-select:none;-webkit-user-select:none}
.coll-body{overflow:hidden}
/*  Chevron (collapsible/expandable indicator) — rotates 90° when an ancestor is .open.
    Unifies the old .caret (player) / .car (tournaments) / .coll-car (event).  */
.caret{color:#6b7484;font-size:.8em;display:inline-block;transition:transform .12s}
.coll-hdr .caret{margin-left:8px}
.set-row.open .caret,.series.open .caret,.coll-hdr.open .caret{transform:rotate(90deg);color:var(--purple)}
/*  Inline "loading…" text placeholder (.spinner is the rotating one)  */
/* Spinner discipline (docs/perf-plans/03): loading feedback only becomes
   visible after 150 ms — fast responses paint content with no flash. */
.loading{color:#8b93a3;padding:20px;text-align:center;opacity:0;animation:ds-appear 0s .15s forwards}
/*  Gold/silver skin name tints (applied by skin.js; declared here so no
    runtime <style> injection reflows the page)  */
.skin-gold{color:#ffd24a !important;font-weight:bold;-webkit-text-stroke:.6px #6e4e00;paint-order:stroke fill;text-shadow:0 0 9px rgba(255,200,60,.6)}
.skin-silver{color:#f4f8fc !important;font-weight:bold;-webkit-text-stroke:.6px #59616e;paint-order:stroke fill;text-shadow:0 0 10px rgba(213,226,242,.85)}
/* form controls inherit the body face unless they opt out */
button,input,select,textarea{font-family:inherit}
/* display layer: Space Grotesk for the reading/label text; data stays mono */
.site-title{font-family:'Space Grotesk',ui-sans-serif,system-ui,-apple-system,sans-serif;font-weight:600;letter-spacing:.06em}
.site-nav a,.mobile-menu a{font-family:'Space Grotesk',ui-sans-serif,system-ui,-apple-system,sans-serif}
.panel-title,.combat-panel-title,.mu-title,h1,h2,h3,thead th,table.dtable th{font-family:'Space Grotesk',ui-sans-serif,system-ui,-apple-system,sans-serif !important}
/*  Shared components (were duplicated per page)  */
.panel{background:#12161f;border:1px solid #283041;border-radius:6px;padding:16px;margin-bottom:16px}
.panel-title{color:var(--purple);font-size:.85rem;letter-spacing:.06em;border-bottom:1px solid #283041;padding-bottom:8px;margin-bottom:12px}
.platform-badge{display:inline-block;font-size:.65rem;letter-spacing:.06em;padding:1px 5px;border-radius:3px;vertical-align:middle;margin-left:6px;background:#1b2130;border:1px solid #283041;color:#8b93a3}
.platform-badge.pc{border-color:var(--purple);color:var(--purple)}
.platform-badge.xbox{border-color:#5cc7ff;color:#5cc7ff}
.spinner-wrap{display:flex;justify-content:center;padding:32px 0;opacity:0;animation:ds-appear 0s .15s forwards}
.spinner{width:28px;height:28px;border:3px solid #1b2130;border-top-color:var(--purple);border-radius:50%;opacity:0;animation:spin .7s linear infinite,ds-appear 0s .15s forwards}
@keyframes spin{to{transform:rotate(360deg)}}
@keyframes ds-appear{to{opacity:1}}
/*  Segmented control — a row of toggle buttons with a sliding pill indicator.
    Mark the container .seg, buttons .seg-btn (one .active); skin.js DSseg.sync()
    slides the .seg-ind pill (via CSS transform) under the active button.  */
.seg{position:relative;display:inline-flex;background:#0b0e14;border:1px solid #283041;border-radius:7px;padding:3px;vertical-align:middle;flex-shrink:0}
.seg-ind{position:absolute;top:3px;bottom:3px;left:0;border-radius:5px;background:#1c2332;box-shadow:inset 0 0 0 1px #2e3850,0 1px 2px rgba(0,0,0,.35);transition:transform .24s cubic-bezier(.4,0,.2,1),width .24s cubic-bezier(.4,0,.2,1);pointer-events:none;z-index:0;opacity:0}
.seg-btn{position:relative;z-index:1;background:none;border:none;color:#8b93a3;cursor:pointer;font-family:inherit;font-size:.74rem;font-weight:600;letter-spacing:.04em;padding:4px 13px;border-radius:5px;transition:color .15s;white-space:nowrap}
.seg-btn.active{color:var(--purple)}
.seg-btn:hover:not(.active){color:#d4dae3}
/* Auth widget */
/* Auth widget: FIXED 180×26 slot, right-aligned content (docs/perf-plans:
   no-layout-shift rule). /api/me is no-store so it resolves after first
   paint on every load — the slot is reserved so nothing moves when it
   fills, and 26px imagery keeps the header height constant. */
.steam-widget{display:flex;align-items:center;justify-content:flex-end;gap:8px;font-size:13px;flex-shrink:0;margin-left:auto;min-width:180px;height:26px;white-space:nowrap}
@media(max-width:880px){.steam-widget{display:none}}
.auth-signin-btn img{display:block;height:26px;width:auto}
.auth-user{display:flex;align-items:center;gap:8px}
.auth-user a{color:#c8d6e5;text-decoration:none}
.auth-user a:hover{text-decoration:underline}
.auth-avatar{width:26px;height:26px;border-radius:3px;object-fit:cover;display:block}
.auth-avatar-link{display:flex;line-height:0}
.auth-avatar-link:hover{text-decoration:none}
.auth-signout{font-size:0.8rem;opacity:0.7;color:inherit;text-decoration:none}
.auth-signout:hover{opacity:1;text-decoration:underline}
/* snowfall — ambient full-viewport background layer, server-rendered into the
   shared chrome (src/api/statics.rs SNOW_HTML) so it paints on every page in
   the first frame. z-index:-1 sits above the body canvas bg but below in-flow
   blocks, so opaque panels hide it and flakes only show through the
   gutters/gaps; position:fixed spans the whole screen width. Per-flake
   width/height/left/duration/opacity + a negative animation-delay are set
   inline (full field on first paint, no ramp-in); the inline sync script keeps
   the fall phase-continuous across page navigations. */
.snowfall{position:fixed; inset:0; z-index:-1; pointer-events:none; overflow:hidden;}
.flake{position:absolute; top:-10px; background:#fff; border-radius:50%; opacity:0.75; animation-name:fall; animation-timing-function:linear; animation-iteration-count:infinite;}
@keyframes fall{
  0%{transform:translateY(-10px) translateX(0);}
  100%{transform:translateY(100vh) translateX(30px);}
}
/* Respect the OS "reduce motion" preference — no falling snow for users who
   opt out (the sync script bails on the same query). */
@media (prefers-reduced-motion: reduce){ .snowfall{display:none;} }