:root {
    --bg: #0f1720;
    --panel: #17212b;
    --panel-2: #1e2b38;
    --stripe: #141d26;
    --border: #2a3a4a;
    --text: #e6edf3;
    --muted: #93a4b5;
    --accent: #6ea8fe;
    --accent-dim: #3d6ebc;
    --fide: #f0a04b;
    --uscf: #6ea8fe;
    --cfc: #e05c5c;
    --nwsrs: #57c98a;
    --warn: #e8b04b;
    --warn-dim: #6b5320;
    --danger: #ff8f8f;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    line-height: 1.5;
}

a { color: var(--accent); }

.wrap {
    max-width: 1000px;
    margin: 0 auto;
    padding: 24px 16px 64px;
}

header.site {
    border-bottom: 1px solid var(--border);
    background: var(--panel);
}

header.site .wrap {
    padding: 18px 16px;
    display: flex;
    align-items: baseline;
    gap: 16px;
    flex-wrap: wrap;
}

header.site h1 {
    font-size: 20px;
    margin: 0;
}

header.site h1 a { text-decoration: none; color: var(--text); }

header.site .tagline {
    color: var(--muted);
    font-size: 13px;
}

header.site nav {
    margin-left: auto;
    font-size: 13px;
}

/* ---------- search ---------- */

.search-box {
    display: flex;
    align-items: flex-start;   /* the hint makes this column taller than the button */
    gap: 8px;
    margin: 28px 0 8px;
}

/* Wraps the input and its hint so both share the input's width, and so the clear button can be
   positioned inside the input's right-hand edge. */
.search-field {
    position: relative;
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.search-box input {
    flex: 1;
    padding: 12px 40px 12px 14px;   /* room on the right for the clear button */
    font-size: 16px;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: var(--panel);
    color: var(--text);
    width: 100%;
}

.clear-btn {
    position: absolute;
    /* Pinned to the INPUT, not the wrapper: the wrapper is now taller because it also holds the
       hint, so centring on it would drop the button below the field. */
    top: 22px;
    right: 10px;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    line-height: 0;
}

.clear-btn:hover { color: var(--text); background: var(--border); }
.clear-btn[hidden] { display: none; }

.search-box input:focus {
    outline: none;
    border-color: var(--accent-dim);
}

/* Scoped to the SUBMIT button: a bare `.search-box button` also matches the clear button inside
   the field and, appearing later in the file, painted it solid blue. */
.search-box button[type="submit"] {
    padding: 12px 20px;
    font-size: 15px;
    border-radius: 8px;
    border: 1px solid var(--accent-dim);
    background: var(--accent-dim);
    color: #fff;
    cursor: pointer;
}

.search-box button[type="submit"]:hover { background: var(--accent); border-color: var(--accent); }
.search-box button[type="submit"]:disabled { opacity: .5; cursor: default; }

.hint {
    color: var(--muted);
    font-size: 13px;
    margin: 8px 0 0;
}

/* Result summary. Boxed and full width so it reads as a header for the results below it rather
   than as loose text floating between the search box and the table. */
.status {
    color: var(--muted);
    font-size: 14px;
    margin-top: 24px;
}

.status:not(:empty) {
    padding: 12px 16px;
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent-dim);
    border-radius: 8px;
    background: var(--panel);
}

