/* =========================================================================
   console.css -- the signed-in surface's design system.

   WHY THIS EXISTS

   There were five design systems on this site and they were the same one.
   vps.css, web.css, ssl.css, ms.css and game.css each opened with a :root
   block of eleven custom properties, and on 2026-09-21 all five blocks were
   byte-identical apart from the prefix:

       --vps-good  --web-good  --ssl-good  --ms-good  --game-good   #87c5a4
       --vps-line  --web-line  --ssl-line  --ms-line  --game-line   rgba(255,255,255,.13)

   Five names for one value is the same defect CLAUDE.md describes for prices
   -- "the last time a price existed twice on this site, the quote said GBP 65
   and the invoice said GBP 89". A colour cannot produce a wrong invoice, but
   it produces the thing a customer actually perceives: five pages that are
   almost, not quite, the same product. Changing "running" green means finding
   five declarations, and missing one is invisible to everybody who does not
   open both pages side by side.

   This file holds the canonical set ONCE, under --vg-. Nothing here renames
   or deletes the five existing blocks: that is a separate change touching
   five stylesheets and every rule in them, and it is not what this file is
   for. What it does is give new work one place to read a value from, and it
   gives tests/console-css-test.php a canonical set to assert the other five
   against, so the next divergence fails CI instead of shipping.

   THE OTHER HALF: WHITE CARDS ON A DARK PAGE

   The theme's ground is dark and its default text colour is white. The
   dashboard draws white cards on it, so every element inside one has to
   restate a dark colour or it inherits white and renders invisible. That is
   not hypothetical -- dashboard.php carries four separate comments about it
   ("renders white-on-white", "both variants need restating"), and
   Command-palette.md records the same family costing a screenshot to find.

   Meanwhile vps.css, game.css and web.css already draw DARK translucent
   panels on the same ground, and have none of those bugs, because a panel
   that is a wash over the page inherits the page's own text colour and is
   correct by default.

   So the dashboard was the odd one out, not the product pages. The panel
   language below is the product pages', promoted to a shared component. It
   removes the bug class rather than documenting it again.

   SCOPE. Everything visual is under .vg-console, set on <body>. Loading this
   file changes nothing on its own; a page opts in. That makes the console
   restyle reversible by deleting one class, which is what a pilot needs.
   The :root block is global because custom properties have to be -- they
   resolve where they are USED, and a token declared inside .vg-console is
   unreadable to a rule outside it.
   ========================================================================= */

