/* ==========================================================================
   metoc.org - console stylesheet

   The look is a military tactical display, and that is a functional choice
   rather than a decorative one. Such displays are built for a room that is
   kept dark, for reading at a glance and at an angle, and for making the
   difference between nominal and not obvious without anybody having to
   interpret a chart. So:

     - A near-black ground, because a bright panel in a dark space destroys
       night vision and washes out everything drawn on it.
     - Monospace throughout, because columns of numbers must line up, and a
       figure that changes width as it updates is a figure that flickers.
     - Colour reserved for state. Green nominal, amber caution, red critical,
       cyan for measured values. Nothing is coloured to look attractive - if
       something is red on this page it is because something is wrong.
     - Thin rules and corner brackets instead of boxes and shadows, which is
       what a vector display could draw and what the idiom inherited from it.

   No external font, no external stylesheet, no image files. Everything below
   is drawn by the browser from this file.
   ========================================================================== */

/* --- Tokens --------------------------------------------------------------- */

:root {
    color-scheme: dark;

    /* Ground */
    --bg-void: #04070a;
    --bg-deep: #070c10;
    --bg-panel: #0a1116;
    --bg-raised: #0e1720;
    --bg-inset: #060b0e;

    /* Rules */
    --rule: rgba(94, 214, 160, .18);
    --rule-strong: rgba(94, 214, 160, .38);
    --rule-dim: rgba(94, 214, 160, .09);

    /* Ink */
    --ink: #d3e5dd;
    --ink-bright: #f2fff8;
    --ink-dim: #7e9a90;
    --ink-faint: #4d6660;

    /* Signal colours - these carry meaning, never decoration */
    --phosphor: #4ade8a;          /* nominal, and the house colour */
    --phosphor-dim: #2c8f58;
    --amber: #ffb340;             /* caution */
    --amber-dim: #a8712a;
    --alert: #ff4d4d;             /* critical */
    --alert-dim: #a52f2f;
    --cyan: #6fdcff;              /* measured data */
    --violet: #b58cff;            /* modelled, not measured */

    /* Severity ramp, used by the map and by every badge */
    --sev-none: #4ade8a;
    --sev-low: #6fdcff;
    --sev-medium: #ffd54a;
    --sev-high: #ff9d3d;
    --sev-critical: #ff4d4d;

    /* Map */
    --map-ocean: #050d11;
    --map-land: #0d201b;
    --map-coast: rgba(94, 214, 160, .62);
    --map-border: rgba(94, 214, 160, .2);
    --map-grid: rgba(94, 214, 160, .1);
    --map-grid-major: rgba(94, 214, 160, .3);
    --map-grid-label: rgba(126, 154, 144, .8);
    --map-terminator: #ffb340;
    --map-sun: #ffd54a;
    --map-cursor: rgba(255, 211, 74, .55);
    --map-station: #4ade8a;
    --map-satellite: #6fdcff;
    --map-event: rgba(255, 157, 61, .9);
    --map-place: rgba(198, 226, 214, .82);
    --map-place-dot: rgba(120, 220, 170, .85);
    --map-box: rgba(94, 214, 160, .9);
    --map-box-fill: rgba(94, 214, 160, .1);
    --map-hover: #f2fff8;
    --map-scale: rgba(211, 229, 221, .78);

    --mono: ui-monospace, "SF Mono", "SFMono-Regular", "JetBrains Mono",
            "IBM Plex Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace;

    --bracket: 9px;               /* size of the panel corner brackets */
}

/* A lighter variant, for reading the console in daylight. It is a change of
   ground and ink only: the signal colours keep their meaning. */
:root[data-theme="day"] {
    color-scheme: light;
    --bg-void: #d8ded9;
    --bg-deep: #e4e9e3;
    --bg-panel: #eef1ec;
    --bg-raised: #f6f8f4;
    --bg-inset: #e0e5df;
    --rule: rgba(20, 70, 50, .22);
    --rule-strong: rgba(20, 70, 50, .45);
    --rule-dim: rgba(20, 70, 50, .12);
    --ink: #10221c;
    --ink-bright: #04120c;
    --ink-dim: #3f5a50;
    --ink-faint: #6e857c;
    --phosphor: #0f7a42;
    --phosphor-dim: #0b5c32;
    --amber: #a15c00;
    --alert: #c02020;
    --cyan: #0d6a8c;
    --violet: #5b3aa8;
    --sev-none: #0f7a42;
    --sev-low: #0d6a8c;
    --sev-medium: #9a7400;
    --sev-high: #b95c00;
    --sev-critical: #c02020;
    --map-ocean: #ccd8d4;
    --map-land: #e8ede6;
    --map-coast: rgba(20, 70, 50, .65);
    --map-border: rgba(20, 70, 50, .28);
    --map-grid: rgba(20, 70, 50, .13);
    --map-grid-major: rgba(20, 70, 50, .3);
    --map-grid-label: rgba(63, 90, 80, .9);
    --map-terminator: #a15c00;
    --map-sun: #9a7400;
    --map-station: #0f7a42;
    --map-satellite: #0d6a8c;
    --map-place: rgba(16, 34, 28, .9);
    --map-place-dot: rgba(15, 122, 66, .9);
    --map-box: rgba(15, 122, 66, .9);
    --map-box-fill: rgba(15, 122, 66, .12);
    --map-hover: #04120c;
    --map-scale: rgba(16, 34, 28, .85);
}

