/*
 * The customer's own view of one server.
 *
 * Plain CSS for the same reason as order-cards.css: the panels have no Vite
 * build and serve Filament's pre-compiled app.css, so `class="flex gap-4"` in a
 * Blade view here is inert markup. Anything bespoke brings its own rules.
 *
 * Registered on the client panel only, in ClientPanelProvider:
 *
 *     Css::make('service-page', resource_path('css/service-page.css'))
 *
 * which `php artisan filament:assets` copies to public/css/app/service-page.css.
 * Editing this file therefore needs that command re-run — composer's
 * post-autoload-dump does it too, by way of `filament:upgrade`.
 *
 * Every colour is a variable FilamentColor writes into :root at runtime
 * (--gray-*, --primary-*, --success-*, --warning-*, --danger-*, --info-*) and
 * every size comes from the theme block in app.css (--radius-xl, --text-sm), so
 * this page tracks the panel's palette and its dark mode instead of defining a
 * second one. Dark mode is the `.dark` class on <html>.
 *
 * The namespace is `sv-`, kept apart from order-cards' `oc-` because both files
 * load on every client page and a shared prefix would let a rule written for the
 * order form silently restyle a server.
 */

.sv-page {
    display: grid;
    gap: 1rem;
}

/* ------------------------------------------------------------------ *
 * State banner — the one thing that outranks the rest of the page
 * ------------------------------------------------------------------ */

.sv-banner {
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
    padding: 0.875rem 1rem;
    border-radius: var(--radius-xl, 0.75rem);
    background-color: color-mix(in oklab, var(--info-500) 8%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--info-500) 25%, transparent);
}

.sv-banner--warning {
    background-color: color-mix(in oklab, var(--warning-500) 9%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--warning-500) 28%, transparent);
}

.sv-banner--danger {
    background-color: color-mix(in oklab, var(--danger-500) 9%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--danger-500) 28%, transparent);
}

.sv-banner-icon {
    flex: none;
    width: 1.25rem;
    height: 1.25rem;
    margin-top: 0.0625rem;
    color: var(--info-600);
}

.sv-banner--warning .sv-banner-icon {
    color: var(--warning-600);
}

.sv-banner--danger .sv-banner-icon {
    color: var(--danger-600);
}

.dark .sv-banner-icon {
    color: var(--info-400);
}

.dark .sv-banner--warning .sv-banner-icon {
    color: var(--warning-400);
}

.dark .sv-banner--danger .sv-banner-icon {
    color: var(--danger-400);
}

/* The icon spins only while something is actually in flight, which is the whole
   signal — a still cog beside "Building" reads as a stalled build. */
.sv-banner-icon--spin {
    animation: sv-spin 1.4s linear infinite;
}

@keyframes sv-spin {
    to {
        transform: rotate(360deg);
    }
}

.sv-banner-title {
    font-size: var(--text-sm, 0.875rem);
    font-weight: 600;
    color: var(--gray-950);
}

.sv-banner-body {
    margin-top: 0.125rem;
    font-size: var(--text-sm, 0.875rem);
    line-height: 1.35rem;
    color: var(--gray-600);
}

.dark .sv-banner-title {
    color: var(--gray-50);
}

.dark .sv-banner-body {
    color: var(--gray-400);
}

/* ------------------------------------------------------------------ *
 * Hero — the identity of the machine, said once and said properly
 * ------------------------------------------------------------------ */

.sv-hero {
    position: relative;
    overflow: hidden;
    padding: 1.25rem;
    border-radius: var(--radius-xl, 0.75rem);
    background-color: white;
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--gray-950) 6%, transparent);
}

/* A wash rather than a picture. It gives the top of the page a horizon without
   putting a decorative image in front of a customer who came here to reboot. */
.sv-hero::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 6rem;
    background-image: linear-gradient(120deg, color-mix(in oklab, var(--primary-500) 12%, transparent), transparent 62%);
    pointer-events: none;
}

.dark .sv-hero {
    background-color: var(--gray-900);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, white 10%, transparent);
}

.dark .sv-hero::before {
    background-image: linear-gradient(120deg, color-mix(in oklab, var(--primary-400) 16%, transparent), transparent 62%);
}

.sv-hero-top {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: center;
}

/* The image the request was about. Sized in rem so it holds its place whatever
   the PNG's intrinsic size is, and `contain` so a square Windows mark and a wide
   Ubuntu one both sit inside the same tile without either being cropped. */
.sv-hero-logo {
    flex: none;
    display: grid;
    place-items: center;
    width: 3.5rem;
    height: 3.5rem;
    padding: 0.5rem;
    border-radius: 0.875rem;
    background-color: white;
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--gray-950) 8%, transparent), 0 1px 2px color-mix(in oklab, var(--gray-950) 6%, transparent);
}

.sv-hero-logo img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.dark .sv-hero-logo {
    background-color: color-mix(in oklab, white 6%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, white 12%, transparent);
}

.sv-hero-main {
    flex: 1 1 16rem;
    min-width: 0;
}

.sv-hero-name {
    font-size: var(--text-xl, 1.25rem);
    font-weight: 700;
    line-height: 1.75rem;
    color: var(--gray-950);
    word-break: break-word;
}

