/* One file, no dependency on the outside world. A public page on a tenant's
   domain must not open a connection to third parties — otherwise it needs a
   consent banner, and that costs more clicks than the design brings in. This
   file serves the admin area too, for the same reason: one stylesheet, one
   trust boundary, nothing fetched from anywhere else. */

:root {
    --ink: #0a0c11;
    --base: #0a0c11;
    --surface: #14161f;
    --surface-bright: #1b1f2b;
    --surface-alt: #1f2330;
    --border: #262b3a;
    --border-soft: #1b1f2a;
    --text: #e9ebf1;
    --muted: #98a1b5;
    --dim: #5d6579;
    --accent: #f2a93b;
    --accent-ink: #17130a;
    --good: #3ed598;
    --bad: #ff6b5a;
    --warn: #e0ac4a;
    --radius: 10px;
    --radius-sm: 6px;
    --mono: ui-monospace, "SF Mono", "IBM Plex Mono", Menlo, monospace;
    --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

@media (prefers-color-scheme: light) {
    :root {
        --ink: #f4f5f9;
        --base: #f4f5f9;
        --surface: #ffffff;
        --surface-bright: #eef0f7;
        --surface-alt: #e7eaf3;
        --border: #dde1ec;
        --border-soft: #e7eaf3;
        --text: #14161d;
        --muted: #5c6478;
        --dim: #848da2;
        --accent: #b0690f;
        --accent-ink: #ffffff;
    }
}

* { box-sizing: border-box; }

body {
    margin: 0;
    background: var(--base);
    color: var(--text);
    font: 15px/1.55 var(--sans);
    -webkit-font-smoothing: antialiased;
}

h1 {
    font-size: 1.4rem;
    font-weight: 800;
    letter-spacing: .01em;
    margin: 0 0 .3rem;
}

h2 {
    font-size: .82rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .09em;
    color: var(--muted);
    margin: 0;
}

h3 { font-size: 1rem; margin: 1.25rem 0 .5rem; }
a { color: inherit; }

code { font-family: var(--mono); font-size: .85em; }

.aside { color: var(--muted); }
.small { font-size: .85rem; }

/* ─── Public link page ────────────────────────────────────────────────────── */

.public {
    max-width: 560px;
    margin: 0 auto;
    padding: 3rem 1.25rem 4rem;
}

.linkpage header { text-align: center; margin-bottom: 2rem; }
.linkpage .avatar { border-radius: 50%; object-fit: cover; }
.linkpage h1 { font-size: 1.6rem; text-transform: none; letter-spacing: normal; font-weight: 650; margin: .75rem 0 .35rem; color: var(--text); }
.linkpage .bio { color: var(--muted); margin: 0; }

.blocks { list-style: none; margin: 0; padding: 0; display: grid; gap: .7rem; }

.blocks .block {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    padding: 1rem 1.15rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    text-decoration: none;
    font-weight: 550;
    transition: transform .12s ease, border-color .12s ease;
}

.blocks .block:hover { transform: translateY(-2px); border-color: var(--accent); }
.blocks .block .platform { color: var(--muted); font-weight: 400; font-size: .85rem; }
.blocks .block.release { border-left: 3px solid var(--accent); }
.blocks .block.gate { border-left: 3px solid var(--good); }

/* A player is not a link — nothing to lift on hover, and the label sits above
   the control instead of beside it, the same way it would above any other
   audio player. */
.blocks .block.player { flex-direction: column; align-items: stretch; gap: .6rem; cursor: default; }
.blocks .block.player:hover { transform: none; border-color: var(--border); }
.blocks .block.player audio { width: 100%; }

.signup { margin-top: 2.5rem; display: grid; gap: .6rem; }
.signup form { display: grid; gap: .5rem; }
.signup .wording { color: var(--muted); font-size: .85rem; margin: 0; }

.empty {
    text-align: center;
    padding: 3rem 1.5rem;
    color: var(--muted);
    background: var(--surface);
    border: 1px dashed var(--border);
    border-radius: var(--radius);
}

/* ─── Administration shell ────────────────────────────────────────────────── */

.admin { display: flex; min-height: 100vh; align-items: stretch; }

/* STICKY AND EXACTLY ONE SCREEN TALL.
   Before: min-height: 100vh on the flex container, so the sidebar stretched to
   the height of the DOCUMENT. margin-top: auto then pushed the block with the
   tenant, the account and the language to the bottom of the PAGE — on a long
   page hundreds of pixels below the fold, where nobody scrolls a navigation bar
   to find it.
   align-items: flex-start stops the stretch; sticky keeps it in view; the height
   is what the screen has, not what the content wants. */
.sidebar {
    width: 232px;
    flex: 0 0 232px;
    background: var(--surface);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    align-self: flex-start;
    position: sticky;
    top: 0;
    height: 100vh;
    height: 100dvh;   /* the mobile browser bar eats part of 100vh */
}

/* The links scroll when there are too many for the screen; the foot does not
   move. min-height: 0 is what allows a flex child to shrink below its content —
   without it overflow never engages and the foot is pushed out again. */
.sidebar .nav { flex: 1 1 auto; overflow-y: auto; min-height: 0; padding: .5rem; }
.sidebar .foot { flex: 0 0 auto; }

.sidebar .brand {
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: 1.15rem 1.25rem 1rem;
    border-bottom: 1px solid var(--border-soft);
    font-weight: 800;
    font-size: .82rem;
    text-transform: uppercase;
    letter-spacing: .1em;
}

.sidebar .brand .led {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 8px var(--accent);
    flex: 0 0 auto;
}

/* A label, not a link — the whole point is that a group is a visual break in
   an otherwise flat list of ten links, so it must not look clickable. */
.sidebar .group {
    font-family: var(--mono);
    font-size: .68rem;
    color: var(--dim);
    text-transform: uppercase;
    letter-spacing: .1em;
    padding: .9rem .75rem .3rem;
}

.sidebar .item {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .5rem .75rem;
    margin: 1px 0;
    border-radius: var(--radius-sm);
    color: var(--muted);
    text-decoration: none;
    font-size: .88rem;
    font-weight: 500;
}

.sidebar .item:hover { color: var(--text); background: var(--surface-bright); }
.sidebar .item.active { color: var(--text); background: var(--surface-alt); }

.sidebar .item .dot {
    width: 5px;
    height: 5px;
    flex: 0 0 5px;
    border-radius: 50%;
    background: var(--border);
}

.sidebar .item.active .dot { background: var(--accent); box-shadow: 0 0 6px var(--accent); }

.sidebar .foot { border-top: 1px solid var(--border-soft); padding: .85rem 1.25rem; }

.sidebar .language { margin-top: .6rem; }
.sidebar .language select { font-size: .82rem; padding: .3rem .45rem; }

/* ─── Topbar ───────────────────────────────────────────────────────────────── */

/* Everything that used to crowd the bottom of the sidebar — who is signed in,
   which tenant, how to switch — now lives up here, where it stays visible
   without competing with the navigation for space. The sidebar is left to do
   one thing: get somewhere else. */
.topbar {
    display: flex;
    align-items: center;
    gap: .85rem;
    padding: .6rem 1.5rem;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 30;
    flex-wrap: wrap;
}

.workspace { display: flex; align-items: center; gap: .6rem; }

.workspace .avatar {
    width: 28px;
    height: 28px;
    flex: 0 0 28px;
    border-radius: var(--radius-sm);
    background: var(--accent);
    color: var(--accent-ink);
    display: grid;
    place-items: center;
    font-weight: 800;
    font-size: .72rem;
    text-transform: uppercase;
}

.workspace .name { font-weight: 650; font-size: .9rem; line-height: 1.15; }
.workspace .kind { font-family: var(--mono); font-size: .68rem; color: var(--dim); text-transform: uppercase; letter-spacing: .06em; }
.workspace .switch { font-size: .78rem; color: var(--accent); text-decoration: none; margin-left: .35rem; }

.topbar .spacer { flex: 1; }

.topbar .who {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-size: .82rem;
    color: var(--muted);
}

.topbar .who .name { color: var(--text); max-width: 16rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.topbar .who a { color: var(--accent); text-decoration: none; font-size: .8rem; }

/* Column flex: the topbar and the content stack vertically underneath it.
   The topbar sticks to the top of the PAGE (there is no scroll container of
   its own in here), the same way the sidebar already stuck to it. */
.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.content { min-width: 0; padding: 1.75rem 2.25rem 4rem; max-width: 1180px; }

@media (max-width: 800px) {
    .admin { flex-direction: column; align-items: stretch; }
    /* A bar at the top, and no longer sticky: on a phone one screen of navigation
       above the content is the content gone. */
    .sidebar {
        width: auto;
        flex: none;
        position: static;
        height: auto;
        border-right: none;
        border-bottom: 1px solid var(--border);
    }
    .sidebar .nav { overflow-y: visible; display: flex; flex-wrap: wrap; gap: .2rem; padding: .6rem; }
    .sidebar .group { display: none; }
    .topbar { position: static; }
    .content { padding: 1.1rem 1.1rem 3rem; }
}

/* ─── The address a page ends up at ───────────────────────────────────────── */

.address {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
    margin: .35rem 0 .9rem;
    font-size: .9rem;
}

.address .label { color: var(--muted); font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; }
.address a { color: var(--accent); word-break: break-all; text-decoration: none; }
.address a:hover { text-decoration: underline; }
.address.missing a { color: var(--muted); }

.address .copy {
    font: inherit;
    font-size: .78rem;
    padding: .15rem .5rem;
    color: var(--muted);
    background: var(--surface-bright);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.address .copy:hover { color: var(--text); }

/* ─── View header ──────────────────────────────────────────────────────────── */

/* Every page starts with these two: what this is, in one line what it is for.
   Giving that its own block with room underneath is what actually separates
   "the page's own headline" from "the first rack's own title" below it — both
   used to be a bare <h1>/<h2> with the same weight, so the page read as one
   long undifferentiated list rather than a title over a stack of panels. */
.view-head { margin-bottom: 1.4rem; }
.view-head h1 { text-transform: uppercase; }
.view-head .lede { color: var(--muted); margin: .4rem 0 0; max-width: 74ch; font-size: .92rem; }

/* ─── Rack: the one container every section of content sits in ───────────── */

/* THE SINGLE BIGGEST CHANGE HERE. Before, a page was h2, table, h2, form, h2,
   table — one flat stream with nothing telling the eye where one piece of
   content ends and the next begins beyond a change in font size. A rack is a
   bordered panel with its own header bar; five racks down a page read as five
   distinct instruments, not one long scroll. */
.rack {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    margin-bottom: 1.1rem;
    overflow: hidden;
}

.rack-head {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .7rem .95rem;
    border-bottom: 1px solid var(--border-soft);
    flex-wrap: wrap;
}

.rack-head h2 { flex: 0 0 auto; }
.rack-head .spacer { flex: 1; }

/* overflow-x, not visible: .rack itself clips (for the rounded corners), and
   without a scrollable escape hatch here a table wider than the panel — a
   release row's full public address plus its action buttons, easily wider
   than 1100px — is not just tight, it is GONE: cut off mid-button by the
   rack's own edge with no way to reach the rest. Scrolling loses nothing;
   overflow: hidden silently did. */
.rack-body { padding: .95rem; overflow-x: auto; }
.rack-body > :first-child { margin-top: 0; }
.rack-body > :last-child { margin-bottom: 0; }

/* A rack with nothing inside — no table rows, no list items — still needs to
   say so rather than show an empty header over a blank body. */
.rack-empty { padding: 1.5rem; text-align: center; color: var(--muted); font-size: .88rem; }

/* The shape of a populated page, faded — shown next to NoTenant so the page
   reads as "pick a tenant to see this" rather than as an empty install.
   Static, not shimmering: a placeholder that moves competes for attention
   with the actual notice above it, which is the one thing here asking to be
   read. */
.skeleton { opacity: .5; margin-top: 1.25rem; }
.skeleton-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: .75rem; margin-bottom: 1.1rem; }
.skeleton-tile { height: 4.2rem; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
.skeleton-rack { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem; display: grid; gap: .65rem; }
.skeleton-row { height: 1rem; background: var(--border-soft); border-radius: 4px; }

.bar { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: 1rem; }
.bar .spacer { flex: 1; }

/* ─── Building blocks ─────────────────────────────────────────────────────── */

.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: .75rem; margin-bottom: 1.1rem; }

.tile {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1rem 1.1rem;
    display: grid;
    gap: .25rem;
}

.tile .label { color: var(--muted); font-size: .7rem; text-transform: uppercase; letter-spacing: .08em; order: -1; }
.tile .number { font-size: 1.7rem; font-weight: 800; line-height: 1; font-stretch: 110%; }
.tile .number.small { font-size: 1.05rem; }
.tile .hint { font-family: var(--mono); font-size: .7rem; color: var(--dim); }
.tile.warning { border-color: var(--warn); }
.tile.warning .number { color: var(--warn); }

.list, .table { width: 100%; border-collapse: collapse; }
.list th, .table th {
    text-align: left;
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .07em;
    font-weight: 600;
    color: var(--muted);
    padding: .55rem .7rem;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}
.list td, .table td { padding: .65rem .7rem; border-top: 1px solid var(--border-soft); vertical-align: middle; font-size: .88rem; }
.list tr:first-child td, .table tr:first-child td { border-top: 0; }
.list tbody tr:hover td, .table tbody tr:hover td { background: var(--surface-bright); }
.list .actions, .table .actions { text-align: right; white-space: nowrap; }
tr.selected td { background: var(--surface-alt); }
tr.click { cursor: pointer; }

.badge {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .18rem .55rem;
    border-radius: 999px;
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    font-family: var(--mono);
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    white-space: nowrap;
}

.badge::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }

.badge.good, .badge.active, .badge.verified { color: var(--good); border-color: var(--good); }
.badge.bad, .badge.suspended { color: var(--bad); border-color: var(--bad); }
.badge.pending, .badge.warning { color: var(--warn); border-color: var(--warn); }
.badge.platform { color: var(--accent); border-color: var(--accent); }

/* An uploaded image shown back at the size it will actually appear at admin
   scale — release artwork, a link page's avatar — never the size it was
   uploaded at. */
.thumbnail { display: block; width: 96px; height: 96px; object-fit: cover;
             border-radius: var(--radius-sm); border: 1px solid var(--border); }

.button {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .5rem .9rem;
    background: var(--surface-bright);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    font: inherit;
    font-weight: 500;
    text-decoration: none;
    transition: border-color .12s ease, background .12s ease;
}

.button:hover:not(:disabled) { border-color: var(--dim); background: var(--surface-alt); }
.button:disabled { opacity: .45; cursor: not-allowed; }
.button.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 650; }
.button.primary:hover:not(:disabled) { filter: brightness(1.08); border-color: var(--accent); }
.button.danger { color: var(--bad); }
.button.danger:hover:not(:disabled) { border-color: var(--bad); }
.button.small { padding: .32rem .6rem; font-size: .82rem; }
.button.wide { width: 100%; text-align: left; display: grid; gap: .1rem; justify-content: unset; }