:root {
    /* ---- Ground -------------------------------------------------------
       NEUTRAL, not the theme's #242943. That navy is a decorative choice
       from a portfolio template; a console reads as an instrument when its
       ground is achromatic and the only saturated things on screen are the
       ones carrying meaning -- a status, a link, a warning.

       Applied under .vg-console only. main.css and assets/sass/libs/_vars.scss
       still say #242943 and are deliberately untouched: main.css is compiled
       output from the sass, editing it diverges the two, and recolouring the
       theme properly means rebuilding the sass and re-checking every page in
       a browser. That is a separate change from this one. */
    --vg-ground:        #1b1d21;
    --vg-ground-raised: #212429;

    /* ---- Surfaces -----------------------------------------------------
       A wash over the ground, not an opaque fill. Translucency is why these
       need no text colour of their own: contrast is inherited and stays
       correct if the ground ever moves. Values match the product sheets'
       --vps-panel / --game-panel exactly. */
    --vg-panel:         rgba(255, 255, 255, 0.045);
    --vg-panel-hover:   rgba(255, 255, 255, 0.075);
    --vg-panel-sunken:  rgba(0, 0, 0, 0.16);

    /* ---- Lines --------------------------------------------------------- */
    --vg-line:          rgba(255, 255, 255, 0.13);
    --vg-line-strong:   rgba(255, 255, 255, 0.30);

    /* ---- Text ---------------------------------------------------------
       --vg-text is not pure white. #fff on a dark ground at small sizes
       haloes, and the console's body size is small by design. */
    --vg-text:          #e8e8ea;
    --vg-muted:         #9a9aa2;
    --vg-faint:         rgba(255, 255, 255, 0.42);

    /* ---- Accent -------------------------------------------------------
       #4cc2ff rather than the theme's #9bf1ff. The pale cyan is a highlight
       colour: at 4.5:1 it is legible, but it reads as decoration next to the
       blue every cloud console uses for an actionable link. */
    --vg-accent:        #4cc2ff;
    --vg-accent-deep:   #2b9fe0;
    --vg-accent-wash:   rgba(76, 194, 255, 0.12);

    /* ---- Status -------------------------------------------------------
       Slightly desaturated from the product sheets' #87c5a4 / #e7b788 /
       #ec8d81. On a neutral ground the originals read as pastel; these keep
       the same hues and hold their meaning without competing with the accent.

       All three are stated as a foreground and a wash, because a status is
       drawn twice on this site -- as a pill, and as a border on a banner --
       and those two were picking their own opacities per page. */
    --vg-good:          #6fbf8b;
    --vg-good-wash:     rgba(111, 191, 139, 0.14);
    --vg-warn:          #e0ae76;
    --vg-warn-wash:     rgba(224, 174, 118, 0.14);
    --vg-bad:           #e07f75;
    --vg-bad-wash:      rgba(224, 127, 117, 0.14);
    --vg-info:          var(--vg-accent);
    --vg-info-wash:     var(--vg-accent-wash);

    /* ---- Geometry -----------------------------------------------------
       4px, against the product sheets' 10px. Radius is one of the two or
       three things that separate "app" from "brochure" at a glance: a large
       radius reads as friendly and marketing, a small one as dense and
       operational. Azure, AWS and GCP consoles all sit at 2-4px. */
    --vg-radius:        4px;
    --vg-radius-lg:     6px;

    /* ---- Type ---------------------------------------------------------
       The theme's body text is around 17px. A console is a reading surface
       for tables, not prose, and 17px in a table of eight columns forces
       either a scroll or a truncation. 13.5px is Azure's portal size.

       These are rem-based so a visitor's own browser setting still scales
       them; a px-locked console is an accessibility failure that nobody who
       builds it ever sees. */
    --vg-font:          0.845rem;   /* ~13.5px at a 16px root */
    --vg-font-sm:       0.78rem;
    --vg-font-xs:       0.70rem;
    --vg-line-height:   1.5;

    /* ---- Spacing ------------------------------------------------------
       A 4px scale. Named by size rather than by use, because the same gap
       is a card's padding on one page and a toolbar's gutter on another,
       and a token called --vg-card-padding invites a second one the day
       something is not a card. */
    --vg-space-1:       0.25rem;
    --vg-space-2:       0.5rem;
    --vg-space-3:       0.75rem;
    --vg-space-4:       1rem;
    --vg-space-5:       1.5rem;
    --vg-space-6:       2rem;
}

/* =========================================================================
   THE CONSOLE SURFACE
   ========================================================================= */

body.vg-console {
    background: var(--vg-ground);
    color: var(--vg-text);
    font-size: var(--vg-font);
    line-height: var(--vg-line-height);
}

/* The theme's chrome is painted from its own palette, so a console ground
   without these leaves a navy header and footer bracketing a grey page --
   which looks like a rendering fault rather than a design. Restated here,
   inside the opt-in scope, so main.css keeps its own values for every page
   that has not opted in. */
body.vg-console #header,
body.vg-console #footer,
body.vg-console #menu {
    background: var(--vg-ground-raised);
}

body.vg-console #main,
body.vg-console #main.alt {
    background: transparent;
}

/* Forty centres its content in a column sized for prose. A resource table is
   not prose, and the whole reason to have a console is to see more of your
   estate at once. */
body.vg-console #main .inner {
    max-width: 1480px;
}

/* The theme underlines every <a> and hangs a dotted border beneath it. In a
   table of forty resource names that is forty underlines, and the page reads
   as a wall of links rather than a list of things. Links inside the console
   get the accent and underline on hover only -- the same decision vps.css
   records making for its section strip. */
body.vg-console a {
    color: var(--vg-accent);
    border-bottom: 0;
    text-decoration: none;
}

body.vg-console a:hover {
    color: var(--vg-accent);
    text-decoration: underline;
}

/* =========================================================================
   PANEL -- the one card

   Replaces dashboard.php's white `.vg-stat`/`.vg-invoice` cards and the
   per-product .vps-card / .game-card / .web-card.
   ========================================================================= */

.vg-panel {
    background: var(--vg-panel);
    border: 1px solid var(--vg-line);
    border-radius: var(--vg-radius);
    padding: var(--vg-space-4) var(--vg-space-5);
}

/* Only where a panel is a target. A hover state on a panel that does nothing
   tells the customer it is clickable, and they click it. */
a.vg-panel:hover,
.vg-panel.is-interactive:hover {
    background: var(--vg-panel-hover);
    border-color: var(--vg-line-strong);
    text-decoration: none;
}

.vg-panel-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--vg-space-3);
    margin-bottom: var(--vg-space-3);
}

.vg-panel-title {
    font-size: var(--vg-font);
    font-weight: 600;
    color: var(--vg-text);
    margin: 0;
}