.sv-hero-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
    align-items: center;
    margin-top: 0.25rem;
    font-size: var(--text-sm, 0.875rem);
    color: var(--gray-500);
}

.dark .sv-hero-name {
    color: var(--gray-50);
}

.dark .sv-hero-meta {
    color: var(--gray-400);
}

.sv-sep {
    width: 0.1875rem;
    height: 0.1875rem;
    border-radius: 9999px;
    background-color: currentColor;
    opacity: 0.55;
}

.sv-hero-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
    align-items: center;
}

/* ------------------------------------------------------------------ *
 * Badges — a dot and a word, coloured from the enum's own colour
 * ------------------------------------------------------------------ */

.sv-badge {
    display: inline-flex;
    gap: 0.375rem;
    align-items: center;
    padding: 0.25rem 0.5rem;
    border-radius: 0.5rem;
    font-size: var(--text-xs, 0.75rem);
    font-weight: 600;
    white-space: nowrap;
    color: var(--gray-700);
    background-color: color-mix(in oklab, var(--gray-500) 10%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--gray-500) 18%, transparent);
}

.sv-badge-dot {
    width: 0.375rem;
    height: 0.375rem;
    border-radius: 9999px;
    background-color: currentColor;
}

.sv-badge--success {
    color: var(--success-700);
    background-color: color-mix(in oklab, var(--success-500) 12%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--success-500) 22%, transparent);
}

.sv-badge--warning {
    color: var(--warning-700);
    background-color: color-mix(in oklab, var(--warning-500) 14%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--warning-500) 24%, transparent);
}

.sv-badge--danger {
    color: var(--danger-700);
    background-color: color-mix(in oklab, var(--danger-500) 12%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--danger-500) 22%, transparent);
}

.sv-badge--info {
    color: var(--info-700);
    background-color: color-mix(in oklab, var(--info-500) 12%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--info-500) 22%, transparent);
}

.sv-badge--primary {
    color: var(--primary-700);
    background-color: color-mix(in oklab, var(--primary-500) 12%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--primary-500) 22%, transparent);
}

.dark .sv-badge {
    color: var(--gray-300);
}

.dark .sv-badge--success {
    color: var(--success-300);
}

.dark .sv-badge--warning {
    color: var(--warning-300);
}

.dark .sv-badge--danger {
    color: var(--danger-300);
}

.dark .sv-badge--info {
    color: var(--info-300);
}

.dark .sv-badge--primary {
    color: var(--primary-300);
}

/* A running server gets a halo on its dot. It is the one piece of motion on a
   healthy page and it answers "is it up" from across the room. */
.sv-badge--success .sv-badge-dot {
    box-shadow: 0 0 0 0.1875rem color-mix(in oklab, var(--success-500) 22%, transparent);
}

/* ------------------------------------------------------------------ *
 * Copying — the flash that says a value went to the clipboard
 * ------------------------------------------------------------------ */

/* Feedback has to be on the thing that was pressed. A toast for a copy is a
   notification for something the customer already knows they did. */
.sv-copied.sv-copy-btn {
    color: var(--success-600);
}

.dark .sv-copied.sv-copy-btn {
    color: var(--success-400);
}

/* Both marks ship in the markup and this is what decides which one is seen. In
   JavaScript it would be one more thing to keep in step with the flash class, and
   a frame with no icon at all between removing one and inserting the other. */
.sv-copy-check {
    display: none;
}

.sv-copied .sv-copy-icon {
    display: none;
}

.sv-copied .sv-copy-check {
    display: block;
}

/* ------------------------------------------------------------------ *
 * Stat tiles — four numbers, each with a unit, none of them a sentence
 * ------------------------------------------------------------------ */

.sv-stats {
    display: grid;
    gap: 0.75rem;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}

.sv-stat {
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
    padding: 0.875rem 1rem;
    border-radius: var(--radius-xl, 0.75rem);
    background-color: white;
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--gray-950) 6%, transparent);
}

.dark .sv-stat {
    background-color: var(--gray-900);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, white 10%, transparent);
}

.sv-stat-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    border-radius: 0.5rem;
    color: var(--primary-600);
    background-color: color-mix(in oklab, var(--primary-500) 10%, transparent);
}

.sv-stat-icon svg {
    width: 1.125rem;
    height: 1.125rem;
}

.dark .sv-stat-icon {
    color: var(--primary-300);
    background-color: color-mix(in oklab, var(--primary-400) 16%, transparent);
}

.sv-stat-body {
    min-width: 0;
    flex: 1 1 auto;
}

.sv-stat-label {
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--gray-500);
}

.sv-stat-value {
    display: flex;
    gap: 0.25rem;
    align-items: baseline;
    margin-top: 0.125rem;
    font-size: var(--text-lg, 1.125rem);
    font-weight: 700;
    line-height: 1.5rem;
    color: var(--gray-950);
}

.sv-stat-unit {
    font-size: var(--text-xs, 0.75rem);
    font-weight: 500;
    color: var(--gray-500);
}

.sv-stat-note {
    margin-top: 0.125rem;
    font-size: var(--text-xs, 0.75rem);
    color: var(--gray-500);
}