/* --- Reset ---------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    background: var(--bg-void);
    color: var(--ink);
    font: 13px/1.5 var(--mono);
    letter-spacing: .01em;
}

h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: .08em; }
p { margin: 0 0 .8em; }
a { color: var(--cyan); text-decoration: none; border-bottom: 1px dotted currentColor; }
a:hover { color: var(--ink-bright); }
table { border-collapse: collapse; width: 100%; }
button { font: inherit; color: inherit; }

::selection { background: var(--phosphor); color: var(--bg-void); }

:focus-visible {
    outline: 1px solid var(--phosphor);
    outline-offset: 2px;
}

/* --- Layout --------------------------------------------------------------- */

.shell {
    /* The full width of the display, less a margin. A console is read at a
       glance from a distance, and capping it at a fixed width throws away the
       screen that was bought for exactly this. */
    max-width: none;
    margin: 0;
    padding: 0 18px 40px;
}

/* --- Command bar ---------------------------------------------------------- */

.commandbar {
    position: sticky;
    top: 0;
    z-index: 40;
    display: flex;
    align-items: stretch;
    gap: 0;
    background: linear-gradient(180deg, var(--bg-deep), var(--bg-panel));
    border-bottom: 1px solid var(--rule-strong);
    box-shadow: 0 1px 0 var(--rule-dim), 0 10px 30px rgba(0, 0, 0, .55);
}

.commandbar__inner {
    max-width: none;
    margin: 0;
    padding: 0 18px;
    width: 100%;
    display: flex;
    align-items: center;
    gap: 18px;
    min-height: 52px;
    flex-wrap: wrap;
}

.sigil {
    display: flex;
    align-items: center;
    gap: 10px;
    padding-right: 18px;
    border-right: 1px solid var(--rule);
    align-self: stretch;
}

.sigil__mark { width: 26px; height: 26px; flex: none; }

.sigil__text { display: flex; flex-direction: column; line-height: 1.15; }
.sigil__name {
    font-size: 15px; font-weight: 700; letter-spacing: .22em;
    color: var(--ink-bright);
}
.sigil__role {
    font-size: 8.5px; letter-spacing: .18em; color: var(--ink-faint);
    text-transform: uppercase; white-space: nowrap;
}

/*
   Aligned at the top, not centred. The zulu and local clocks carry a date line
   and the day-of-year does not, so centring each column vertically pushed the
   day number half a line below the two times it sits beside. Aligning the tops
   puts every label on one row and every value on the next.
 */
.clocks { display: flex; gap: 16px; align-items: flex-start; }

.clock { display: flex; flex-direction: column; line-height: 1.1; }
.clock__label {
    font-size: 8px; letter-spacing: .2em; color: var(--ink-faint);
    text-transform: uppercase;
}
.clock__value {
    font-size: 15px; font-variant-numeric: tabular-nums; color: var(--ink-bright);
    letter-spacing: .06em;
}
.clock--zulu .clock__value { color: var(--phosphor); }

.clock__date {
    font-size: 8.5px; letter-spacing: .14em; color: var(--ink-faint);
    text-transform: uppercase; white-space: nowrap;
}
.clock--zulu .clock__date { color: var(--phosphor-dim); }

.commandbar__spacer { flex: 1 1 auto; }

/* Overall condition lamp. */
.condition {
    --frame-fill: var(--bg-inset);
    display: flex; align-items: center; gap: 9px;
    padding: 5px 12px;
}
.condition[data-state="caution"] { --frame: var(--amber-dim); }
.condition[data-state="critical"] { --frame: var(--alert-dim); }
.condition__lamp {
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--phosphor);
    box-shadow: 0 0 8px currentColor;
    color: var(--phosphor);
    animation: pulse 2.4s ease-in-out infinite;
}
.condition[data-state="caution"] .condition__lamp { background: var(--amber); color: var(--amber); }
.condition[data-state="critical"] .condition__lamp { background: var(--alert); color: var(--alert); animation-duration: .9s; }
.condition[data-state="offline"] .condition__lamp { background: var(--ink-faint); color: var(--ink-faint); animation: none; }
.condition__text {
    font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
    color: var(--ink-dim);
}

@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

@media (prefers-reduced-motion: reduce) {
    .condition__lamp { animation: none; }
}

/* --- Framed controls ------------------------------------------------------ */
/*
   Buttons and status chips are plain rectangles with a 1 px border.

   They used to have cut corners, which looked right but could not be outlined
   cleanly: a border is drawn on the rectangle and the clip then slices its
   corners away, leaving the diagonals undrawn. Every workaround for that -
   masked rings, polygons with holes - added machinery to the stylesheet
   without ever quite looking like one continuous line. A rectangle needs none
   of it and reads just as well.

   Each control sets `--frame` for the border colour and `--frame-fill` for the
   interior, so the state variants below only have to change two values.
 */
.btn, .condition, .link {
    --frame: var(--rule);
    --frame-fill: var(--bg-raised);
    position: relative;
    background: var(--frame-fill);
    border: 1px solid var(--frame);
}

/* --- Buttons -------------------------------------------------------------- */

.btn {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 6px 12px;
    color: var(--ink-dim);
    font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
    cursor: pointer;
    transition: color .12s, background .12s;
}

