/* =====================================================================
   VPN SaaS - stylesheet
   Single file, no build step. Light + dark via prefers-color-scheme.
   ===================================================================== */

:root {
    --bg: #0d1117;
    --bg-elevated: #161b22;
    --bg-soft: #1c2128;
    --border: #30363d;
    --text: #e6edf3;
    --text-dim: #9198a1;
    --accent: #2f81f7;
    --accent-hover: #4a94ff;
    --ok: #3fb950;
    --warn: #d29922;
    --danger: #f85149;
    --radius: 10px;
    --radius-sm: 6px;
    --shadow: 0 4px 16px rgba(0, 0, 0, .35);
    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    --sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

@media (prefers-color-scheme: light) {
    :root {
        --bg: #f6f8fa;
        --bg-elevated: #ffffff;
        --bg-soft: #eef1f4;
        --border: #d0d7de;
        --text: #1f2328;
        --text-dim: #59636e;
        --shadow: 0 2px 10px rgba(31, 35, 40, .10);
    }
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--bg);
    color: var(--text);
    font-family: var(--sans);
    font-size: 15px;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3, h4 { line-height: 1.25; margin: 0 0 .5em; font-weight: 600; }
h1 { font-size: 1.9rem; letter-spacing: -.02em; }
h2 { font-size: 1.35rem; }
h3 { font-size: 1.05rem; }
p { margin: 0 0 1em; }
code, pre, .mono { font-family: var(--mono); font-size: .875em; }

pre {
    background: var(--bg-soft);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    overflow-x: auto;
    white-space: pre-wrap;
    word-break: break-all;
}

/* ---------- layout ---------- */
.wrap { max-width: 1120px; margin: 0 auto; padding: 0 20px; }
.wrap-narrow { max-width: 720px; margin: 0 auto; padding: 0 20px; }
.page { padding: 32px 0 64px; }
.stack > * + * { margin-top: 16px; }
.row { display: flex; gap: 16px; flex-wrap: wrap; align-items: center; }
.row-between { display: flex; gap: 16px; flex-wrap: wrap; align-items: center; justify-content: space-between; }
.grid { display: grid; gap: 16px; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.spacer { flex: 1; }
.muted { color: var(--text-dim); }
.small { font-size: .85rem; }
.tiny { font-size: .78rem; }
.center { text-align: center; }
.right { text-align: right; }
.nowrap { white-space: nowrap; }
.mt0 { margin-top: 0; } .mb0 { margin-bottom: 0; }

/* ---------- header ---------- */
.site-header {
    border-bottom: 1px solid var(--border);
    background: var(--bg-elevated);
    position: sticky;
    top: 0;
    z-index: 40;
}
.site-header .wrap { display: flex; align-items: center; gap: 20px; height: 60px; }
.brand { font-weight: 700; font-size: 1.1rem; color: var(--text); display: flex; align-items: center; gap: 8px; }
.brand:hover { text-decoration: none; }
/* The plain accent dot. STILL IN USE by the admin and auth layouts -
   admin deliberately tints it amber so a glance at the header tells you
   which side of the site you are on. Removing this rule (rather than
   just the markup that used it) blanked both of those headers. */
.brand-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 10px var(--accent); }

/* The real logo, used by the public/dashboard layout. Square box: the
   source is 376x376 and border-radius 50% only reads as a circle when
   width == height. */
.brand-logo {
    width: 50px;
    height: 50px;
    flex: 0 0 50px;
    border-radius: 50%;
    box-shadow: 0 0 10px #970fbd;
    background-image: url('../p-logo376.png');
    background-repeat: no-repeat;
    background-size: contain;
    background-position: center;
}
.nav { display: flex; gap: 18px; align-items: center; }
.nav a { color: var(--text-dim); font-size: .92rem; }
.nav a:hover, .nav a.active { color: var(--text); text-decoration: none; }

/* ---------- cards ---------- */
.card {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 20px;
}
.card + .card { margin-top: 16px; }
.card-header { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 14px; }
.card-header h2, .card-header h3 { margin: 0; }
.card-tight { padding: 14px 16px; }

.stat { background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; }
.stat .label { color: var(--text-dim); font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; }
.stat .value { font-size: 1.7rem; font-weight: 650; margin-top: 4px; letter-spacing: -.02em; }
.stat .sub { color: var(--text-dim); font-size: .8rem; }

/* ---------- buttons ---------- */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 15px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--bg-soft);
    color: var(--text);
    font-size: .9rem;
    font-family: inherit;
    font-weight: 500;
    cursor: pointer;
    transition: background .12s, border-color .12s;
    text-decoration: none;
}
.btn:hover { background: var(--border); text-decoration: none; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn-danger { background: transparent; border-color: var(--danger); color: var(--danger); }
.btn-danger:hover { background: var(--danger); color: #fff; }
.btn-ghost { background: transparent; }
.btn-sm { padding: 5px 10px; font-size: .82rem; }
.btn-block { width: 100%; justify-content: center; }
/* v2.1.3: disabled buttons must be UNMISTAKABLE. The old opacity-only
   look sat so close to the flat enabled style that LIVE buttons were
   twice reported as "greyed out" - so disabled now means washed out,
   dashed border, muted text, no hover reaction; enabled buttons keep
   their solid border and hover response. */
.btn:disabled, .btn[disabled] {
    opacity: .45;
    cursor: not-allowed;
    border-style: dashed;
    color: var(--text-dim);
    background: transparent;
}
.btn:disabled:hover, .btn[disabled]:hover { background: transparent; }
.btn-primary:disabled, .btn-primary[disabled] {
    background: var(--bg-soft);
    border-color: var(--border);
    color: var(--text-dim);
}

/* ---------- forms ---------- */
.field { margin-bottom: 14px; }
.field label { display: block; font-size: .85rem; font-weight: 500; margin-bottom: 5px; }
.field .hint { color: var(--text-dim); font-size: .78rem; margin-top: 4px; }
input[type=text], input[type=email], input[type=password], input[type=number],
input[type=url], select, textarea {
    width: 100%;
    padding: 9px 11px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-family: inherit;
    font-size: .92rem;
}
input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(47, 129, 247, .18);
}
textarea { min-height: 90px; resize: vertical; }
.check { display: flex; align-items: flex-start; gap: 9px; margin-bottom: 10px; font-size: .9rem; }
.check input { margin-top: 3px; }
.field-error { color: var(--danger); font-size: .8rem; margin-top: 4px; }
fieldset { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 14px; margin: 0 0 16px; }
legend { padding: 0 6px; font-size: .82rem; text-transform: uppercase; letter-spacing: .05em; color: var(--text-dim); }

/* ---------- tables ---------- */
.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); }
table { width: 100%; border-collapse: collapse; font-size: .9rem; }
th, td { padding: 10px 12px; text-align: left; border-bottom: 1px solid var(--border); vertical-align: middle; }
th { background: var(--bg-soft); font-weight: 600; font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: var(--text-dim); }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--bg-soft); }

/* ---------- badges ---------- */
.badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: .74rem;
    font-weight: 600;
    border: 1px solid var(--border);
    background: var(--bg-soft);
    color: var(--text-dim);
    white-space: nowrap;
}
.badge-ok { color: var(--ok); border-color: rgba(63, 185, 80, .4); background: rgba(63, 185, 80, .12); }
.badge-warn { color: var(--warn); border-color: rgba(210, 153, 34, .4); background: rgba(210, 153, 34, .12); }
.badge-danger { color: var(--danger); border-color: rgba(248, 81, 73, .4); background: rgba(248, 81, 73, .12); }
.badge-accent { color: var(--accent); border-color: rgba(47, 129, 247, .4); background: rgba(47, 129, 247, .12); }

/* ---------- alerts ---------- */
.alert {
    padding: 11px 14px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--bg-soft);
    margin-bottom: 16px;
    font-size: .9rem;
}
.alert-success { border-color: rgba(63, 185, 80, .45); background: rgba(63, 185, 80, .10); }
.alert-error { border-color: rgba(248, 81, 73, .45); background: rgba(248, 81, 73, .10); }
.alert-info { border-color: rgba(47, 129, 247, .45); background: rgba(47, 129, 247, .10); }
.alert-warning { border-color: rgba(210, 153, 34, .45); background: rgba(210, 153, 34, .10); }


/* ---------- connection banner (v2.2.15) ----------
   "Are you inside one of our tunnels?", on every page. It sits above the
   content on every layout, so it is deliberately ONE LINE and quiet -
   a full alert box was right when it appeared on the home page alone and
   would shout over the actual content everywhere else.
   It wraps to two lines on narrow screens rather than truncating: the
   address is the part people want to read, so it must not be the part
   that gets cut off. */
.conn-banner {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 4px 10px;
    padding: 8px 13px;
    margin-bottom: 14px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg-soft);
    font-size: .85rem;
    line-height: 1.45;
}
.conn-banner.is-on  { border-color: rgba(63, 185, 80, .40); background: rgba(63, 185, 80, .09); }
.conn-banner.is-off { border-color: rgba(210, 153, 34, .40); background: rgba(210, 153, 34, .09); }
.conn-icon { font-size: 1rem; line-height: 1; align-self: center; }
.conn-main { font-weight: 600; }
.conn-banner.is-on  .conn-main { color: var(--ok); }
.conn-banner.is-off .conn-main { color: var(--warn); }
.conn-sub { color: var(--text-dim); }
.conn-sub .mono { color: var(--text); }
/* ---------- hero / marketing ---------- */
.hero { padding: 70px 0 50px; text-align: center; }
/* Same mark and glow as the header, at hero size. Square, because
   border-radius 50% only reads as a circle when width == height. */
.hero-logo {
    display: block;
    width: 112px;
    height: 112px;
    margin: 0 auto 20px;
    border-radius: 50%;
    box-shadow: 0 0 24px rgba(151, 15, 189, .45);
}
.hero h1 { font-size: 2.7rem; margin-bottom: .3em; letter-spacing: -.03em; }
.hero p.lead { font-size: 1.12rem; color: var(--text-dim); max-width: 620px; margin: 0 auto 28px; }
.feature { padding: 18px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-elevated); }
.feature h3 { margin-bottom: 6px; }
.feature p { margin: 0; color: var(--text-dim); font-size: .9rem; }

.plan-card {
    border: 1px solid var(--border);
    background: var(--bg-elevated);
    border-radius: var(--radius);
    padding: 22px;
    display: flex;
    flex-direction: column;
}
.plan-card.featured { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.plan-price { font-size: 2.1rem; font-weight: 700; letter-spacing: -.03em; }
.plan-price span { font-size: .9rem; font-weight: 400; color: var(--text-dim); }
.plan-features { list-style: none; padding: 0; margin: 16px 0; flex: 1; }
.plan-features li { padding: 5px 0 5px 22px; position: relative; font-size: .9rem; }
.plan-features li::before { content: "✓"; position: absolute; left: 0; color: var(--ok); font-weight: 700; }

/* ---------- dashboard shell ---------- */
.shell { display: grid; grid-template-columns: 216px 1fr; gap: 26px; align-items: start; }
@media (max-width: 860px) { .shell { grid-template-columns: 1fr; } }
.sidebar {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 10px;
    position: sticky;
    top: 76px;
}
.sidebar a {
    display: block;
    padding: 8px 11px;
    border-radius: var(--radius-sm);
    color: var(--text-dim);
    font-size: .9rem;
}
.sidebar a:hover { background: var(--bg-soft); color: var(--text); text-decoration: none; }
.sidebar a.active { background: var(--accent); color: #fff; }
.sidebar .section { padding: 12px 11px 5px; font-size: .7rem; text-transform: uppercase; letter-spacing: .07em; color: var(--text-dim); }

/* ---------- misc ---------- */
.load-bar { height: 6px; background: var(--bg-soft); border-radius: 3px; overflow: hidden; min-width: 70px; }
.load-bar i { display: block; height: 100%; background: var(--ok); }
.load-bar i.warn { background: var(--warn); }
.load-bar i.hot { background: var(--danger); }

.copybox { display: flex; gap: 8px; align-items: stretch; }
.copybox pre { flex: 1; margin: 0; }

.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; background: var(--text-dim); }
.dot-online { background: var(--ok); box-shadow: 0 0 6px var(--ok); }
.dot-offline { background: var(--danger); }
.dot-warn { background: var(--warn); }

.log {
    background: #0a0d12;
    color: #c9d1d9;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 12px;
    max-height: 400px;
    overflow: auto;
    font-family: var(--mono);
    font-size: .78rem;
    white-space: pre-wrap;
}

.site-footer {
    border-top: 1px solid var(--border);
    padding: 26px 0;
    color: var(--text-dim);
    font-size: .85rem;
    margin-top: 50px;
}
.site-footer a { color: var(--text-dim); }

.auth-shell { min-height: 100vh; display: grid; place-items: center; padding: 32px 16px; }
.auth-card { width: 100%; max-width: 400px; }

.sparkline { display: flex; align-items: flex-end; gap: 2px; height: 46px; }
.sparkline i { flex: 1; background: var(--accent); border-radius: 2px 2px 0 0; min-height: 2px; opacity: .8; }

/* v2.2.31: SVG sparklines for throughput/clients (Network page, server
   detail). The <svg> is drawn by spark_svg() in PHP and redrawn by
   live-status.js; this only sizes and colours it. */
.spark { display: block; width: 100%; height: 32px; margin-top: 8px; }
.spark.spark-sm { width: 90px; height: 14px; margin-top: 0; }
.spark svg { display: block; width: 100%; height: 100%; overflow: visible; }
.spark polyline { fill: none; stroke: var(--accent); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.spark.spark-out polyline { stroke: var(--ok); }
.spark.spark-clients polyline { stroke: var(--warn); }
.rate { font-variant-numeric: tabular-nums; white-space: nowrap; }

details summary { cursor: pointer; padding: 6px 0; font-weight: 500; }
hr { border: none; border-top: 1px solid var(--border); margin: 20px 0; }

@media (max-width: 640px) {
    .hero h1 { font-size: 2rem; }
    /* Shrinks with the heading, so the hero does not become mostly logo
       on a phone. */
    .hero-logo { width: 80px; height: 80px; margin-bottom: 14px; }
    .site-header .wrap { gap: 12px; }
    .nav { gap: 12px; overflow-x: auto; }
}

/* =====================================================================
   Live-status badge.
   Shows what the auto-refresh poller is doing, next to a live table's
   heading. Written by /assets/js/live-status.js, which sets the text and
   swaps the state class:
     .live-live   green  - polling normally ("Live")
     .live-paused grey   - tab in the background ("Paused")
     .live-error  red    - something is wrong; the text says what
   If JavaScript never runs at all the badge keeps its server-rendered
   text ("Starting") and never turns green - which is itself the clearest
   possible signal that the script did not load.
   ===================================================================== */
.live-dot {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: .75rem;
    line-height: 1;
    padding: 4px 9px;
    border-radius: 999px;
    border: 1px solid transparent;
    white-space: nowrap;
}

.live-dot::before {
    content: "";
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}

.live-live {
    color: var(--ok, #2ea043);
    border-color: color-mix(in srgb, var(--ok, #2ea043) 35%, transparent);
    background: color-mix(in srgb, var(--ok, #2ea043) 10%, transparent);
}

/* The dot gently pulses so it is obvious at a glance that the page is
   updating itself, without anything moving on screen. */
.live-live::before { animation: live-pulse 2s ease-in-out infinite; }

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

.live-paused {
    color: var(--muted, #8b949e);
    border-color: color-mix(in srgb, var(--muted, #8b949e) 30%, transparent);
    background: color-mix(in srgb, var(--muted, #8b949e) 8%, transparent);
}

.live-error {
    color: var(--danger, #f85149);
    border-color: color-mix(in srgb, var(--danger, #f85149) 35%, transparent);
    background: color-mix(in srgb, var(--danger, #f85149) 10%, transparent);
}

/* Respect a user's "reduce motion" setting - the pulse is decoration. */
@media (prefers-reduced-motion: reduce) {
    .live-live::before { animation: none; }
}

/* A location row the customer's plan does not cover. Set by the live
   updater via data-live-class="row_class", and by PHP on first paint, so
   both paths dim the row the same way. */
.row-dim { opacity: .55; }


/* =====================================================================
   downloads  (v2.3.3)
   ---------------------------------------------------------------------
   The chooser tiles and the per-system download page. Everything here
   uses the existing tokens, so it follows the light/dark switch at the
   top of this file without a second set of colours.
   ===================================================================== */

.dl-head { text-align: center; max-width: 620px; margin: 8px auto 26px; }
.dl-head h1 { margin: 0 0 8px; font-size: 1.9rem; }
.dl-head p { margin: 0; }

/* The tiles. auto-fit keeps six across on a desktop, three on a tablet
   and two on a phone without a single media query. */
.os-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 14px;
    max-width: 1080px;
    margin: 0 auto;
}

.os-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 148px;
    padding: 22px 14px 18px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg-elevated);
    color: var(--text);
    text-align: center;
    transition: border-color .12s, transform .12s, box-shadow .12s;
}
.os-tile:hover {
    border-color: var(--accent);
    transform: translateY(-2px);
    box-shadow: var(--shadow);
    text-decoration: none;
}
.os-tile-icon { color: var(--text-dim); line-height: 0; transition: color .12s; }
.os-tile:hover .os-tile-icon { color: var(--accent); }
.os-tile-name { font-weight: 600; font-size: 1rem; }
.os-tile-note { font-size: .78rem; color: var(--text-dim); }

/* "Your system", from the User-Agent. A hint, so it is a tint and a
   label - never a disabled state on the others. */
.os-tile.is-you { border-color: var(--accent); }
.os-tile.is-you .os-tile-icon { color: var(--accent); }
.os-tile-flag {
    position: absolute;
    top: 8px; right: 8px;
    font-size: .66rem;
    letter-spacing: .02em;
    padding: 2px 7px;
    border-radius: 999px;
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}

.dl-foot { max-width: 720px; margin: 26px auto 0; text-align: center; }
.dl-foot p { margin: 0 0 8px; }

/* ---------- one system's page ---------- */

.crumbs { max-width: 820px; margin: 0 auto 10px; }
.dl-page { max-width: 820px; margin: 0 auto; }

.dl-hero {
    text-align: center;
    padding: 26px 20px 24px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg-elevated);
}
.dl-hero-icon { color: var(--accent); line-height: 0; display: block; margin-bottom: 6px; }
.dl-hero-title { margin: 0 0 4px; font-size: 1.7rem; }
.dl-hero-tagline { margin: 0 0 20px; }

/* The one button the page exists for. */
.btn-download {
    font-size: 1.05rem;
    font-weight: 600;
    padding: 13px 46px;
    letter-spacing: .01em;
}
.dl-hero-file { margin: 8px 0 0; }

.dl-alt-files { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; margin-top: 12px; }

.dl-meta {
    display: flex; gap: 14px; flex-wrap: wrap; justify-content: center;
    margin-top: 16px; font-size: .86rem;
}
.dl-meta-version { color: var(--text); }

.dl-pills { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; margin-top: 14px; }
.pill {
    font-size: .8rem;
    padding: 5px 14px;
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--text-dim);
    background: var(--bg-soft);
}
.pill:hover { color: var(--text); border-color: var(--accent); text-decoration: none; }

.dl-requires {
    display: flex; flex-wrap: wrap; gap: 8px;
    justify-content: center;
    margin: 16px 0 0;
}
.dl-require {
    font-size: .8rem;
    color: var(--text-dim);
    background: var(--bg-soft);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 5px 11px;
}
.dl-require code { font-size: .95em; }

/* The "your OS will warn you" box - the point of the page. Warm border
   so it reads as "expected, here is what to do", not as an error. */
.dl-warn {
    margin-top: 20px;
    border-color: color-mix(in srgb, var(--warn) 45%, var(--border));
}
.dl-warn h3 { color: var(--warn); }
.dl-warn-body { margin-top: 12px; line-height: 1.6; }

/* A quiet reproduction of what the operating system actually says, so
   the sentence on screen matches the sentence in the box. Deliberately
   NOT a screenshot: no vendor artwork to ship, and it stays readable at
   any size and in both themes. */
.dl-warn-screen {
    margin-top: 14px;
    padding: 14px 16px;
    border-radius: var(--radius-sm);
    background: var(--bg-soft);
    border-left: 3px solid var(--warn);
}
.dl-warn-screen-title { font-weight: 600; font-size: .95rem; margin-bottom: 4px; }
.dl-warn-screen-body { font-size: .85rem; color: var(--text-dim); }

.dl-steps { line-height: 1.7; padding-left: 20px; }
.dl-steps li { margin-bottom: 6px; }

.dl-hash {
    margin: 6px 0 0;
    padding: 10px 12px;
    background: var(--bg-soft);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-family: var(--mono);
    font-size: .78rem;
    word-break: break-all;
    white-space: pre-wrap;
}

.dl-section-title { margin: 28px 0 10px; font-size: 1.25rem; }
.alt-card h4 { margin-bottom: 6px; }
.alt-card p { margin: 0 0 10px; line-height: 1.55; }


/* =====================================================================
   one server  (v2.3.4)
   ---------------------------------------------------------------------
   /servers/{slug}: identity, capacity, the crypto in plain sight, and a
   traffic history drawn as inline SVG from the node's own heartbeats.
   ===================================================================== */
/* ---------- one server ---------- */

.srv-page { max-width: 900px; margin: 0 auto; }

.srv-head {
    display: flex; gap: 16px; flex-wrap: wrap;
    align-items: flex-start; justify-content: space-between;
    margin-bottom: 18px;
}
.srv-name { margin: 0 0 4px; font-size: 1.75rem; display: flex; align-items: center; gap: 10px; }
.srv-head-right { display: flex; gap: 8px; flex-wrap: wrap; }

/* The four numbers worth seeing without reading anything. */
.srv-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 12px;
    margin-bottom: 18px;
}
.srv-stat {
    padding: 14px 16px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg-elevated);
}
.srv-stat-label {
    display: block;
    font-size: .72rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-dim);
    margin-bottom: 4px;
}
.srv-stat-value { display: block; font-size: 1.5rem; font-weight: 600; line-height: 1.2; }
.srv-stat-of { font-size: .9rem; font-weight: 400; color: var(--text-dim); }
.srv-stat-bar { margin-top: 8px; }

/* The facts table: a narrow right-aligned label column, like a spec
   sheet, so the values line up and scan vertically. */
.srv-facts th {
    width: 34%;
    text-align: right;
    color: var(--text-dim);
    font-weight: 500;
    white-space: nowrap;
    vertical-align: top;
    padding-right: 14px;
}
.srv-facts td { vertical-align: top; }
.srv-facts code { word-break: break-all; }

/* Cipher chips. The first data-channel one is what a current client
   actually negotiates, so it is the one that is coloured. */
.cipher-list { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.cipher {
    font-family: var(--mono);
    font-size: .74rem;
    padding: 4px 9px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg-soft);
    color: var(--text-dim);
    white-space: nowrap;
}
.cipher-first {
    color: var(--accent);
    border-color: color-mix(in srgb, var(--accent) 40%, transparent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

/* Traffic. The SVG scales to the card with preserveAspectRatio="none",
   so the polylines need vector-effect to keep an even stroke width. */
.srv-chart {
    margin-top: 12px;
    height: 200px;
    background: var(--bg-soft);
    border-radius: var(--radius-sm);
    overflow: hidden;
}
.srv-chart svg { width: 100%; height: 100%; display: block; }
.srv-chart polyline {
    fill: none;
    stroke-width: 1.6;
    stroke-linejoin: round;
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
}
.srv-chart-rx { stroke: var(--accent); }
.srv-chart-tx { stroke: var(--ok); }

.srv-chart-legend { display: flex; gap: 18px; flex-wrap: wrap; margin-top: 10px; }
.srv-chart-legend .key {
    display: inline-block;
    width: 10px; height: 10px;
    border-radius: 2px;
    margin-right: 6px;
}
.key-rx { background: var(--accent); }
.key-tx { background: var(--ok); }

/* ---------------------------------------------------------------------
   v2.4.10: crypto payment status - the site-wide one-liner (.pay-banner)
   and the full view on the billing return page (.pay-status). Same
   palette as .conn-banner and .alert so it reads as part of the site.
   --------------------------------------------------------------------- */
.pay-banner {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    margin: 0 0 12px; padding: 8px 12px;
    border: 1px solid var(--border); border-radius: 8px;
    font-size: .9rem;
}
.pay-banner.is-info { border-color: rgba(47, 129, 247, .40); background: rgba(47, 129, 247, .09); }
.pay-banner.is-ok   { border-color: rgba(63, 185, 80, .40);  background: rgba(63, 185, 80, .09); }
.pay-banner.is-warn { border-color: rgba(210, 153, 34, .40); background: rgba(210, 153, 34, .09); }
.pay-icon { font-size: 1rem; line-height: 1; }
.pay-main { font-weight: 600; }
.pay-sub  { color: var(--text-dim); font-size: .85rem; }
.pay-link { margin-left: auto; white-space: nowrap; }

.pay-status { text-align: left; }
.pay-headline { font-size: 1.05rem; font-weight: 600; margin: 14px 0 6px; }
.pay-steps {
    list-style: none; margin: 0 0 8px; padding: 0;
    display: flex; gap: 6px; flex-wrap: wrap;
}
.pay-steps li {
    flex: 1 1 140px; padding: 8px 10px;
    border: 1px solid var(--border); border-radius: 8px;
    font-size: .85rem; color: var(--text-dim);
}
.pay-steps li.is-done   { border-color: rgba(63, 185, 80, .40);  background: rgba(63, 185, 80, .10);  color: var(--ok); }
.pay-steps li.is-active { border-color: rgba(47, 129, 247, .45); background: rgba(47, 129, 247, .10); color: var(--text); font-weight: 600; }
.pay-confs { font-weight: 600; margin: 6px 0 2px; }