.dark .sv-stat-label,
.dark .sv-stat-unit,
.dark .sv-stat-note {
    color: var(--gray-400);
}

.dark .sv-stat-value {
    color: var(--gray-50);
}

/* The meter only appears on a metered allowance, so its absence is information:
   an unmetered server shows no bar to read into. */
.sv-meter {
    height: 0.25rem;
    margin-top: 0.4375rem;
    border-radius: 9999px;
    overflow: hidden;
    background-color: var(--gray-200);
}

.dark .sv-meter {
    background-color: color-mix(in oklab, white 12%, transparent);
}

.sv-meter-fill {
    height: 100%;
    border-radius: 9999px;
    background-color: var(--primary-500);
}

.sv-meter-fill--warning {
    background-color: var(--warning-500);
}

.sv-meter-fill--danger {
    background-color: var(--danger-500);
}

/* ------------------------------------------------------------------ *
 * Cards — the containers everything else lives in
 * ------------------------------------------------------------------ */

/* Two columns where there is room and one where there is not. `auto-fit` with a
   floor rather than a media query, because the panel's sidebar can be open or
   collapsed and the breakpoint that matters is the width of this column, not the
   width of the window. */
.sv-grid {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    align-items: start;
}

.sv-card {
    display: flex;
    flex-direction: column;
    border-radius: var(--radius-xl, 0.75rem);
    background-color: white;
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--gray-950) 6%, transparent);
}

.dark .sv-card {
    background-color: var(--gray-900);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, white 10%, transparent);
}

.sv-card-head {
    display: flex;
    gap: 0.625rem;
    align-items: center;
    padding: 0.875rem 1rem;
    border-bottom: 1px solid color-mix(in oklab, var(--gray-950) 6%, transparent);
}

.dark .sv-card-head {
    border-bottom-color: color-mix(in oklab, white 10%, transparent);
}

.sv-card-icon {
    flex: none;
    width: 1.125rem;
    height: 1.125rem;
    color: var(--gray-400);
}

.sv-card-title {
    flex: 1 1 auto;
    font-size: var(--text-sm, 0.875rem);
    font-weight: 600;
    color: var(--gray-950);
}

.dark .sv-card-title {
    color: var(--gray-50);
}

.sv-card-body {
    padding: 0.25rem 1rem 0.875rem;
}

/* ------------------------------------------------------------------ *
 * Rows — a label and a value, aligned down a card
 * ------------------------------------------------------------------ */

.sv-rows {
    display: grid;
}

.sv-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
    align-items: center;
    justify-content: space-between;
    padding: 0.625rem 0;
    border-bottom: 1px solid color-mix(in oklab, var(--gray-950) 5%, transparent);
}

.dark .sv-row {
    border-bottom-color: color-mix(in oklab, white 8%, transparent);
}

.sv-row:last-child {
    border-bottom: 0;
}

.sv-row-label {
    flex: none;
    font-size: var(--text-sm, 0.875rem);
    color: var(--gray-500);
}

.sv-row-value {
    min-width: 0;
    font-size: var(--text-sm, 0.875rem);
    font-weight: 500;
    text-align: right;
    color: var(--gray-950);
}

.sv-row-value--mono {
    font-family: ui-monospace, "SF Mono", "Cascadia Mono", monospace;
    overflow-wrap: anywhere;
}

.sv-row-note {
    display: block;
    font-size: var(--text-xs, 0.75rem);
    font-weight: 400;
    color: var(--gray-500);
}

/* ------------------------------------------------------------------ *
 * Card actions — buttons at the foot of a card, under its rows
 * ------------------------------------------------------------------ */

/*
 * Extend and the auto-renew switch, at the bottom of the Billing card.
 *
 * A ruled top edge rather than only a gap, because the rows above it are ruled
 * too and a pair of buttons floating in whitespace under the last one reads as
 * something that fell off the bottom of the list. Ruled, it is a footer.
 *
 * `1rem` of clear air above the rule and `0.875rem` below is the asymmetry the
 * rows use — `.sv-row` pads 0.625rem either side of its own rule and
 * `.sv-card-body` closes with 0.875rem — so the rule sits closer to what it
 * separates from than to the card's own edge.
 *
 * Wrapping, because both labels grow: "Turn auto-renew off" beside "Extend"
 * fits a paired card at desktop width and does not in the single column a phone
 * gets. They wrap rather than shrink — a truncated button label is a button
 * nobody presses.
 */
.sv-card-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    margin-top: 1rem;
    padding-top: 0.875rem;
    border-top: 1px solid color-mix(in oklab, var(--gray-950) 5%, transparent);
}

.dark .sv-card-actions {
    border-top-color: color-mix(in oklab, white 8%, transparent);
}

.dark .sv-row-label,
.dark .sv-row-note {
    color: var(--gray-400);
}

.dark .sv-row-value {
    color: var(--gray-50);
}

/* A tone on a value, for the one row that can be bad news: a due date that has
   already passed. Declared after the dark override above so it survives it. */
.sv-row-value--danger {
    color: var(--danger-600);
}

.sv-row-value--success {
    color: var(--success-600);
}