.btn:hover {
    --frame: var(--rule-strong);
    --frame-fill: var(--bg-panel);
    color: var(--ink-bright);
}
.btn[aria-pressed="true"], .btn.is-active {
    --frame: var(--phosphor-dim);
    --frame-fill: color-mix(in srgb, var(--phosphor) 12%, var(--bg-raised));
    color: var(--phosphor);
}
.btn:disabled { opacity: .5; cursor: default; }
.btn--icon { padding: 6px 8px; }
.btn svg { width: 13px; height: 13px; flex: none; }

/* A control that is a menu rather than a switch, styled to sit in the same
   row as the buttons it lives beside. */
.btn--select { cursor: pointer; padding-right: 6px; }
.btn--select select {
    appearance: none;
    background: none;
    border: none;
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    cursor: pointer;
    padding: 0 14px 0 0;
    outline: none;
    /* The chevron, drawn rather than fetched. */
    background-image:
        linear-gradient(45deg, transparent 50%, currentColor 50%, currentColor 62%, transparent 62%),
        linear-gradient(-45deg, transparent 50%, currentColor 50%, currentColor 62%, transparent 62%);
    background-position: right 5px center, right 1px center;
    background-size: 4px 4px, 4px 4px;
    background-repeat: no-repeat;
}
.btn--select select option { background: var(--bg-panel); color: var(--ink); }

/* --- Tab rail ------------------------------------------------------------- */

.rail {
    display: flex;
    gap: 2px;
    margin: 14px 0 0;
    border-bottom: 1px solid var(--rule);
    /* Still scrollable on a narrow screen, but the scrollbar is hidden: it
       drew a grey bar through the tab row on every platform that renders one
       in-flow. */
    overflow-x: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.rail::-webkit-scrollbar { display: none; }

.rail__tab {
    position: relative;
    display: flex; align-items: center; gap: 9px;
    padding: 11px 18px 10px;
    background: transparent;
    border: 1px solid transparent;
    border-bottom: none;
    color: var(--ink-faint);
    font-size: 11px; letter-spacing: .16em; text-transform: uppercase;
    cursor: pointer;
    white-space: nowrap;
    transition: color .12s, background .12s;
}
.rail__tab:hover { color: var(--ink); background: var(--bg-deep); }

.rail__tab[aria-selected="true"] {
    color: var(--ink-bright);
    background: var(--bg-panel);
    border-color: var(--rule);
    border-bottom: 1px solid var(--bg-panel);
    margin-bottom: -1px;
}
.rail__tab[aria-selected="true"]::before {
    content: "";
    position: absolute; inset: -1px 0 auto; height: 2px;
    background: var(--phosphor);
    box-shadow: 0 0 10px var(--phosphor);
}

.rail__index {
    font-size: 9px; color: var(--phosphor-dim);
    font-variant-numeric: tabular-nums;
}
.rail__tab[aria-selected="true"] .rail__index { color: var(--phosphor); }

/* A count of whatever the tab is watching - storms, alerts, warnings. */
.rail__count {
    min-width: 17px; padding: 1px 4px;
    font-size: 9px; text-align: center;
    border: 1px solid var(--rule);
    color: var(--ink-dim);
    font-variant-numeric: tabular-nums;
}
.rail__count[data-state="alert"] {
    color: var(--alert); border-color: var(--alert-dim);
    background: color-mix(in srgb, var(--alert) 14%, transparent);
}
.rail__count[data-state="caution"] { color: var(--amber); border-color: var(--amber-dim); }

/* --- Panels --------------------------------------------------------------- */

.panel {
    position: relative;
    background: var(--bg-panel);
    border: 1px solid var(--rule);
    padding: 13px 15px 15px;
}

/*
   Corner brackets on all four corners, drawn as eight gradient slivers on a
   single pseudo-element: two per corner, one horizontal and one vertical.
   No extra markup, no image, and nothing to keep in sync when a panel resizes.
 */
.panel::before {
    content: "";
    position: absolute;
    inset: -1px;
    pointer-events: none;
    background:
        /* top-left */
        linear-gradient(var(--rule-strong) 0 0) left top / var(--bracket) 1px no-repeat,
        linear-gradient(var(--rule-strong) 0 0) left top / 1px var(--bracket) no-repeat,
        /* top-right */
        linear-gradient(var(--rule-strong) 0 0) right top / var(--bracket) 1px no-repeat,
        linear-gradient(var(--rule-strong) 0 0) right top / 1px var(--bracket) no-repeat,
        /* bottom-left */
        linear-gradient(var(--rule-strong) 0 0) left bottom / var(--bracket) 1px no-repeat,
        linear-gradient(var(--rule-strong) 0 0) left bottom / 1px var(--bracket) no-repeat,
        /* bottom-right */
        linear-gradient(var(--rule-strong) 0 0) right bottom / var(--bracket) 1px no-repeat,
        linear-gradient(var(--rule-strong) 0 0) right bottom / 1px var(--bracket) no-repeat;
}

.panel--accent { border-color: var(--rule-strong); }
.panel--alert {
    border-color: var(--alert-dim);
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--alert) 7%, var(--bg-panel)), var(--bg-panel));
}
.panel--alert { --bracket-colour: var(--alert); }
.panel--alert::before {
    background:
        linear-gradient(var(--alert) 0 0) left top / var(--bracket) 1px no-repeat,
        linear-gradient(var(--alert) 0 0) left top / 1px var(--bracket) no-repeat,
        linear-gradient(var(--alert) 0 0) right top / var(--bracket) 1px no-repeat,
        linear-gradient(var(--alert) 0 0) right top / 1px var(--bracket) no-repeat,
        linear-gradient(var(--alert) 0 0) left bottom / var(--bracket) 1px no-repeat,
        linear-gradient(var(--alert) 0 0) left bottom / 1px var(--bracket) no-repeat,
        linear-gradient(var(--alert) 0 0) right bottom / var(--bracket) 1px no-repeat,
        linear-gradient(var(--alert) 0 0) right bottom / 1px var(--bracket) no-repeat;
}