.status.error { color: #ff9c9c; }
.status.error:not(:empty) { border-left-color: #ff9c9c; }

/* ---------- results table ---------- */

table.results {
    width: 100%;
    border-collapse: collapse;
    margin-top: 16px;
}

table.results th, table.results td {
    text-align: left;
    padding: 10px 12px;
    border-bottom: 1px solid var(--border);
    font-size: 14px;
}

table.results th {
    color: var(--muted);
    font-weight: 600;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .04em;
}

table.results tbody tr { cursor: pointer; }

/* Zebra striping. Search results are frequently a long list of near-identical names, so an
   alternating band makes it much easier to keep your eye on one row while scanning across to the
   IDs and rating. The class is assigned in JS, not with :nth-child, because each result row is
   followed by its hidden detail row. */
table.results tbody tr.result-row.stripe,
table.results tbody tr.detail-row.stripe { background: var(--stripe); }

/* Hover and expanded both need to beat the stripe, so they come after it. */
table.results tbody tr.result-row:hover { background: var(--panel-2); }

/* ---------- expandable result rows ---------- */

/* Any `display` rule beats the [hidden] attribute, so state this explicitly and keep it ahead of
   the layout rules below. Without it an expanded row cannot be collapsed again. */
table.results tr.detail-row[hidden] { display: none; }

table.results tbody tr.detail-row { cursor: default; }

table.results td.expander,
table.results th.expander { width: 28px; padding-right: 0; }

/* The IDs column sizes to its content so the chips never wrap; Name and Country absorb the slack. */
table.results td.ids,
table.results th.ids { width: 1%; white-space: nowrap; }

table.results td.name { min-width: 140px; }

/* The trailing cells open the profile in a new tab, so hint that with the cursor. */
table.results td.zone-open { cursor: alias; }

.expand-btn {
    width: 20px;
    height: 20px;
    padding: 0;
    border: none;
    border-radius: 4px;
    background: transparent;
    color: var(--muted);
    font-size: 11px;
    line-height: 1;
    cursor: pointer;
}

.expand-btn:hover { background: var(--border); color: var(--text); }

.detail-row td { padding: 0 12px 16px; border-bottom: 1px solid var(--border); }

/* Cards inside an expanded row are denser than on the full profile page, and sit on a lighter
   background so the nesting is obvious. */
.detail-body .cards {
    margin-top: 0;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

/* An expanded pair is highlighted as one block, overriding whatever stripe it carries. A detail row
   is only ever visible when its parent is expanded, so both can be shaded unconditionally here. */
table.results tbody tr.result-row.expanded,
table.results tbody tr.detail-row:not([hidden]) { background: var(--panel-2); }

.detail-body .card { background: var(--bg); }

td.name { font-weight: 600; }
td.num { text-align: right; font-variant-numeric: tabular-nums; }

.title-badge {
    display: inline-block;
    background: var(--fide);
    color: #1b1b1b;
    font-size: 11px;
    font-weight: 700;
    padding: 1px 6px;
    border-radius: 4px;
    margin-right: 6px;
    vertical-align: middle;
}

.fed-tags { display: flex; gap: 4px; flex-wrap: wrap; }

.fed-tag {
    font-size: 11px;
    font-weight: 700;
    padding: 1px 6px;
    border-radius: 4px;
    border: 1px solid;
}

.fed-tag.fide  { color: var(--fide);  border-color: var(--fide); }
.fed-tag.uscf  { color: var(--uscf);  border-color: var(--uscf); }
.fed-tag.cfc   { color: var(--cfc);   border-color: var(--cfc); }
.fed-tag.nwsrs { color: var(--nwsrs); border-color: var(--nwsrs); }

/* ---------- ID chips (search results) ---------- */

.id-chips {
    display: flex;
    gap: 6px;
    flex-wrap: nowrap;   /* never stack onto a second line - the cell widens instead */
}

.id-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 2px 4px 2px 6px;
    border: 1px solid var(--border);
    border-radius: 5px;
    background: var(--panel-2);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.id-chip-label {
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: .05em;
}

.id-chip.fide  .id-chip-label { color: var(--fide); }
.id-chip.uscf  .id-chip-label { color: var(--uscf); }
.id-chip.cfc   .id-chip-label { color: var(--cfc); }
.id-chip.nwsrs .id-chip-label { color: var(--nwsrs); }

.id-chip-value { color: var(--text); }

.copy-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    padding: 0;
    border: none;
    border-radius: 4px;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
}

.copy-btn:hover { background: var(--border); color: var(--text); }
.copy-btn.copied { color: var(--nwsrs); }

/* ---------- tabs ---------- */

.tabs {
    display: flex;
    gap: 4px;
    margin: 20px 0 0;
    border-bottom: 1px solid var(--border);
}

.tabs .tab {
    padding: 10px 16px;
    font-size: 14px;
    font-weight: 600;
    color: var(--muted);
    text-decoration: none;
    border: 1px solid transparent;
    border-bottom: none;
    border-radius: 8px 8px 0 0;
    /* Sits on top of the container's bottom border so the active tab joins the panel below it. */
    margin-bottom: -1px;
}

.tabs .tab:hover { color: var(--text); background: var(--panel); }

.tabs .tab.active {
    color: var(--text);
    background: var(--panel);
    border-color: var(--border);
    border-bottom: 1px solid var(--panel);
}

/* ---------- filters ---------- */

.filters {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
    margin: 4px 0 0;
}

.filters label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--muted);
}