/* Neither bad news nor good: a fact that is legitimate but wants noticing. Added
   for the admin page's drift row — a service on grandfathered terms is doing
   exactly what it should, and an operator comparing this page against the plan
   page needs to be told before they conclude the panel has the wrong numbers. */
.sv-row-value--warning {
    color: var(--warning-600);
}

.dark .sv-row-value--danger {
    color: var(--danger-400);
}

.dark .sv-row-value--success {
    color: var(--success-400);
}

.dark .sv-row-value--warning {
    color: var(--warning-400);
}

/* ------------------------------------------------------------------ *
 * Credential field — the box the password appears in, in place
 * ------------------------------------------------------------------ */

.sv-field {
    padding: 0.625rem 0;
}

.sv-field-label {
    display: block;
    margin-bottom: 0.3125rem;
    font-size: var(--text-sm, 0.875rem);
    color: var(--gray-500);
}

.dark .sv-field-label {
    color: var(--gray-400);
}

.sv-field-box {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    padding: 0.4375rem 0.5rem 0.4375rem 0.625rem;
    border-radius: 0.625rem;
    background-color: var(--gray-50);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--gray-950) 8%, transparent);
}

.dark .sv-field-box {
    background-color: color-mix(in oklab, white 5%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, white 10%, transparent);
}

.sv-field-text {
    flex: 1 1 auto;
    min-width: 0;
    font-family: ui-monospace, "SF Mono", "Cascadia Mono", monospace;
    font-size: var(--text-sm, 0.875rem);
    font-weight: 600;
    color: var(--gray-950);
    overflow-wrap: anywhere;
}

.dark .sv-field-text {
    color: var(--gray-50);
}

/* The mask is not the password with a filter over it — there is no password in
   the page until somebody asks the server for one. It is twelve dots that stand
   in for a value the browser has never been sent. */
.sv-field-mask {
    letter-spacing: 0.15em;
    color: var(--gray-400);
}

.sv-field-actions {
    display: flex;
    flex: none;
    gap: 0.125rem;
    align-items: center;
}

.sv-icon-btn {
    display: grid;
    place-items: center;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border: 0;
    border-radius: 0.4375rem;
    cursor: pointer;
    color: var(--gray-500);
    background-color: transparent;
    transition: background-color 120ms, color 120ms;
}

.sv-icon-btn svg {
    width: 1rem;
    height: 1rem;
}

.sv-icon-btn:hover {
    color: var(--gray-700);
    background-color: color-mix(in oklab, var(--gray-950) 6%, transparent);
}

.sv-icon-btn:focus-visible {
    outline: 2px solid var(--primary-600);
    outline-offset: 1px;
}

.sv-icon-btn[disabled] {
    cursor: not-allowed;
    opacity: 0.5;
}

.dark .sv-icon-btn {
    color: var(--gray-400);
}

.dark .sv-icon-btn:hover {
    color: var(--gray-200);
    background-color: color-mix(in oklab, white 10%, transparent);
}

.sv-field-hint {
    margin-top: 0.375rem;
    font-size: var(--text-xs, 0.75rem);
    line-height: 1.125rem;
    color: var(--gray-500);
}

.dark .sv-field-hint {
    color: var(--gray-400);
}

/* ------------------------------------------------------------------ *
 * Connect command — the line a customer pastes into a terminal
 * ------------------------------------------------------------------ */

.sv-code {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    padding: 0.5rem 0.5rem 0.5rem 0.625rem;
    border-radius: 0.625rem;
    color: var(--gray-100);
    background-color: var(--gray-900);
}

.dark .sv-code {
    background-color: color-mix(in oklab, black 45%, var(--gray-900));
    box-shadow: inset 0 0 0 1px color-mix(in oklab, white 8%, transparent);
}

.sv-code-text {
    flex: 1 1 auto;
    min-width: 0;
    font-family: ui-monospace, "SF Mono", "Cascadia Mono", monospace;
    font-size: var(--text-xs, 0.75rem);
    line-height: 1.125rem;
    overflow-wrap: anywhere;
}

.sv-code-prompt {
    color: var(--gray-500);
    user-select: none;
}

.sv-code .sv-icon-btn {
    color: var(--gray-400);
}

.sv-code .sv-icon-btn:hover {
    color: white;
    background-color: color-mix(in oklab, white 12%, transparent);
}

.sv-code .sv-copied {
    color: var(--success-400);
}

/* ------------------------------------------------------------------ *
 * Network — one block per address, because an address has four facts
 * ------------------------------------------------------------------ */

.sv-nets {
    display: grid;
    gap: 0.625rem;
    padding-top: 0.625rem;
}

.sv-net {
    padding: 0.75rem;
    border-radius: 0.625rem;
    background-color: var(--gray-50);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--gray-950) 6%, transparent);
}

.dark .sv-net {
    background-color: color-mix(in oklab, white 4%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, white 8%, transparent);
}

.sv-net-top {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
}

.sv-net-addr {
    flex: 1 1 auto;
    min-width: 0;
    font-family: ui-monospace, "SF Mono", "Cascadia Mono", monospace;
    font-size: var(--text-sm, 0.875rem);
    font-weight: 600;
    color: var(--gray-950);
    overflow-wrap: anywhere;
}