/* =========================================================================
   PILL -- the one status

   Every product invents this. The dashboard has .vg-pill with light-on-white
   colours; the product sheets each have their own --*-good/warn/bad. One
   component, and the modifier says MEANING (is-ok) not APPEARANCE (is-green),
   so a status whose colour is reconsidered does not need its markup changed.
   ========================================================================= */

.vg-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--vg-space-2);
    padding: 0.15em 0.6em;
    border-radius: var(--vg-radius);
    border: 1px solid transparent;
    font-size: var(--vg-font-xs);
    font-weight: 600;
    letter-spacing: 0.04em;
    line-height: 1.7;
    white-space: nowrap;
    vertical-align: middle;
}

/* NOT uppercase, and this is the one place it is worth saying why. main.css
   styles every button and label on this site in spaced capitals, and the
   dashboard's old pill followed it. Capitals cost roughly 12% width and are
   measurably slower to read; in a status column that is a column you cannot
   narrow. A status is read hundreds of times a session -- it is the wrong
   place to spend width on emphasis. */
.vg-pill.is-ok   { background: var(--vg-good-wash); color: var(--vg-good); border-color: var(--vg-good-wash); }
.vg-pill.is-warn { background: var(--vg-warn-wash); color: var(--vg-warn); border-color: var(--vg-warn-wash); }
.vg-pill.is-bad  { background: var(--vg-bad-wash);  color: var(--vg-bad);  border-color: var(--vg-bad-wash); }
.vg-pill.is-info { background: var(--vg-info-wash); color: var(--vg-info); border-color: var(--vg-info-wash); }

/* The fifth state, and the one most often missed: a resource that is neither
   healthy nor broken but simply off, or not yet built. Painting that amber
   makes every stopped server look like a problem to be fixed. */
.vg-pill.is-idle {
    background: var(--vg-panel);
    color: var(--vg-muted);
    border-color: var(--vg-line);
}

/* COLOUR IS NOT THE ONLY SIGNAL. A dot carries the state to anyone who cannot
   separate the hues, and the pill's text carries it to a screen reader. WCAG
   1.4.1. The dot is decorative to assistive tech -- the word beside it is the
   accessible name -- so it is drawn with ::before rather than markup. */
.vg-pill::before {
    content: "";
    width: 0.45em;
    height: 0.45em;
    border-radius: 50%;
    background: currentColor;
    flex: 0 0 auto;
}

/* =========================================================================
   TABLE -- the resource list

   The list of things you own is the centre of a cloud console, and the
   theme's table is styled for a pricing comparison: generous padding,
   uppercase headers, a heavy rule under every row.
   ========================================================================= */

.vg-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--vg-font);
}

.vg-table th {
    text-align: left;
    padding: var(--vg-space-2) var(--vg-space-3);
    border-bottom: 1px solid var(--vg-line-strong);
    color: var(--vg-muted);
    font-size: var(--vg-font-xs);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: none;
    white-space: nowrap;
}

.vg-table td {
    padding: var(--vg-space-3);
    border-bottom: 1px solid var(--vg-line);
    vertical-align: middle;
}

.vg-table tbody tr:hover {
    background: var(--vg-panel);
}

/* The name column is the one people scan. Everything else on the row is
   context for it. */
.vg-table .vg-cell-name {
    font-weight: 600;
    color: var(--vg-text);
}

.vg-table .vg-cell-muted {
    color: var(--vg-muted);
}

/* Numbers align right and share one glyph width, so a column of them can be
   compared by eye without reading any of them. Without tabular-nums, a "1" is
   narrower than a "7" and the decimal points do not line up. */
.vg-table .vg-cell-num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.vg-table .vg-cell-actions {
    text-align: right;
    white-space: nowrap;
}

/* =========================================================================
   EMPTY STATE

   An empty table currently renders as a header row over nothing, which reads
   as a page that failed to load rather than an account with no servers in it.
   An empty state says which it is and offers the one thing to do next.
   ========================================================================= */

.vg-empty {
    border: 1px dashed var(--vg-line);
    border-radius: var(--vg-radius);
    padding: var(--vg-space-6) var(--vg-space-5);
    text-align: center;
    color: var(--vg-muted);
}

.vg-empty-title {
    color: var(--vg-text);
    font-weight: 600;
    margin: 0 0 var(--vg-space-2);
}

.vg-empty p {
    margin: 0 auto var(--vg-space-4);
    max-width: 46ch;
}