.buttons { display: flex; gap: .6rem; margin: .9rem 0; flex-wrap: wrap; }
.rack-body > .buttons:last-child { margin-bottom: 0; }

.field { display: grid; gap: .7rem; margin: .9rem 0; }
.field.horizontal { grid-auto-flow: column; grid-auto-columns: 1fr auto; align-items: center; }
.field label { display: grid; gap: .3rem; font-size: .82rem; color: var(--muted); }
.field label.checkbox { display: flex; flex-direction: row; align-items: center; gap: .5rem; font-size: .88rem; color: var(--text); }
.field label.checkbox input { width: auto; }

input, select, textarea {
    padding: .5rem .65rem;
    background: var(--ink);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font: inherit;
    width: 100%;
}

input:focus, select:focus, textarea:focus, :focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }

/* Blazor's enhanced navigation focuses the page's own h1 after every route
   change, for screen readers. Useful, but plain heading text has no visible
   affordance for being a focus target, so the same ring that helps on a
   button just reads as a stray orange box here. */
h1:focus-visible { outline: none; }
input[type="checkbox"] { width: auto; accent-color: var(--accent); }

.notice {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: var(--radius);
    padding: .85rem 1.1rem;
    margin: 1rem 0;
    font-size: .9rem;
}

.notice.warning { border-left-color: var(--warn); }
.notice h2 { margin-top: 0; text-transform: none; letter-spacing: normal; font-size: 1rem; color: var(--text); }