.filters select {
    padding: 6px 10px;
    font-size: 13px;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: var(--panel);
    color: var(--text);
}

.filters select:focus { outline: none; border-color: var(--accent-dim); }

/* The school search has no fuzzy matching, so its hint is shorter and the box needs less air. */
.search-box.school-search { margin-bottom: 4px; }

/* ---------- school roster (expanded school row) ---------- */

.level-tag {
    display: inline-block;
    background: var(--panel-2);
    color: var(--nwsrs);
    border: 1px solid var(--nwsrs);
    font-size: 10px;
    font-weight: 700;
    padding: 1px 6px;
    border-radius: 4px;
    margin-left: 8px;
    vertical-align: middle;
}

.roster-status {
    color: var(--muted);
    font-size: 13px;
    margin: 4px 0 8px;
}

.roster-status.error { color: var(--danger); }

table.roster {
    width: 100%;
    border-collapse: collapse;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
}

table.roster th, table.roster td {
    text-align: left;
    padding: 8px 10px;
    border-bottom: 1px solid var(--border);
    font-size: 13px;
}

table.roster tr:last-child td { border-bottom: none; }

table.roster th {
    color: var(--muted);
    font-weight: 600;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
}

/* The header must share the cell's alignment, otherwise a right-aligned rating column sits under a
   left-aligned "NWSRS RATING" label. */
table.roster th.num { text-align: right; }
table.roster th.grade, table.roster td.grade { white-space: nowrap; }

table.roster tbody tr.roster-row { cursor: pointer; }
table.roster tbody tr.roster-row:hover { background: var(--panel-2); }

table.roster td.num { text-align: right; font-variant-numeric: tabular-nums; }
table.roster td.name { font-weight: 600; }
table.roster td.full-id {
    color: var(--muted);
    font-family: Consolas, "Courier New", monospace;
    font-size: 12px;
}

/* ---------- sortable column headers ---------- */

table.sortable th[data-sort] {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

table.sortable th[data-sort]:hover { color: var(--text); }

/* A neutral caret on every sortable column advertises that it can be sorted; the active column
   replaces it with a solid arrow. Reserving the space here stops the header shifting on click. */
table.sortable th[data-sort]::after {
    content: '\2195';           /* up-down arrow */
    margin-left: 6px;
    font-size: 10px;
    opacity: .35;
}

table.sortable th[data-sort].sorted { color: var(--text); }
table.sortable th[data-sort].sorted.asc::after  { content: '\25b4'; opacity: 1; color: var(--accent); }
table.sortable th[data-sort].sorted.desc::after { content: '\25be'; opacity: 1; color: var(--accent); }

/* ---------- result filter ---------- */

/* Narrows results already on screen. Deliberately lighter than the main search box so it reads as
   a refinement of the results below, not a second way to query the API. */
.result-filter {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 12px 0 0;
}

.result-filter[hidden] { display: none; }

.result-filter label {
    font-size: 12px;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .04em;
    white-space: nowrap;
}

.result-filter input {
    flex: 1;
    min-width: 0;
    max-width: 380px;
    padding: 7px 10px;
    font-size: 13px;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: var(--panel);
    color: var(--text);
}

.result-filter input:focus { outline: none; border-color: var(--accent-dim); }

.filter-count {
    font-size: 12px;
    color: var(--muted);
    white-space: nowrap;
}

/* Roster filter sits on the same line as the roster summary inside an expanded row. */
.roster-head {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.roster-head .roster-status { flex: 1; min-width: 0; }

.roster-filter {
    width: 200px;
    padding: 5px 9px;
    font-size: 12px;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: var(--panel);
    color: var(--text);
}

.roster-filter:focus { outline: none; border-color: var(--accent-dim); }

/* ---------- profile ---------- */

.profile-head { margin: 12px 0 8px; }
.profile-head h2 { margin: 0 0 4px; font-size: 28px; }
.profile-head .sub { color: var(--muted); font-size: 14px; }

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 16px;
    margin-top: 24px;
}

.card {
    background: var(--panel);
    border: 1px solid var(--border);
    border-top: 3px solid var(--border);
    border-radius: 10px;
    padding: 16px 18px;
}

.card.fide  { border-top-color: var(--fide); }
.card.uscf  { border-top-color: var(--uscf); }
.card.cfc   { border-top-color: var(--cfc); }
.card.nwsrs { border-top-color: var(--nwsrs); }

.card h3 {
    margin: 0 0 2px;
    font-size: 15px;
    letter-spacing: .03em;
    text-transform: uppercase;
}

.card .card-id { color: var(--muted); font-size: 12px; margin-bottom: 12px; }

/* Copy buttons sit inline with the ID and with the Raw JSON toggle. */
.card .card-id .copy-btn,
.raw-json summary .copy-btn,
.raw-json .json-label .copy-btn {
    width: 18px;
    height: 18px;
    margin-left: 4px;
    vertical-align: -4px;
}

.raw-json summary { display: flex; align-items: center; gap: 2px; }

/* Small status badge next to a card's ID, e.g. "Inactive" / "Expired". */
.card .pill {
    display: inline-block;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    padding: 1px 6px;
    border-radius: 999px;
    border: 1px solid var(--muted);
    color: var(--muted);
    vertical-align: 1px;
}

.card dl {
    margin: 0;
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 6px 12px;
    font-size: 14px;
    align-items: baseline;
}

.card dt { color: var(--muted); }
.card dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }

/* Links out to the federation's own page for this player. */
.card a.ext {
    color: var(--text);
    text-decoration: none;
    border-bottom: 1px dotted var(--muted);
}

.card a.ext:hover { color: var(--accent); border-bottom-color: var(--accent); }

.card .card-id a.ext { color: var(--muted); }
.card .card-id a.ext:hover { color: var(--accent); }

/* Secondary detail under a value, e.g. school city/type beneath the school name. */
.card .sub-value {
    color: var(--muted);
    font-size: 12px;
    font-variant-numeric: normal;
}

.card .note {
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--border);
    color: var(--muted);
    font-size: 12px;
}

/* Rating-list freshness. Ratings are only as current as the list they were imported from, so a
   list older than the current month is flagged - the player's live rating may have moved since. */
.card .rating-month.fresh { color: var(--text); }

.card .rating-month.aging { color: var(--warn); }

.card .rating-month.stale {
    color: var(--danger);
    font-weight: 600;
}

.card .stale-flag { font-size: 11px; vertical-align: 1px; }

.card .note.stale-note {
    color: var(--warn);
    border-top-color: var(--warn-dim);
}

.back-link { display: inline-block; margin: 20px 0 4px; font-size: 14px; }

/* Collapsible raw API payload. On a card it sits under that card's own data, so an API consumer
   can see exactly which fields produced the values above it. */
.raw-json {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}

.raw-json summary {
    cursor: pointer;
    color: var(--muted);
    font-size: 12px;
    user-select: none;
}

.raw-json summary:hover { color: var(--accent); }

.raw-json pre {
    margin: 10px 0 0;
    padding: 10px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow-x: auto;
    max-height: 360px;
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--text);
    text-align: left;
}

/* Names the endpoint a payload came from, for blocks fetched separately. */
.raw-json .json-label {
    margin-top: 10px;
    color: var(--muted);
    font-size: 11px;
    font-family: Consolas, "Courier New", monospace;
    word-break: break-all;
}

/* ---------- narrow screens: results become cards, not a scrolling table ---------- */