.panel__head {
    display: flex; align-items: baseline; gap: 10px;
    margin: -2px 0 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--rule-dim);
}
.panel__title {
    font-size: 11px; letter-spacing: .2em; text-transform: uppercase;
    color: var(--ink-bright);
}
.panel__tag {
    font-size: 8.5px; letter-spacing: .16em; text-transform: uppercase;
    color: var(--phosphor-dim);
    padding: 1px 5px; border: 1px solid var(--rule);
}
.panel__note {
    margin-left: auto;
    font-size: 9.5px; letter-spacing: .1em; color: var(--ink-faint);
    text-transform: uppercase; text-align: right;
}

/* --- Grids ---------------------------------------------------------------- */

.grid { display: grid; gap: 12px; }
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid--auto { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.grid--stats { grid-template-columns: repeat(auto-fit, minmax(158px, 1fr)); }
@media (max-width: 1100px) {
    .grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 680px) {
    .grid--2, .grid--3, .grid--4 { grid-template-columns: minmax(0, 1fr); }
}

/* --- Readouts ------------------------------------------------------------- */

.readout {
    position: relative;
    padding: 9px 11px 10px;
    background: var(--bg-inset);
    border: 1px solid var(--rule-dim);
    border-left: 2px solid var(--rule);
}
.readout[data-state="nominal"] { border-left-color: var(--phosphor); }
.readout[data-state="caution"] { border-left-color: var(--amber); }
.readout[data-state="critical"] { border-left-color: var(--alert); }
.readout[data-state="unknown"] { border-left-color: var(--ink-faint); }

.readout__label {
    display: flex; align-items: center; gap: 6px;
    font-size: 8.5px; letter-spacing: .17em; text-transform: uppercase;
    color: var(--ink-faint);
    margin-bottom: 4px;
}
.readout__value {
    font-size: 25px; line-height: 1.05; font-weight: 500;
    color: var(--ink-bright);
    font-variant-numeric: tabular-nums;
    letter-spacing: -.01em;
}
.readout__value--small { font-size: 17px; }
.readout__unit { font-size: 11px; color: var(--ink-dim); margin-left: 3px; letter-spacing: .04em; }
.readout__sub {
    margin-top: 4px;
    font-size: 9.5px; color: var(--ink-dim); letter-spacing: .06em;
}
.readout[data-state="caution"] .readout__value { color: var(--amber); }
.readout[data-state="critical"] .readout__value { color: var(--alert); }

/* Marks a number this site computed rather than received. */
.derived {
    font-size: 8px; letter-spacing: .14em; text-transform: uppercase;
    color: var(--violet); border: 1px solid currentColor;
    padding: 0 3px; margin-left: auto;
}

/* --- Meters --------------------------------------------------------------- */

.meter { margin-top: 7px; }
.meter__track {
    position: relative; height: 5px;
    background: var(--bg-void);
    border: 1px solid var(--rule-dim);
    overflow: hidden;
}
.meter__fill {
    position: absolute; inset: 0 auto 0 0;
    background: var(--phosphor);
    transition: width .35s ease-out;
}
.meter__fill[data-state="caution"] { background: var(--amber); }
.meter__fill[data-state="critical"] { background: var(--alert); }
.meter__scale {
    display: flex; justify-content: space-between;
    font-size: 8px; color: var(--ink-faint); margin-top: 3px;
    letter-spacing: .1em;
}

/* Segmented bar - discrete steps, for K index and the NOAA scales. */
.segments { display: flex; gap: 2px; margin-top: 7px; }
.segment {
    flex: 1; height: 14px;
    background: var(--bg-void);
    border: 1px solid var(--rule-dim);
}
.segment[data-on="1"] { background: var(--phosphor); border-color: var(--phosphor); }
.segment[data-on="1"][data-level="caution"] { background: var(--amber); border-color: var(--amber); }
.segment[data-on="1"][data-level="critical"] { background: var(--alert); border-color: var(--alert); }

/* --- Map plate ------------------------------------------------------------ */

.plate {
    position: relative;
    background: var(--bg-inset);
    border: 1px solid var(--rule);
}

/* Armed for a drag-to-zoom rectangle. */
.plate.is-boxing .plate__canvas { cursor: crosshair; }
.plate__canvas--overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;      /* the base canvas takes the input */
}

.plate__canvas {
    display: block; width: 100%;
    /* Equirectangular is 2:1 by construction, so a 2:1 plate shows the whole
       world with nothing left over. Any other ratio letterboxes the Earth. */
    aspect-ratio: 2 / 1;
    max-height: 72vh;
    cursor: crosshair; touch-action: none;
}
@media (max-width: 900px) { .plate__canvas { max-height: 52vh; } }