.two-column { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; align-items: start; }
@media (max-width: 950px) { .two-column { grid-template-columns: 1fr; } }

.block-list { list-style: none; padding: 0; margin: 0; display: grid; gap: .6rem; }

.block-list li {
    background: var(--surface-bright);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: .6rem;
    display: grid;
    gap: .4rem;
}

.block-list .row { display: flex; gap: .4rem; }
.block-list .id { font-family: var(--mono); font-size: .8rem; }

.steps { list-style: none; padding: 0; margin: 0; display: grid; gap: .2rem; }
.steps li { display: grid; grid-template-columns: 1.2rem 1fr; gap: .5rem; font-size: .88rem; padding: .15rem 0; }
.steps li .kind { color: var(--dim); }
.steps li.done .kind { color: var(--good); }
.steps li.pending { color: var(--muted); }

.diff { list-style: none; padding: 0; margin: 0; display: grid; gap: .3rem; }

.diff li {
    display: grid;
    grid-template-columns: 1.2rem 10rem 1fr;
    gap: .5rem;
    padding: .45rem .6rem;
    border-radius: var(--radius-sm);
    background: var(--surface-bright);
    font-size: .85rem;
}

.diff .kind { font-weight: 700; text-align: center; }
.diff .id { font-family: var(--mono); color: var(--muted); font-size: .8rem; }
.diff li.add .kind { color: var(--good); }
.diff li.remove .kind { color: var(--bad); }
.diff li.modify .kind { color: var(--warn); }
.diff li.unchanged, .diff li.foreignkept { opacity: .55; }