@media (max-width: 720px) {
    /* A five-column table cannot fit a phone, and letting it scroll sideways hides the ratings
       entirely. Collapse each row into a stacked card instead, labelling values from data-label. */
    table.results thead { display: none; }

    table.results,
    table.results tbody,
    table.results tr,
    table.results td {
        display: block;
        width: auto;
    }

    /* MUST come after the rule above: `display: block` overrides the [hidden] attribute, so without
       this the detail row can be expanded but never collapsed again. */
    table.results tr.detail-row[hidden] { display: none; }

    table.results tr.result-row {
        position: relative;
        margin-bottom: 10px;
        padding: 12px 12px 12px 34px;
        /* Deliberately flatter than the federation cards inside it: a result is a list item, and if
           it looked like a card too, an expanded row would read as cards nested in an identical
           card. Squared corners, no fill and a left accent keep the hierarchy obvious. */
        border: none;
        border-bottom: 1px solid var(--border);
        border-left: 3px solid transparent;
        border-radius: 0;
        /* Background is left to the stripe rule so alternating rows stay distinguishable here too;
           the cards stack vertically on a phone, which makes that banding more useful, not less. */
    }

    table.results tr.result-row.expanded {
        border-left-color: var(--accent);
        border-bottom-color: transparent;
        background: var(--panel-2);
        margin-bottom: 0;
    }

    table.results tr.result-row td {
        padding: 2px 0;
        border: none;
    }

    /* The expander sits in the padding gutter so it does not consume a row of its own. */
    table.results td.expander {
        position: absolute;
        top: 10px;
        left: 8px;
        width: auto;
        padding: 0;
    }

    table.results td.name {
        font-size: 16px;
        font-weight: 600;
        margin-bottom: 6px;
    }

    /* IDs may legitimately need a second line on a phone; horizontal scrolling would be worse. */
    table.results td.ids { white-space: normal; }
    table.results td.ids .id-chips { flex-wrap: wrap; }

    /* Print the column name beside each remaining value. */
    table.results td.country,
    table.results td.city,
    table.results td.num {
        display: flex;
        justify-content: space-between;
        gap: 12px;
        text-align: left;
        font-size: 13px;
        color: var(--muted);
    }

    table.results td.country::before,
    table.results td.city::before,
    table.results td.num::before {
        content: attr(data-label);
        color: var(--muted);
    }

    table.results td.city { margin-top: 6px; }

    /* An empty value would otherwise leave a stray label floating on its own line. */
    table.results td.country:empty,
    table.results td.city:empty,
    table.results td.num:empty { display: none; }

    /* The nested roster is a five-column table inside an already-narrow card, so it collapses to
       stacked rows too rather than scrolling sideways. */
    table.roster thead { display: none; }

    table.roster,
    table.roster tbody,
    table.roster tr,
    table.roster td {
        display: block;
        width: auto;
    }

    table.roster tr {
        padding: 8px 0;
        border-bottom: 1px solid var(--border);
    }

    table.roster tr:last-child { border-bottom: none; }

    table.roster td {
        padding: 2px 10px;
        border: none;
    }

    table.roster td[data-label] {
        display: flex;
        justify-content: space-between;
        gap: 12px;
        text-align: left;
        color: var(--muted);
    }

    table.roster td[data-label]::before {
        content: attr(data-label);
        color: var(--muted);
    }

    table.roster td.num { text-align: left; }

    table.results tr.detail-row {
        margin-bottom: 10px;
        padding-bottom: 4px;
        border: none;
        border-left: 3px solid var(--accent);
        border-bottom: 1px solid var(--border);
        background: var(--panel-2);
    }

    .detail-row td { padding: 0 10px 12px; border-bottom: none; }

    /* One card per row on a phone: side-by-side cards would be unreadably narrow. */
    .detail-body .cards { grid-template-columns: 1fr; gap: 10px; }

    /* The federation cards sit on the expanded row's lighter background, so darken them to keep
       them reading as distinct raised panels rather than merging into it. */
    .detail-body .card { background: var(--bg); }

    /* Reclaim horizontal space for the content. */
    .wrap { padding: 16px 12px 48px; }

    header.site .wrap { padding: 12px; gap: 8px; }
    header.site h1 { font-size: 17px; }
    header.site .tagline { display: none; }

    .search-box { margin: 18px 0 8px; }
    .search-box input { font-size: 16px; }   /* 16px stops iOS zooming in on focus */

    /* Card values can be long (school names, locations); let them wrap rather than overflow. */
    .card dl { grid-template-columns: minmax(90px, auto) 1fr; }
    .card dd { word-break: break-word; }

    /* Profile page. */
    .profile-head h2 { font-size: 22px; }
    .cards { grid-template-columns: 1fr; }

    /* Long JSON lines must scroll inside the block, never widen the page. */
    .raw-json pre { font-size: 11px; max-height: 300px; }
}