/* Corner ticks on the map frame. */
.plate__frame {
    position: absolute; inset: 0; pointer-events: none;
}
.plate__frame::before, .plate__frame::after {
    content: ""; position: absolute; width: 16px; height: 16px;
    border-color: var(--rule-strong); border-style: solid;
}
.plate__frame::before { top: 6px; left: 6px; border-width: 1px 0 0 1px; }
.plate__frame::after { bottom: 6px; right: 6px; border-width: 0 1px 1px 0; }

.plate__controls {
    position: absolute; top: 8px; right: 8px;
    display: flex; flex-direction: column; gap: 4px;
    align-items: stretch;
}
.plate__controls .btn {
    --frame-fill: color-mix(in srgb, var(--bg-void) 84%, transparent);
    justify-content: flex-start;
    padding: 5px 9px;
    gap: 8px;
}
.plate__controls .btn:hover {
    --frame-fill: color-mix(in srgb, var(--bg-void) 94%, transparent);
}
.plate__controls .btn.is-active {
    --frame-fill: color-mix(in srgb, var(--phosphor) 14%, var(--bg-void));
}
/* An icon on its own needs the reader to already know the convention. The
   label removes the guessing; it is small enough not to compete with the map. */
.plate__controls .btn__glyph {
    width: 13px; text-align: center; font-size: 12px; line-height: 1;
    color: var(--ink-dim);
}
.plate__controls .btn.is-active .btn__glyph { color: var(--phosphor); }
.plate__controls .btn__label { font-size: 9px; letter-spacing: .13em; }

@media (max-width: 720px) {
    /* No room for words next to the map on a phone. */
    .plate__controls .btn__label { display: none; }
    .plate__controls .btn { padding: 6px 8px; }
}

/*
   The legend sits under the map rather than on it. Floating inside the plate
   it covered the longitude labels along the bottom edge - and a legend that
   hides the coordinates is a poor trade for the space it saves.
 */
.plate__legend {
    display: flex; gap: 10px; flex-wrap: wrap; align-items: center;
    margin-top: 8px;
    padding: 7px 10px;
    background: var(--bg-panel);
    border: 1px solid var(--rule-dim);
    font-size: 8.5px; letter-spacing: .12em; text-transform: uppercase;
    color: var(--ink-dim);
}
.legend__key { display: inline-flex; align-items: center; gap: 5px; }

/* The key shows the symbol itself, drawn by the map's own code, not a colour
   chip standing in for it. A chip tells you what colour to look for; it does
   not tell you what shape you are looking for. */
.legend__symbol { display: block; flex: none; }
.legend__swatch { width: 9px; height: 9px; border: 1px solid currentColor; }

/* --- Coordinate readout --------------------------------------------------- */

.fix { display: grid; gap: 8px; }

.fix__row {
    display: grid;
    grid-template-columns: 74px minmax(0, 1fr);
    gap: 8px; align-items: baseline;
    padding-bottom: 7px;
    border-bottom: 1px solid var(--rule-dim);
}
.fix__row:last-child { border-bottom: none; padding-bottom: 0; }

.fix__system {
    font-size: 8.5px; letter-spacing: .15em; text-transform: uppercase;
    color: var(--ink-faint);
}
.fix__value {
    font-size: 13px; color: var(--ink-bright);
    font-variant-numeric: tabular-nums; letter-spacing: .04em;
    word-break: break-all;
}
.fix__value--grid { color: var(--phosphor); font-size: 14px; letter-spacing: .08em; }
.fix__hint { font-size: 9px; color: var(--ink-faint); letter-spacing: .06em; }

/* --- Tables --------------------------------------------------------------- */

.tablewrap { overflow-x: auto; }

table.data { font-size: 11.5px; }
table.data th {
    text-align: left; padding: 6px 9px;
    font-size: 8.5px; letter-spacing: .15em; text-transform: uppercase;
    color: var(--ink-faint); font-weight: 500;
    border-bottom: 1px solid var(--rule);
    white-space: nowrap;
}
table.data td {
    padding: 7px 9px;
    border-bottom: 1px solid var(--rule-dim);
    vertical-align: top;
}
table.data tbody tr:hover { background: var(--bg-raised); }
table.data .num { text-align: right; font-variant-numeric: tabular-nums; }
table.data .row-action { cursor: pointer; }

/* --- Badges and chips ----------------------------------------------------- */

.badge {
    display: inline-block; padding: 1px 6px;
    font-size: 9px; letter-spacing: .13em; text-transform: uppercase;
    border: 1px solid currentColor;
    color: var(--ink-dim);
    white-space: nowrap;
}
.badge[data-level="none"] { color: var(--sev-none); }
.badge[data-level="low"] { color: var(--sev-low); }
.badge[data-level="medium"] { color: var(--sev-medium); }
.badge[data-level="high"] { color: var(--sev-high); }
.badge[data-level="critical"] {
    color: var(--sev-critical);
    background: color-mix(in srgb, var(--sev-critical) 15%, transparent);
}

.chips { display: flex; flex-wrap: wrap; gap: 5px; }

/* --- Message rows --------------------------------------------------------- */

.message {
    border: 1px solid var(--rule-dim);
    border-left: 2px solid var(--ink-faint);
    background: var(--bg-inset);
    margin-bottom: 7px;
}
.message[data-level="critical"] { border-left-color: var(--alert); }
.message[data-level="high"] { border-left-color: var(--sev-high); }
.message[data-level="medium"] { border-left-color: var(--amber); }
.message[data-level="low"] { border-left-color: var(--cyan); }

.message__head {
    display: flex; align-items: center; gap: 9px;
    padding: 8px 11px;
    cursor: pointer;
    list-style: none;
}
.message__head::-webkit-details-marker { display: none; }
.message__head:hover { background: var(--bg-raised); }

.message__title {
    font-size: 11.5px; color: var(--ink-bright);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.message__meta {
    margin-left: auto; font-size: 9px; color: var(--ink-faint);
    letter-spacing: .1em; white-space: nowrap;
}
.message__body {
    padding: 0 11px 11px 13px;
    font-size: 11px; color: var(--ink-dim);
    white-space: pre-wrap; word-break: break-word;
    max-height: 320px; overflow-y: auto;
    line-height: 1.55;
}

/* --- Forms ---------------------------------------------------------------- */

.controls {
    display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end;
    margin-bottom: 12px;
}

.field { display: flex; flex-direction: column; gap: 3px; }
.field__label {
    font-size: 8.5px; letter-spacing: .16em; text-transform: uppercase;
    color: var(--ink-faint);
}
.field input, .field select {
    font: 12px var(--mono);
    padding: 5px 8px;
    background: var(--bg-inset);
    border: 1px solid var(--rule);
    color: var(--ink-bright);
    letter-spacing: .04em;
    min-width: 0;
}
.field input:focus, .field select:focus {
    outline: none; border-color: var(--phosphor); background: var(--bg-void);
}
.field input[aria-invalid="true"] { border-color: var(--alert); }

/* --- Charts --------------------------------------------------------------- */

.chart { width: 100%; display: block; }
.chart text { font-family: var(--mono); font-size: 9px; fill: var(--ink-faint); }
.chart .axis { stroke: var(--rule); stroke-width: 1; }
.chart .gridline { stroke: var(--rule-dim); stroke-width: 1; }

.legend {
    display: flex; flex-wrap: wrap; gap: 12px;
    margin-top: 9px; font-size: 9px; letter-spacing: .1em;
    color: var(--ink-faint); text-transform: uppercase;
}

/* --- Feed state indicator ------------------------------------------------- */
/*
   A lamp on the row it describes. This replaced a separate status strip, which
   meant reading a name in one panel and then hunting for the same name in the
   table below to find out what it was.
 */
.feeddot {
    display: inline-block;
    width: 7px; height: 7px;
    margin-right: 7px;
    border-radius: 50%;
    background: var(--ink-faint);
    vertical-align: baseline;
}
.feeddot[data-state="ok"] { background: var(--phosphor); }
.feeddot[data-state="cold"] { background: var(--ink-faint); }
.feeddot[data-state="stale"] { background: var(--amber); }
.feeddot[data-state="degraded"] { background: var(--sev-high); }
.feeddot[data-state="down"] {
    background: var(--alert);
    box-shadow: 0 0 6px var(--alert);
}

/* --- Utility -------------------------------------------------------------- */

.muted { color: var(--ink-dim); }
.faint { color: var(--ink-faint); }
.small { font-size: 10px; }
.tiny { font-size: 9px; letter-spacing: .1em; }
.upper { text-transform: uppercase; letter-spacing: .14em; }
.num { font-variant-numeric: tabular-nums; }
.nowrap { white-space: nowrap; }
.stack { display: flex; flex-direction: column; gap: 12px; }
.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.spacer { flex: 1 1 auto; }
.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

.empty {
    padding: 22px 14px; text-align: center;
    color: var(--ink-faint); font-size: 10.5px;
    letter-spacing: .14em; text-transform: uppercase;
}

/* --- Panels that are switched by the rail --------------------------------- */

.view { display: none; }
.view.is-active { display: block; }

/* --- Footer --------------------------------------------------------------- */

.colophon {
    margin-top: 26px; padding: 18px 0 0;
    border-top: 1px solid var(--rule);
    font-size: 10.5px; color: var(--ink-dim); line-height: 1.7;
}
.colophon h2 {
    font-size: 10px; letter-spacing: .2em; text-transform: uppercase;
    color: var(--ink-dim); margin-bottom: 8px;
}
.colophon ul { margin: 0 0 14px; padding-left: 16px; }
.colophon li { margin-bottom: 4px; }

/* --- Banner --------------------------------------------------------------- */

.banner {
    margin: 12px 0 0;
    padding: 9px 13px;
    border: 1px solid var(--amber-dim);
    border-left: 3px solid var(--amber);
    background: color-mix(in srgb, var(--amber) 8%, var(--bg-panel));
    color: var(--ink);
    font-size: 11px;
}
.banner[data-level="critical"] {
    border-color: var(--alert-dim); border-left-color: var(--alert);
    background: color-mix(in srgb, var(--alert) 9%, var(--bg-panel));
}

/* --- Print ---------------------------------------------------------------- */

@media print {
    body { background: #fff; color: #000; }
    .commandbar, .rail, .plate__controls { display: none; }
    .view { display: block !important; }
    .panel { border-color: #999; break-inside: avoid; }
}

/* --- Compatibility tokens for charts.js ----------------------------------- */
/*
   charts.js draws with the token names from the dashboard this console grew
   out of. That code is verified and there is no reason to disturb it, so the
   old names are aliased onto the palette above instead. Anything added from
   here on should use the signal colours directly.
 */
:root, :root[data-theme="day"] {
    --series-1: var(--cyan);
    --series-2: var(--amber);
    --series-3: var(--phosphor);
    --series-4: var(--sev-medium);
    --series-5: var(--violet);
    --series-6: var(--phosphor-dim);
    --series-7: var(--violet);
    --series-8: var(--alert);

    --good: var(--phosphor);
    --warning: var(--amber);
    --serious: var(--sev-high);
    --critical: var(--alert);

    --border: var(--rule);
    --border-strong: var(--rule-strong);
    --surface-1: var(--bg-panel);
    --surface-2: var(--bg-raised);
    --text-primary: var(--ink-bright);
    --text-secondary: var(--ink);
    --text-muted: var(--ink-dim);

    --seq-250: color-mix(in srgb, var(--cyan) 45%, var(--bg-void));
    --seq-550: var(--cyan);
}

/* --- Separator ------------------------------------------------------------ */
/*
   A thin vertical rule between two values in the same line. A middle dot is
   too easy to mistake for a decimal point or a piece of the data when the
   values either side are already full of dots and digits.
 */
.sep {
    display: inline-block;
    width: 1px;
    height: 1em;
    margin: 0 .7em;
    background: var(--rule-strong);
    vertical-align: -.15em;
}

/* --- Area of interest ----------------------------------------------------- */
/*
   Shown when the map is zoomed in and the panels below have been narrowed to
   what is on screen. The bar exists so that a short list is never mistaken for
   a quiet day: if events are being hidden, the page says so.
 */
.aoi {
    display: flex; align-items: center; gap: 10px;
    margin: 12px 0 0;
    padding: 7px 12px;
    border: 1px solid var(--rule);
    border-left: 2px solid var(--phosphor);
    background: var(--bg-panel);
    font-size: 10.5px; letter-spacing: .08em;
    color: var(--ink-dim);
}
.aoi__lamp {
    width: 6px; height: 6px; flex: none;
    background: var(--phosphor);
    box-shadow: 0 0 6px var(--phosphor);
}
.aoi strong { color: var(--ink-bright); }

/* --- Connection indicator ------------------------------------------------- */
/*
   Replaces the Refresh button. A button implies the page cannot tell when its
   data changed; this says whether it is being told.

   One lamp, not a bar graph. There is no signal strength to report here - the
   link is either carrying updates or it is not - and drawing bars would be
   inventing a measurement that does not exist.
 */
.link {
    --frame-fill: var(--bg-inset);
    display: inline-flex; align-items: center; gap: 8px;
    padding: 5px 11px;
}
.link[data-state="live"] { --frame: var(--phosphor-dim); }
.link[data-state="connecting"] { --frame: var(--amber-dim); }
.link[data-state="offline"] { --frame: var(--alert-dim); }
.link[data-state="polling"] { --frame: var(--rule-strong); }

.link__lamp {
    width: 9px; height: 9px; border-radius: 50%;
    background: var(--ink-faint);
    box-shadow: 0 0 7px currentColor;
    color: var(--ink-faint);
    flex: none;
}

.link__text {
    font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
    color: var(--ink-dim);
}

.link[data-state="live"] .link__lamp { background: var(--phosphor); color: var(--phosphor); }
.link[data-state="live"] .link__text { color: var(--phosphor); }

.link[data-state="connecting"] .link__lamp { background: var(--amber); color: var(--amber); }
.link[data-state="connecting"] .link__text { color: var(--amber); }
.link[data-state="connecting"] .link__lamp { animation: pulse 1.1s ease-in-out infinite; }

.link[data-state="offline"] .link__lamp { background: var(--alert); color: var(--alert); }
.link[data-state="offline"] .link__text { color: var(--alert); }

.link[data-state="polling"] .link__lamp { background: var(--cyan); color: var(--cyan); }
.link[data-state="polling"] .link__text { color: var(--cyan); }

@media (prefers-reduced-motion: reduce) {
    .link[data-state="connecting"] .link__lamp { animation: none; }
}

/* A brief flash on a panel whose data has just been replaced, so an update
   that arrives while you are reading is noticed rather than silently swapped
   under your eyes. */
@keyframes refreshed {
    from { border-color: var(--phosphor); }
    to { border-color: var(--rule); }
}
.panel.is-refreshed { animation: refreshed 1.4s ease-out; }
@media (prefers-reduced-motion: reduce) {
    .panel.is-refreshed { animation: none; }
}

/* --- Map tooltip ---------------------------------------------------------- */
/*
   What the symbol under the pointer is. Follows the cursor, never takes the
   pointer, and flips to the other side of the cursor near the right edge so it
   cannot be pushed off the plate.
 */
.maptip {
    position: absolute;
    z-index: 5;
    max-width: 300px;
    padding: 7px 10px;
    background: color-mix(in srgb, var(--bg-void) 94%, transparent);
    border: 1px solid var(--rule-strong);
    box-shadow: 0 6px 20px rgba(0, 0, 0, .5);
    pointer-events: none;
    font-size: 10.5px;
    line-height: 1.5;
    color: var(--ink);
}
.maptip[hidden] { display: none; }

.maptip__head {
    display: flex; align-items: baseline; gap: 7px;
    margin-bottom: 3px;
}
.maptip__title {
    font-size: 11px; color: var(--ink-bright); letter-spacing: .04em;
}
.maptip__kind {
    font-size: 8px; letter-spacing: .16em; text-transform: uppercase;
    color: var(--phosphor-dim); border: 1px solid var(--rule); padding: 0 4px;
}
.maptip__row { display: flex; gap: 8px; }
.maptip__key { color: var(--ink-faint); min-width: 74px; }
.maptip__value { color: var(--ink); font-variant-numeric: tabular-nums; }
.maptip__note { color: var(--ink-faint); margin-top: 4px; font-size: 9.5px; }

/* --- Legend keys as switches ---------------------------------------------- */
/*
   Each key turns its layer on and off. A legend that only explains symbols
   answers "what is that"; one that also switches them answers "show me only
   that", which on a plot carrying a dozen overlays is the more useful
   question.
 */
.legend__key {
    background: none;
    border: none;
    padding: 2px 3px;
    margin: 0;
    font: inherit;
    color: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    cursor: pointer;
    transition: opacity .12s, color .12s;
}
.legend__key:hover { color: var(--ink-bright); }

/* Off: dimmed. The symbol beside it dims with the label, which reads as
   "switched off" without putting a line through the name. */
.legend__key[aria-pressed="false"] {
    opacity: .34;
}

.legend__key:focus-visible { outline: 1px solid var(--phosphor); outline-offset: 1px; }

/* The scale is a readout, not a switch. */
#map-scale {
    margin-left: auto;
    cursor: default;
    text-align: right;
}
#map-scale:hover { color: var(--ink-dim); }

/* --- Footer version ------------------------------------------------------- */
/*
   The running version, read from /api/health rather than baked into the page,
   so it names the code that is actually answering rather than the code that
   was current when the HTML was written.
 */
.colophon__version {
    margin: 14px 0 0;
    padding-top: 12px;
    border-top: 1px solid var(--rule-dim);
    font-size: 9.5px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--ink-faint);
}
#app-version { color: var(--phosphor-dim); font-variant-numeric: tabular-nums; }

/* --- Small screens -------------------------------------------------------- */
/*
   A phone is not a narrow desktop. The console's habit of layering things over
   the map - a legend of fourteen keys, a column of controls - works because a
   desktop plot is a thousand pixels wide with room to spare. At 375 pixels the
   legend wraps to six rows and covers the map completely.

   So on a small screen the legend comes out of the plate and sits under it as
   a scrolling row, the map is given real height instead of a 2:1 sliver, and
   the readouts are tightened. Nothing is removed: every layer can still be
   switched, every coordinate is still shown.
 */
@media (max-width: 720px) {

    .shell { padding: 0 10px 32px; }

    /* Let the strapline wrap rather than run off the edge. */
    .sigil { padding-right: 12px; }
    .sigil__role { white-space: normal; line-height: 1.35; }

    .commandbar__inner { gap: 10px; padding: 6px 10px; }
    .clocks { gap: 12px; flex-wrap: wrap; }

    /* A usable map, rather than a 187-pixel-tall strip. The world no longer
       fills it edge to edge at full zoom-out, which is a fair trade for being
       able to see and touch anything. */
    .plate__canvas {
        aspect-ratio: auto;
        height: 46vh;
        min-height: 260px;
        max-height: none;
    }

    /* One scrolling row rather than four wrapped ones, which on a phone
       would take more height than the map. */
    .plate__legend {
        margin-top: 6px;
        padding: 6px 4px;
        border: none;
        background: none;
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        -ms-overflow-style: none;
    }
    .plate__legend::-webkit-scrollbar { display: none; }

    .legend__key { flex: none; padding: 3px 5px; }

    /* It scrolls with the keys instead of being pinned right, which has no
       meaning in a scrolling row. */
    #map-scale { margin-left: 4px; }

    /* Icon-only controls, tucked tighter into the corner. */
    .plate__controls { top: 5px; right: 5px; gap: 3px; }
    .plate__controls .btn { padding: 7px 8px; }

    /* Coordinates: same information, less of the screen. */
    .fix { gap: 5px; }
    .fix__row {
        grid-template-columns: 68px minmax(0, 1fr);
        gap: 6px;
        padding-bottom: 5px;
    }
    .fix__value { font-size: 12px; }
    .fix__value--grid { font-size: 12.5px; }

    .panel { padding: 10px 11px 12px; }
    .readout__value { font-size: 21px; }

    /* Two columns of readouts at 375px is two columns of nothing. */
    .grid--stats { grid-template-columns: minmax(0, 1fr); }
    .grid--auto { grid-template-columns: minmax(0, 1fr); }

    .rail__tab { padding: 10px 12px 9px; }
}

/*
   Touch devices have no pointer to hover with, so the map's cursor readout
   fills in on tap instead. The wording is swapped in app.js; this just keeps
   the hint from claiming a cursor that does not exist.
 */
@media (hover: none) {
    .plate__canvas { cursor: default; }
}