.tenant-choice .list { list-style: none; padding: 0; display: grid; gap: .5rem; }

/* Rows of equal-weight items — a segment, a backlog entry, a queued job. Its
   own pattern because a <table> row assumes columns line up across many rows,
   and a lot of what these pages show is really a short flat list where only
   one row's numbers ever differ in shape from the next. */
.rows { display: flex; flex-direction: column; gap: .4rem; }

.row {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .6rem .75rem;
    background: var(--surface-bright);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm);
}

.row .row-main { min-width: 0; flex: 1; }
.row .row-title { font-weight: 600; font-size: .88rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row .row-sub { font-family: var(--mono); font-size: .74rem; color: var(--dim); margin-top: .1rem; }

/* The bar that appears when the circuit is gone.
 *
 * display:none is not decoration — it IS the switch. The framework looks for
 * #blazor-error-ui and sets display:block on it when a circuit dies; with no rule
 * here the element would stand on every page from the start.
 *
 * Fixed to the bottom and above everything, because it has to be readable over a
 * page that is still fully drawn and no longer reacts to anything. */
#blazor-error-ui {
    display: none;
    position: fixed;
    inset: auto 0 0 0;
    z-index: 1000;
    padding: .85rem 3rem .85rem 1.25rem;
    background: var(--bad);
    color: #fff;
    box-shadow: 0 -2px 12px rgba(0, 0, 0, .35);
    font-size: .95rem;
}