/* =========================================================================
   BREADCRUMB

   A console is a hierarchy and the customer needs to know their depth in it.
   This site has pages three levels down -- dashboard > servers > a server >
   its firewall -- reachable only by a single "Back to dashboard" link, which
   collapses all three into one step and loses where they were.
   ========================================================================= */

.vg-breadcrumb {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--vg-space-2);
    margin: 0 0 var(--vg-space-4);
    padding: 0;
    list-style: none;
    font-size: var(--vg-font-sm);
    color: var(--vg-muted);
}

.vg-breadcrumb li {
    display: flex;
    align-items: center;
    gap: var(--vg-space-2);
    padding: 0;
}

/* The separator is generated, not markup: a "/" typed between two <li>s is
   read aloud by a screen reader as part of the link text. */
.vg-breadcrumb li + li::before {
    content: "/";
    color: var(--vg-faint);
}

.vg-breadcrumb [aria-current="page"] {
    color: var(--vg-text);
    font-weight: 600;
}

/* =========================================================================
   STAT -- the at-a-glance number
   ========================================================================= */

.vg-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: var(--vg-space-3);
    margin: 0 0 var(--vg-space-6);
}

.vg-stat {
    background: var(--vg-panel);
    border: 1px solid var(--vg-line);
    border-radius: var(--vg-radius);
    padding: var(--vg-space-4);
    display: flex;
    flex-direction: column;
    gap: var(--vg-space-1);
}

.vg-stat-label {
    font-size: var(--vg-font-xs);
    font-weight: 600;
    color: var(--vg-muted);
    letter-spacing: 0.04em;
}

.vg-stat-value {
    font-size: 1.6rem;
    font-weight: 600;
    line-height: 1.15;
    color: var(--vg-text);
    font-variant-numeric: tabular-nums;
}

.vg-stat-value.is-ok   { color: var(--vg-good); }
.vg-stat-value.is-warn { color: var(--vg-warn); }
.vg-stat-value.is-bad  { color: var(--vg-bad); }

.vg-stat-foot {
    font-size: var(--vg-font-sm);
}

/* =========================================================================
   NOTICE -- the one message

   Replaces .vg-card-warning and .vg-status-banner in dashboard.php, which
   that file's own comment already identifies as "a third near-identical
   block -- only the colour differs".
   ========================================================================= */

.vg-notice {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--vg-space-3) var(--vg-space-4);
    margin: 0 0 var(--vg-space-5);
    padding: var(--vg-space-3) var(--vg-space-4);
    border: 1px solid var(--vg-line);
    border-left: 3px solid var(--vg-muted);
    border-radius: var(--vg-radius);
    background: var(--vg-panel);
    /* status.post-message's content field accepts newlines (up to 2000
       chars); pre-line keeps them without needing nl2br() on escaped output.
       Carried over from .vg-status-banner, where it was load-bearing. */
    white-space: pre-line;
}

.vg-notice strong {
    display: block;
    width: 100%;
    color: inherit;
}

.vg-notice.is-info { border-left-color: var(--vg-info); background: var(--vg-info-wash); }
.vg-notice.is-warn { border-left-color: var(--vg-warn); background: var(--vg-warn-wash); }
.vg-notice.is-bad  { border-left-color: var(--vg-bad);  background: var(--vg-bad-wash); }

/* The notice's trailing action. .button comes from main.css and is 2.75em
   tall, uppercase and letter-spaced -- the call-to-action styling
   Command-palette.md records having to undo for the same reason. A banner's
   action is secondary to its text and must not out-shout it. */
.vg-notice .button {
    margin: 0;
    flex: 0 0 auto;
    height: 2.2em;
    line-height: 2.2em;
    padding: 0 1.1em;
    font-size: var(--vg-font-sm);
    letter-spacing: 0;
    text-transform: none;
}

/* =========================================================================
   SECTION HEADING
   ========================================================================= */

.vg-section-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--vg-space-3);
    margin: var(--vg-space-6) 0 var(--vg-space-4);
    padding-bottom: var(--vg-space-2);
    border-bottom: 1px solid var(--vg-line);
}

.vg-section-head:first-child {
    margin-top: 0;
}

.vg-section-head h2,
.vg-section-head h3 {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
    color: var(--vg-text);
}

.vg-section-sub {
    color: var(--vg-muted);
    font-size: var(--vg-font-sm);
}

/* =========================================================================
   REDUCED MOTION

   Somebody who has asked their operating system to stop animations has
   usually done it because animation makes them ill.
   ========================================================================= */

@media (prefers-reduced-motion: reduce) {
    body.vg-console *,
    body.vg-console *::before,
    body.vg-console *::after {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
    }
}