.dark .sv-net-addr {
    color: var(--gray-50);
}

/* Gateway, mask and resolvers are reference material: needed exactly once, when
   something is being configured by hand, and noise on every other visit. So they
   are a definition grid under the address rather than four more rows in a list. */
.sv-net-facts {
    display: grid;
    gap: 0.5rem 1rem;
    grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
    margin: 0.625rem 0 0;
}

.sv-net-fact dt {
    font-size: 0.6875rem;
    font-weight: 500;
    color: var(--gray-500);
}

.sv-net-fact dd {
    margin: 0.0625rem 0 0;
    font-family: ui-monospace, "SF Mono", "Cascadia Mono", monospace;
    font-size: var(--text-xs, 0.75rem);
    color: var(--gray-800);
    overflow-wrap: anywhere;
}

.dark .sv-net-fact dt {
    color: var(--gray-400);
}

.dark .sv-net-fact dd {
    color: var(--gray-200);
}

/* ------------------------------------------------------------------ *
 * Price headline, empty states and the notes disclosure
 * ------------------------------------------------------------------ */

.sv-price {
    display: flex;
    gap: 0.375rem;
    align-items: baseline;
    padding: 0.75rem 0 0.5rem;
}

.sv-price-amount {
    font-size: var(--text-2xl, 1.5rem);
    font-weight: 700;
    line-height: 1.75rem;
    color: var(--gray-950);
}

.sv-price-term {
    font-size: var(--text-sm, 0.875rem);
    color: var(--gray-500);
}

.dark .sv-price-amount {
    color: var(--gray-50);
}

.dark .sv-price-term {
    color: var(--gray-400);
}

.sv-empty {
    padding: 0.75rem 0;
    font-size: var(--text-sm, 0.875rem);
    color: var(--gray-500);
}

.dark .sv-empty {
    color: var(--gray-400);
}

/* Notes are the admin's, not the customer's, and are usually absent. A closed
   disclosure keeps them findable and stops an empty card taking a slot on a page
   whose job is the four things above it. */
.sv-notes {
    padding: 0.25rem 1rem 0.875rem;
}

.sv-notes summary {
    display: flex;
    gap: 0.375rem;
    align-items: center;
    padding: 0.5rem 0;
    font-size: var(--text-sm, 0.875rem);
    font-weight: 500;
    cursor: pointer;
    color: var(--gray-600);
    list-style: none;
}

.sv-notes summary::-webkit-details-marker {
    display: none;
}

.sv-notes summary:hover {
    color: var(--gray-950);
}

.sv-notes-caret {
    width: 0.875rem;
    height: 0.875rem;
    transition: transform 150ms;
}

.sv-notes[open] .sv-notes-caret {
    transform: rotate(90deg);
}

.dark .sv-notes summary {
    color: var(--gray-400);
}

.dark .sv-notes summary:hover {
    color: var(--gray-50);
}

.sv-notes-body {
    padding: 0.25rem 0 0;
    font-size: var(--text-sm, 0.875rem);
    line-height: 1.375rem;
    white-space: pre-line;
    color: var(--gray-700);
}

.dark .sv-notes-body {
    color: var(--gray-300);
}

/* ------------------------------------------------------------------ *
 * Narrow screens
 * ------------------------------------------------------------------ */

/* A label and its value stop competing for one line and stack instead. Right
   alignment is what makes a two-column card scannable and what makes a
   single-column one look broken. */
@media (max-width: 30rem) {
    .sv-row {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.125rem;
    }

    .sv-row-value {
        text-align: left;
    }

    .sv-hero-badges {
        width: 100%;
    }
}


/*
 * The graphs on a server's page.
 *
 * Drawn by Apache ECharts into the card below; see resources/js/server-charts.js
 * and App\Filament\Concerns\ServerMetricChart. Everything here is the card
 * around the canvas — the canvas itself is painted in JavaScript because a
 * stylesheet cannot reach inside one.
 *
 * Built on Filament's own tokens (`--gray-*`, `--primary-*`) so it follows the
 * panel's theme and its dark mode rather than carrying a second palette.
 */

.panel-metric-card {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    height: 100%;
    padding: 1.125rem 1.25rem 1rem;
    border-radius: 0.75rem;
    background-color: #fff;
    box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.04);
    outline: 1px solid rgb(9 9 11 / 0.08);
    outline-offset: -1px;
}

.dark .panel-metric-card {
    background-color: rgb(24 24 27);
    outline-color: rgb(255 255 255 / 0.08);
}

/* -- Heading ------------------------------------------------------------- */

.panel-metric-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
}

.panel-metric-titles {
    min-width: 0;
}

.panel-metric-title {
    font-size: 0.75rem;
    line-height: 1rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--gray-500, #71717a);
}

/*
 * The figure somebody came to read, large, and in tabular numerals so a value
 * that updates every second does not jitter sideways as its digits change width.
 */