#blazor-error-ui strong { margin-right: .5rem; }
#blazor-error-ui .reload { color: #fff; margin-left: .75rem; font-weight: 600; }

#blazor-error-ui .dismiss {
    position: absolute;
    top: .5rem;
    right: 1rem;
    color: #fff;
    text-decoration: none;
    font-size: 1.2rem;
    line-height: 1;
}

/* ─── Modal ────────────────────────────────────────────────────────────────── */
/*
 * A native <dialog>, shown with showModal() — the backdrop below is the
 * ::backdrop pseudo-element that comes with it, not a div this stylesheet
 * has to position and stack by hand.
 */
.modal {
    padding: 0;
    border: none;
    border-radius: var(--radius);
    background: transparent;
    max-width: min(640px, calc(100vw - 2rem));
    max-height: calc(100vh - 4rem);
    max-height: calc(100dvh - 4rem);
}

/* Detail modals wrapping a data table need more than 640px or the table
   forces a horizontal scrollbar inside the already vertically-scrolling
   modal body — technically reachable, but nothing hints it's there. */
.modal-wide { max-width: min(920px, calc(100vw - 2rem)); }

.modal::backdrop { background: rgba(6, 8, 14, .55); }

.modal-box {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    display: flex;
    flex-direction: column;
    max-height: inherit;
}

.modal-head {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .8rem 1rem;
    border-bottom: 1px solid var(--border-soft);
    flex: 0 0 auto;
}

.modal-head h2 { flex: 1; margin: 0; }

.modal-close {
    background: none;
    border: none;
    color: var(--muted);
    font-size: 1.4rem;
    line-height: 1;
    padding: 0 .2rem;
    cursor: pointer;
}

.modal-close:hover { color: var(--text); }

.modal-body { padding: 1rem; overflow-y: auto; overflow-x: auto; }
.modal-body > :first-child { margin-top: 0; }
.modal-body > :last-child { margin-bottom: 0; }

@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }
