/* Deliberately the same palette and idiom as the on-node host UI
   (edge/web/static/style.css) — one project, two audiences. */
:root{
  --bg:#0e1116;--panel:#171c24;--border:#21262d;--fg:#e6edf3;--muted:#7d8794;
  --ok:#3fb950;--stale:#d29922;--err:#f85149;--line:#58a6ff;
}
*{box-sizing:border-box}
body{margin:0;font:14px/1.5 system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--fg)}
header{display:flex;align-items:baseline;gap:14px;padding:12px 20px;border-bottom:1px solid var(--border);background:var(--panel)}
.brand{font-weight:600}
.brand a{color:var(--fg);text-decoration:none}
.mono{font-family:ui-monospace,monospace}
.crumbs{font-size:13px}
.crumbs a{text-decoration:none}

/* ── landing — the one public page ────────────────────────────────────────
   Still says nothing an unauthenticated visitor should not learn (no node id,
   no site, no imagery from the rig). The photograph is decorative and lives
   in CSS, so the page reads identically with images off. */
.landing-body{margin:0;min-height:100svh;display:flex;flex-direction:column;
              justify-content:space-between;overflow:hidden}

/* `fixed` rather than a body background: it pins the photo to the viewport, so
   the mobile URL bar collapsing cannot make the image jump. `100svh` above is
   the same defence for height. */
.landing-bg{position:fixed;inset:0;z-index:-2;background:#05070c
            url("img/earth-limb.jpg") center 58% / cover no-repeat}

/* Legibility must not depend on the photograph. `cover` crops differently at
   every aspect ratio, so which pixels sit behind the title is not knowable —
   the scrim guarantees contrast at any crop, darkest at the top where the
   text is and easing off over the Earth so the city lights stay visible. */
.landing-scrim{position:fixed;inset:0;z-index:-1;
  background:linear-gradient(180deg,
    rgba(3,5,10,.88) 0%, rgba(3,5,10,.72) 26%,
    rgba(3,5,10,.34) 48%, rgba(3,5,10,.12) 64%, rgba(3,5,10,.42) 100%)}

/* A halo that travels with the text. The page-wide scrim cannot carry
   legibility on its own: `cover` crops differently at every aspect ratio, so
   in landscape the content lands on the city lights while in portrait it sits
   on black sky. Measured the hard way at 1440x820, where the copy was sitting
   directly on Atlanta. This is anchored to the copy rather than to the
   viewport, so it is correct at any crop — and tight, because the block is
   now just a title and a button. */
.landing{max-width:600px;margin:0 auto;text-align:center;
         padding:clamp(64px,14vh,180px) 24px clamp(40px,7vh,90px);
         background:radial-gradient(46% 58% at 50% 40%,
           rgba(3,5,10,.78) 0%, rgba(3,5,10,.56) 44%,
           rgba(3,5,10,.22) 72%, rgba(3,5,10,0) 100%)}
.landing h1{font-size:clamp(32px,6.6vw,54px);margin:0;font-weight:600;
            letter-spacing:.014em;text-shadow:0 2px 24px rgba(0,0,0,.8)}

/* Glass rather than the flat panel fill the rest of the UI uses: a solid chip
   punches a hole in the photograph, and this is the one page that has one. */
.button{display:inline-block;margin-top:26px;padding:11px 26px;border-radius:9px;
        border:1px solid rgba(255,255,255,.30);background:rgba(255,255,255,.09);
        -webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);
        color:var(--fg);text-decoration:none;font-size:15px;
        transition:background .18s ease,border-color .18s ease}
.button:hover,.button:focus-visible{background:rgba(255,255,255,.18);
                                    border-color:rgba(255,255,255,.72)}

.landing-credit{padding:0 24px 18px;text-align:center;font-size:11px;
                line-height:1.6;color:rgba(230,237,243,.34);
                text-shadow:0 1px 10px rgba(0,0,0,.75)}
.nowrap{white-space:nowrap}

@media (max-width:640px){
  /* Portrait crops hard into the middle of a 3:2 frame. Biasing the focal
     point upward keeps the limb and the airglow band in shot instead of
     filling the screen with city lights. */
  .landing-bg{background-position:center 44%}
  .landing{padding-top:15vh}
}

/* /nodes — one card per node, the whole card a link */
.node-card{display:flex;flex-direction:column;gap:9px;padding:14px;
           background:var(--panel);border:1px solid var(--border);
           border-radius:10px;text-decoration:none;color:var(--fg)}
.node-card:hover{border-color:var(--line)}
.node-card-head .id{font-size:15px;font-weight:600}
.node-card .site{font-size:12px;margin-top:-3px}
.node-card-legs{display:flex;flex-wrap:wrap;gap:6px}

/* the node page's header strip: position + the surface trio */
.conditions{display:flex;flex-wrap:wrap;gap:8px;margin:4px 0 6px}
/* auto-FILL, not auto-fit: with one node on the bench, auto-fit collapses the
   empty tracks and stretches the single card across the whole page, which
   reads as a banner rather than as the first item of a list.
   Qualified with .grid so it beats the base .grid rule regardless of which
   one appears later in this file — equal specificity would let source order
   decide, and it silently did. */
.grid.nodes-grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr))}
.muted{color:var(--muted);font-weight:400;font-size:12px}
main{padding:20px;max-width:1100px;margin:0 auto}
a{color:var(--line)}
h2{font-size:15px;margin:26px 0 12px;font-weight:600;display:flex;align-items:baseline;gap:10px}
h3{font-size:13px;margin:0 0 8px;font-weight:600;color:var(--muted)}

.node-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:10px;margin:26px 0 14px;
           padding-bottom:8px;border-bottom:1px solid var(--border)}
.node-head .id{font-family:ui-monospace,monospace;font-size:15px;font-weight:600}

.chip{display:inline-flex;align-items:center;gap:6px;background:var(--panel);
      border:1px solid var(--border);border-radius:20px;padding:4px 12px;font-size:12px}
.dot{width:8px;height:8px;border-radius:50%;background:var(--muted);display:inline-block}
.dot.ok{background:var(--ok)}
.dot.stale{background:var(--stale)}
.dot.err{background:var(--err)}

/* live view — one card per leg, side by side on desktop */
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:14px}
.card{background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:12px}
/* Cap the height, don't crop: an all-sky frame is a circle in a rectangle, so
   object-fit:contain letterboxes rather than cutting the horizon off. Without
   a cap a single live frame fills the whole viewport and the page stops being
   a summary — click through for full resolution instead. */
.card img,.card video{width:100%;max-height:360px;object-fit:contain;
                      display:block;border-radius:6px;background:#000}
/* Images are wrapped in a link to the full-resolution object; an inline anchor
   would sit the image on the text baseline and leave a gap under every card. */
.card a{display:block;line-height:0}
.card .cap{display:flex;justify-content:space-between;align-items:baseline;gap:8px;margin-top:8px;font-size:12px}
.card .cap .leg{font-weight:600;text-transform:capitalize}

/* archive table */
table{width:100%;border-collapse:collapse;font-size:13px}
th{text-align:left;font-weight:600;color:var(--muted);font-size:12px;
   padding:6px 10px;border-bottom:1px solid var(--border)}
td{padding:6px 10px;border-bottom:1px solid var(--border)}
tr:hover td{background:var(--panel)}
td.num{text-align:right;color:var(--muted);font-variant-numeric:tabular-nums}
.tag{display:inline-block;padding:1px 7px;border-radius:10px;font-size:11px;
     border:1px solid var(--border);color:var(--muted)}