.panel-metric-value {
    margin-top: 0.25rem;
    font-size: 1.375rem;
    line-height: 1.75rem;
    font-weight: 650;
    letter-spacing: -0.01em;
    font-variant-numeric: tabular-nums;
    color: var(--gray-950, #09090b);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.dark .panel-metric-value {
    color: #fff;
}

.panel-metric-tools {
    display: flex;
    flex-shrink: 0;
    align-items: center;
    gap: 0.25rem;
}

.panel-metric-tool {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: 0.5rem;
    color: var(--gray-400, #a1a1aa);
    transition: background-color 100ms, color 100ms;
}

.panel-metric-tool:hover,
.panel-metric-tool:focus-visible {
    color: var(--gray-700, #3f3f46);
    background-color: var(--gray-100, #f4f4f5);
}

.dark .panel-metric-tool:hover,
.dark .panel-metric-tool:focus-visible {
    color: var(--gray-200, #e4e4e7);
    background-color: rgb(255 255 255 / 0.06);
}

.panel-metric-tool .fi-icon,
.panel-metric-tool svg {
    width: 1.125rem;
    height: 1.125rem;
}

.panel-metric-reset {
    padding: 0.25rem 0.625rem;
    border-radius: 0.5rem;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--primary-600, #2563eb);
    background-color: var(--primary-50, #eff6ff);
}

.dark .panel-metric-reset {
    color: var(--primary-400, #60a5fa);
    background-color: rgb(96 165 250 / 0.1);
}

/* -- Range picker -------------------------------------------------------- */

/*
 * Its own row, under the heading rather than beside it. Beside it, six buttons
 * took the width the heading needed and folded "Network · 0 bit/s out" into a
 * column one word wide — which is what this card looked like before.
 */
.panel-metric-ranges {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem;
}

.panel-metric-range {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.25rem 0.625rem;
    border-radius: 9999px;
    font-size: 0.75rem;
    line-height: 1rem;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    color: var(--gray-500, #71717a);
    box-shadow: inset 0 0 0 1px rgb(9 9 11 / 0.08);
    transition: background-color 100ms, color 100ms, box-shadow 100ms;
}

.panel-metric-range:hover {
    color: var(--gray-900, #18181b);
}

.panel-metric-range.is-on {
    color: #fff;
    background-color: var(--primary-600, #2563eb);
    box-shadow: none;
}

.dark .panel-metric-range {
    color: var(--gray-400, #a1a1aa);
    box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.1);
}

.dark .panel-metric-range:hover {
    color: #fff;
}

.dark .panel-metric-range.is-on {
    color: var(--gray-950, #09090b);
    background-color: var(--primary-400, #60a5fa);
}

/* The live range says it is live, the way every monitoring tool does. */
.panel-metric-live-dot {
    width: 0.4375rem;
    height: 0.4375rem;
    border-radius: 9999px;
    background-color: #22c55e;
    animation: panel-metric-pulse 1.8s infinite;
}

.panel-metric-range.is-on .panel-metric-live-dot {
    background-color: currentColor;
}

@keyframes panel-metric-pulse {
    0% { box-shadow: 0 0 0 0 rgb(34 197 94 / 0.55); }
    70% { box-shadow: 0 0 0 0.375rem rgb(34 197 94 / 0); }
    100% { box-shadow: 0 0 0 0 rgb(34 197 94 / 0); }
}

@media (prefers-reduced-motion: reduce) {
    .panel-metric-live-dot {
        animation: none;
    }
}

.panel-metric-busy {
    width: 0.75rem;
    height: 0.75rem;
    margin-left: 0.25rem;
    border-radius: 9999px;
    border: 2px solid var(--gray-300, #d4d4d8);
    border-top-color: transparent;
    animation: panel-metric-spin 0.7s linear infinite;
}

@keyframes panel-metric-spin {
    to { transform: rotate(360deg); }
}

/* -- Chart --------------------------------------------------------------- */

.panel-metric-stage {
    position: relative;
    flex: 1 1 auto;
    min-height: 15rem;
}

.panel-metric-canvas {
    position: absolute;
    inset: 0;
}

.panel-metric-empty {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 1rem;
    text-align: center;
    font-size: 0.8125rem;
    color: var(--gray-500, #71717a);
    border-radius: 0.5rem;
    background-image: repeating-linear-gradient(135deg, transparent 0 10px, rgb(9 9 11 / 0.025) 10px 20px);
}

.dark .panel-metric-empty {
    color: var(--gray-400, #a1a1aa);
    background-image: repeating-linear-gradient(135deg, transparent 0 10px, rgb(255 255 255 / 0.025) 10px 20px);
}

.panel-metric-empty-icon {
    width: 1.75rem;
    height: 1.75rem;
    opacity: 0.6;
}

/* -- Legend with figures ------------------------------------------------- */

.panel-metric-legend {
    width: 100%;
    font-size: 0.75rem;
    line-height: 1rem;
    font-variant-numeric: tabular-nums;
    border-collapse: collapse;
}

.panel-metric-legend th,
.panel-metric-legend td {
    padding: 0.3125rem 0.5rem;
    text-align: right;
    white-space: nowrap;
}

.panel-metric-legend thead th {
    font-weight: 500;
    color: var(--gray-400, #a1a1aa);
}

.panel-metric-legend thead th:first-child,
.panel-metric-legend tbody th {
    /* The name takes the slack, so the figures sit together on the right
       rather than being spread across the whole width of the card. */
    width: 100%;
}

.panel-metric-legend td,
.panel-metric-legend thead th:not(:first-child) {
    min-width: 6.5rem;
}

.panel-metric-legend tbody th {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    text-align: left;
    font-weight: 500;
    color: var(--gray-700, #3f3f46);
}

.panel-metric-legend th:first-child,
.panel-metric-legend td:first-child {
    padding-left: 0;
}

.panel-metric-legend th:last-child,
.panel-metric-legend td:last-child {
    padding-right: 0;
}

.panel-metric-legend tbody td {
    color: var(--gray-950, #09090b);
    font-weight: 500;
}

.panel-metric-legend tbody tr {
    cursor: pointer;
    border-top: 1px solid rgb(9 9 11 / 0.06);
}

.panel-metric-legend tbody tr.is-off {
    opacity: 0.4;
}

.dark .panel-metric-legend tbody th {
    color: var(--gray-300, #d4d4d8);
}

.dark .panel-metric-legend tbody td {
    color: #fff;
}

.dark .panel-metric-legend tbody tr {
    border-top-color: rgb(255 255 255 / 0.06);
}

.panel-metric-swatch {
    flex-shrink: 0;
    width: 0.625rem;
    height: 0.625rem;
    border-radius: 0.1875rem;
}

.panel-metric-caption {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 0.25rem 1rem;
    font-size: 0.75rem;
    line-height: 1rem;
    color: var(--gray-500, #71717a);
}

.panel-metric-hint {
    color: var(--gray-400, #a1a1aa);
}

/* -- Full screen --------------------------------------------------------- */

/*
 * The whole card goes full screen — heading, figure, ranges and legend come
 * with it, because a naked canvas on a black backdrop has lost the only labels
 * that say what it is. The background has to be restated: the fullscreen
 * backdrop is black by default.
 */
.panel-metric-card:fullscreen,
.panel-metric-card.is-expanded {
    padding: 2rem 2.5rem;
    border-radius: 0;
    outline: none;
    background-color: #fff;
}

/*
 * The fallback for a browser that will not go full screen — iPhone Safari, or
 * a page inside a frame. The card covers the window instead, above Filament's
 * topbar and sidebar, and Escape puts it back.
 */
.panel-metric-card.is-expanded {
    position: fixed;
    inset: 0;
    z-index: 60;
    height: 100dvh;
    overflow: auto;
}

.panel-metric-locked,
.panel-metric-locked body {
    overflow: hidden;
}

.dark .panel-metric-card:fullscreen,
.dark .panel-metric-card.is-expanded {
    background-color: rgb(9 9 11);
}

.panel-metric-card:fullscreen .panel-metric-value,
.panel-metric-card.is-expanded .panel-metric-value {
    font-size: 2rem;
    line-height: 2.5rem;
}

.panel-metric-card:fullscreen .panel-metric-legend,
.panel-metric-card.is-expanded .panel-metric-legend {
    font-size: 0.875rem;
}

/* ------------------------------------------------------------------ *
 * The build card — what a customer watches while their server is made
 *
 * See resources/views/filament/client/services/build.blade.php. Built on the
 * same tokens as the cards below it, so it reads as the top of this page rather
 * than as something dropped onto it.
 * ------------------------------------------------------------------ */

.sv-build {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.25rem;
    align-items: center;
    padding: 1.25rem;
    /* The picture's light spills towards the words and stops at the card's
       own rounded edge. */
    overflow: hidden;
    border-radius: var(--radius-xl, 0.75rem);
    background:
        radial-gradient(120% 90% at 0% 0%, color-mix(in oklab, var(--primary-500) 10%, transparent), transparent 60%),
        white;
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--primary-500) 22%, transparent);
}

.dark .sv-build {
    background:
        radial-gradient(120% 90% at 0% 0%, color-mix(in oklab, var(--primary-500) 16%, transparent), transparent 60%),
        var(--gray-900);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--primary-400) 25%, transparent);
}

@media (min-width: 768px) {
    .sv-build {
        grid-template-columns: minmax(0, 17rem) minmax(0, 1fr);
        gap: 2rem;
        padding: 1.5rem 2rem 1.5rem 1.5rem;
    }
}

/*
 * The picture is an `<x-illustration>` standing in its own light — see
 * resources/css/illustration.css. This only sizes it within the card; the
 * margin gives the light room before the card's edge.
 */
.sv-build-stage {
    width: 100%;
    max-width: 17rem;
    margin: 0.75rem auto;
}

.sv-build-body {
    display: flex;
    min-width: 0;
    flex-direction: column;
    gap: 0.625rem;
}

.sv-build-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--primary-600);
}

.dark .sv-build-eyebrow {
    color: var(--primary-400);
}

.sv-build-pulse {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 9999px;
    background-color: currentColor;
    animation: sv-build-pulse 1.6s ease-out infinite;
}

@keyframes sv-build-pulse {
    0% { box-shadow: 0 0 0 0 color-mix(in oklab, currentColor 55%, transparent); }
    75% { box-shadow: 0 0 0 0.45rem color-mix(in oklab, currentColor 0%, transparent); }
    100% { box-shadow: 0 0 0 0 transparent; }
}

.sv-build-title {
    font-size: 1.375rem;
    line-height: 1.75rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--gray-950);
}

.dark .sv-build-title {
    color: white;
}

.sv-build-now {
    font-size: 0.9375rem;
    color: var(--gray-600);
}

.dark .sv-build-now {
    color: var(--gray-300);
}

/* -- The bar ---------------------------------------------------------- */

.sv-build-meter {
    position: relative;
    height: 0.5rem;
    margin-top: 0.25rem;
    border-radius: 9999px;
    background-color: color-mix(in oklab, var(--primary-500) 14%, transparent);
    overflow: hidden;
}

.sv-build-meter-fill {
    position: absolute;
    inset: 0 auto 0 0;
    border-radius: inherit;
    /* Keeps the sheen below to the filled part. Without it the highlight slides
       on across the empty track, which reads as progress that is not there. */
    overflow: hidden;
    background: linear-gradient(90deg, var(--primary-500), var(--primary-400));
    transition: width 900ms cubic-bezier(0.22, 1, 0.36, 1);
}

/*
 * A sheen across the filled part, so the bar looks alive between the polls that
 * move it. Without it a stretch that takes two minutes — copying the disk —
 * leaves a bar that has not visibly moved, and a bar that does not move is
 * what "stuck" looks like.
 */
.sv-build-meter-fill::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, rgb(255 255 255 / 0.45), transparent);
    transform: translateX(-100%);
    animation: sv-build-sheen 1.8s ease-in-out infinite;
}

@keyframes sv-build-sheen {
    to { transform: translateX(100%); }
}

.sv-build-figures {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 1rem;
    font-size: 0.8125rem;
    color: var(--gray-500);
    font-variant-numeric: tabular-nums;
}

.dark .sv-build-figures {
    color: var(--gray-400);
}

.sv-build-percent {
    font-weight: 700;
    color: var(--primary-600);
}

.dark .sv-build-percent {
    color: var(--primary-400);
}

.sv-build-elapsed {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}

.sv-build-inline-icon {
    width: 0.875rem;
    height: 0.875rem;
}

.sv-build-typical {
    margin-left: auto;
}

@media (max-width: 639px) {
    .sv-build-typical {
        margin-left: 0;
        flex-basis: 100%;
    }
}

/* -- The stages ------------------------------------------------------- */

.sv-build-phases {
    display: grid;
    gap: 0.5rem;
    margin-top: 0.375rem;
}

.sv-build-phase {
    display: flex;
    align-items: flex-start;
    gap: 0.625rem;
    font-size: 0.875rem;
    color: var(--gray-400);
}

.sv-build-dot {
    position: relative;
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.25rem;
    margin-top: 0.0625rem;
    border-radius: 9999px;
    box-shadow: inset 0 0 0 1.5px color-mix(in oklab, var(--gray-400) 60%, transparent);
}

.sv-build-dot-icon {
    width: 0.8125rem;
    height: 0.8125rem;
}

.sv-build-phase--done {
    color: var(--gray-600);
}

.sv-build-phase--done .sv-build-dot {
    color: white;
    background-color: var(--success-500);
    box-shadow: none;
}

/* The one in hand: a ring that turns, which is the smallest honest "working". */
.sv-build-phase--current {
    color: var(--gray-950);
    font-weight: 600;
}

.sv-build-phase--current .sv-build-dot {
    box-shadow: inset 0 0 0 2px color-mix(in oklab, var(--primary-500) 25%, transparent);
}

.sv-build-phase--current .sv-build-dot::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    border: 2px solid transparent;
    border-top-color: var(--primary-500);
    animation: sv-build-spin 0.9s linear infinite;
}

@keyframes sv-build-spin {
    to { transform: rotate(360deg); }
}

.dark .sv-build-phase--done {
    color: var(--gray-300);
}

.dark .sv-build-phase--current {
    color: white;
}

.sv-build-phase-text {
    display: flex;
    min-width: 0;
    flex-direction: column;
}

.sv-build-phase-detail {
    font-size: 0.8125rem;
    font-weight: 400;
    color: var(--gray-500);
}

.dark .sv-build-phase-detail {
    color: var(--gray-400);
}

.sv-build-note {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    margin-top: 0.5rem;
    padding: 0.75rem 0.875rem;
    border-radius: 0.625rem;
    font-size: 0.8125rem;
    line-height: 1.25rem;
    color: var(--gray-600);
    background-color: color-mix(in oklab, var(--gray-500) 7%, transparent);
}

.dark .sv-build-note {
    color: var(--gray-300);
    background-color: rgb(255 255 255 / 0.05);
}

.sv-build-note-icon {
    flex: none;
    width: 1rem;
    height: 1rem;
    margin-top: 0.125rem;
    color: var(--gray-400);
}

@media (prefers-reduced-motion: reduce) {
    .sv-build-pulse,
    .sv-build-meter-fill::after,
    .sv-build-phase--current .sv-build-dot::after {
        animation: none;
    }

    .sv-build-meter-fill {
        transition: none;
    }
}
