/* Aurora Monitor: Space View
   A real rotating 3D Earth with the live aurora oval, minimal overlay UI. */

:root {
    --bg:        #03060d;
    --ink:       #eef3f4;
    /* Text tints. Alphas are tuned so each still clears WCAG AA (4.5:1) once
       composited over --bg, because every one of them is used at 11px to 13px.
       Previously --faint sat at 4.25:1 and --dim at 2.39:1. */
    --muted:     rgba(238, 243, 244, 0.66);  /* 7.9:1 */
    --faint:     rgba(238, 243, 244, 0.56);  /* 5.9:1 */
    --dim:       rgba(238, 243, 244, 0.50);  /* 4.9:1 */
    /* This is the only visible edge on the search field, both icon buttons, the notify
       buttons, the view switch and the modal, so WCAG 1.4.11 wants 3:1. It was 0.18
       (1.55:1) and the search box read as having no border at all.
       --line-soft stays decorative and is deliberately left alone. */
    --line:      rgba(238, 243, 244, 0.38);  /* 3.2:1 */
    --line-soft: rgba(238, 243, 244, 0.10);
    --green:     #2ee39b;
    --green-dim: rgba(46, 227, 155, 0.75);
    --violet:    #8a7bd8;
    --amber:     #e7c069;
    --sky:       #6fc8e8;
    --sans: 'Instrument Sans', system-ui, -apple-system, sans-serif;
    --pad: clamp(20px, 4vw, 58px);
    --panel-w: clamp(300px, 24vw, 360px);
}

*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }

body {
    margin: 0;
    background:
        radial-gradient(120% 80% at 75% -10%, rgba(46, 227, 155, 0.08), transparent 55%),
        radial-gradient(100% 70% at 10% 110%, rgba(138, 123, 216, 0.10), transparent 60%),
        var(--bg);
    color: var(--ink);
    font-family: var(--sans);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    overflow: hidden;
}
body.no-globe #globe { display: none; }

:focus-visible { outline: 2px solid var(--green); outline-offset: 3px; border-radius: 6px; }
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; } }
/* ---------------- Loader ---------------- */
.loader {
    position: fixed; inset: 0; z-index: 99999;
    display: flex; align-items: center; justify-content: center;
    background:
        radial-gradient(80% 60% at 50% 35%, rgba(46,227,155,0.10), transparent 60%),
        var(--bg);
    transition: opacity 0.7s ease;
}
.loader.hide { opacity: 0; pointer-events: none; }
.loader-inner { display: flex; flex-direction: column; align-items: center; gap: 22px; }
.loader-orb { position: relative; width: 78px; height: 78px; }
.loader-orb span {
    position: absolute; inset: 0; border-radius: 50%;
    border: 2px solid transparent; border-top-color: var(--green);
    animation: orbSpin 1.3s linear infinite;
}
.loader-orb span:nth-child(2) { inset: 9px; border-top-color: #78ffd2; animation-duration: 1.9s; animation-direction: reverse; }
.loader-orb span:nth-child(3) { inset: 18px; border-top-color: var(--violet); animation-duration: 2.5s; }
@keyframes orbSpin { to { transform: rotate(360deg); } }
.loader-mark {
    font-size: 14px; letter-spacing: 0.4em; text-transform: uppercase; font-weight: 600;
    background: linear-gradient(90deg, var(--green), #9fe6ff, var(--violet), var(--green));
    background-size: 250% 100%; -webkit-background-clip: text; background-clip: text; color: transparent;
    animation: shimmer 3s linear infinite; padding-left: 0.4em;
}
@keyframes shimmer { to { background-position: 250% 0; } }
.loader-bar { width: 190px; height: 2px; background: rgba(255,255,255,0.10); border-radius: 2px; overflow: hidden; }
.loader-bar i {
    display: block; width: 40%; height: 100%; border-radius: 2px;
    background: linear-gradient(90deg, transparent, var(--green), #9fe6ff, transparent);
    animation: barSweep 1.3s ease-in-out infinite;
}
@keyframes barSweep { 0% { transform: translateX(-120%); } 100% { transform: translateX(360%); } }
.loader-sub { font-size: 12.5px; letter-spacing: 0.14em; color: var(--faint); text-transform: lowercase; }

/* ---------------- Backdrop ---------------- */
#stars-canvas { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 0; }

#globe {
    position: fixed; inset: 0; z-index: 1;
    display: flex; align-items: center; justify-content: center;
    /* Camera view offset places Earth; keep the canvas edge at the viewport edge. */
    cursor: grab;
}
#globe:active { cursor: grabbing; }
#globe canvas { display: block; }

/* Legibility scrim behind the hero.
   The old version was a radial anchored at the bottom-left corner plus a bottom-up
   linear. Measured on a 1871x921 viewport the radial died at y>=534 and the linear at
   y>=571, but the headline sits around y=270..460, so the H1 had no scrim at all and
   fell to 1.29:1 whenever the day-side Earth rotated behind it.
   This ramp holds 0.78 or better across the text column (roughly the left 30%, which is
   9:1 or better) and reaches zero by 58%, so the globe's bright face stays vivid and
   only its left limb is touched. */
.scrim {
    position: fixed; inset: 0; z-index: 2; pointer-events: none;
    background:
        linear-gradient(to right, rgba(3, 6, 13, 0.90) 0%, rgba(3, 6, 13, 0.78) 28%, rgba(3, 6, 13, 0.34) 42%, transparent 58%),
        linear-gradient(to top, rgba(3, 6, 13, 0.62), transparent 46%);
}

/* ---------------- Minimap (camera-view indicator) ---------------- */
#minimap {
    position: fixed; top: 66px; left: var(--pad); z-index: 6;
    width: 132px; height: 132px; border-radius: 50%; overflow: hidden;
    border: 1px solid var(--line); background: transparent;
    pointer-events: auto; cursor: pointer;
}
#minimap canvas { display: block; width: 132px !important; height: 132px !important; }
@media (max-width: 900px) { #minimap { width: 96px; height: 96px; top: 60px; } #minimap canvas { width: 96px !important; height: 96px !important; } }

/* Sun / Moon edge markers, shown only while that body is off-screen.
   globe.js sets --ang so the arrow points outward toward the real position. */
.sky-marker {
    position: fixed; z-index: 6; display: none;
    align-items: center; justify-content: center;
    width: 34px; height: 34px; border-radius: 50%;
    transform: translate(-50%, -50%);
    pointer-events: none;
}
.sm-arrow { position: absolute; inset: 0; transform: rotate(var(--ang, 0deg)); }
.sm-arrow::before {
    content: ''; position: absolute; left: 50%; top: -9px;
    width: 0; height: 0; transform: translateX(-50%); opacity: 0.9;
    border-left: 5px solid transparent; border-right: 5px solid transparent;
    border-bottom: 7px solid currentColor;
}
.sky-sun {
    color: #ffd68a;
    background: rgba(255, 214, 140, 0.15);
    border: 1px solid rgba(255, 214, 140, 0.55);
    box-shadow: 0 0 16px rgba(255, 200, 110, 0.32);
}
.sky-moon {
    color: #cdd8ea;
    background: rgba(200, 215, 240, 0.13);
    border: 1px solid rgba(200, 215, 240, 0.45);
    box-shadow: 0 0 14px rgba(170, 195, 235, 0.26);
}

/* ---------------- Top bar ---------------- */
.topbar {
    position: fixed; top: 0; left: 0; right: 0; z-index: 6;
    display: flex; justify-content: space-between; align-items: center;
    padding: clamp(15px, 2.4vw, 24px) var(--pad);
    pointer-events: none;
}
.topbar > * { pointer-events: auto; }
.wordmark { font-size: 13px; letter-spacing: 0.34em; text-transform: uppercase; font-weight: 500; }
.top-right { display: flex; align-items: center; gap: 20px; }
.clock { font-size: 13px; letter-spacing: 0.04em; color: var(--muted); display: inline-flex; align-items: center; gap: 8px; font-variant-numeric: tabular-nums; }
.clock .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); box-shadow: 0 0 9px var(--green); animation: blink 2.4s ease-in-out infinite; }
body[data-live="0"] .clock .dot { background: var(--amber); box-shadow: 0 0 9px var(--amber); animation: none; }
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:.4} }
/* settings rows in the sidebar */
.panel-clock {
    display: flex; justify-content: flex-start; align-items: center; gap: 7px;
    position: relative; padding: 0 2px 12px;
    border-bottom: 1px solid var(--line-soft);
}
.panel-clock::after {
    content: ''; position: absolute; left: 0; bottom: -1px;
    width: 58px; height: 1px;
    background: linear-gradient(90deg, var(--green), transparent);
}
.panel-clock .sep, .panel-clock .unit { color: var(--faint); }

.lang-row {
    display: flex; justify-content: space-between; align-items: center;
    padding: 10px 12px; border-radius: 11px;
    background: rgba(0, 0, 0, 0.28); border: 1px solid var(--line-soft);
}
.ctrl-lbl { font-size: 13.5px; color: var(--muted); }

.lang { display: flex; gap: 4px; font-size: 14px; letter-spacing: 0.06em; }
/* Padded out to a usable tap target; the text alone was about 20px tall. */
.lang a {
    color: var(--faint); text-decoration: none; transition: color .15s;
    padding: 7px 9px; border-radius: 7px; border-bottom: 1.5px solid transparent;
}
.lang a:hover { color: var(--ink); background: rgba(255, 255, 255, 0.05); }
.lang a.on { color: var(--ink); border-bottom-color: var(--green); }

/* ---------------- Hero (non-interactive except slider) ---------------- */
.hero {
    position: fixed; left: 0; bottom: 0; z-index: 4;
    padding: 0 var(--pad) clamp(22px, 4vh, 44px);
    max-width: 620px; pointer-events: none;
}
/* The topical h1. The big percentage below is the visual hero, but it is a live
   counter whose text never contains the subject, so the real heading sits here as a
   small eyebrow. Visible, not hidden: Google discounts visually hidden headings. */
.hero-h1 {
    margin: 0 0 8px; font-size: 12px; font-weight: 600;
    letter-spacing: 0.2em; text-transform: uppercase; color: var(--green-dim);
}

.place { margin: 0 0 12px; font-size: 14.5px; letter-spacing: 0.14em; text-transform: lowercase; color: var(--muted); }
.head {
    margin: 0; font-weight: 400; line-height: 1.03; letter-spacing: -0.015em;
    font-size: clamp(38px, 5.6vw, 76px);
    /* A tight high-alpha halo carries far better over a busy backdrop than one wide
       soft blur, which spreads too thin to darken anything. */
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.95), 0 2px 10px rgba(0, 0, 0, 0.85), 0 0 32px rgba(0, 0, 0, 0.6);
}
.head .chance { font-weight: 600; }
.head .verdict { font-style: normal; font-weight: 600; }
.head .dash { color: var(--faint); font-weight: 400; }
.head .tonight { font-weight: 400; color: var(--muted); }
/* min-height reserves TWO lines. The old 1.2em floor was dead (line-height is already
   1.45em), so when a two-line status string arrived the bottom-anchored hero shoved the
   whole headline up by about 23px on every load. */
.subline {
    margin: 14px 0 0; font-size: 16px; color: var(--muted); max-width: 44ch;
    min-height: 2.9em; line-height: 1.45;
    text-shadow: 0 1px 2px rgba(0,0,0,.95), 0 1px 12px rgba(0,0,0,.8);
}
.rule { width: 54px; height: 1px; background: var(--line); margin: 24px 0 20px; }

/* Grid, not flex. Flex sized each column to its own text, so the four sparklines
   came out four different widths and the row read as broken. */
.metrics {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: clamp(14px, 2vw, 26px);
}
.metric { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.metric .m-label {
    font-size: 11.5px; letter-spacing: 0.13em; text-transform: uppercase; color: var(--faint);
    line-height: 1.2; min-height: 2.4em;
}
.metric .m-value { font-size: 20px; font-weight: 500; color: var(--ink); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.spark { width: 100%; height: 18px; margin-top: 2px; display: block; overflow: visible; }
.metric:nth-child(1) .spark { color: var(--green); }
.metric:nth-child(2) .spark { color: var(--violet); }
.metric:nth-child(3) .spark { color: var(--sky); }
.metric:nth-child(4) .spark { color: var(--amber); }
.spark-line { filter: drop-shadow(0 0 3px currentColor); opacity: 0.9; }
.spark-dot { fill: #fff; stroke: currentColor; stroke-width: 1.4; filter: drop-shadow(0 0 4px currentColor); transition: cx .12s linear, cy .12s linear; }

.metrics-sub { display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px 22px; margin-top: 20px; font-size: 14px; color: var(--muted); }
.metrics-sub span { display: inline-flex; gap: 7px; align-items: baseline; }
.metrics-sub i { font-style: normal; color: var(--faint); letter-spacing: 0.05em; text-transform: uppercase; font-size: 11.5px; }
.metrics-sub b { font-weight: 500; color: var(--ink); font-variant-numeric: tabular-nums; }

.scrub-wrap { position: relative; margin-top: 26px; max-width: 560px; pointer-events: auto; }
/* The label and the return button share one flex row. The button used to be absolutely
   positioned over the label's top right, sized by shrink-to-fit, with 108px of padding
   on the label to keep the two apart. In a real browser profile that collapsed the
   button to nothing but its 26px of padding: a circle round the arrow with the words
   spilling out past it. As a flex item with flex:none its width is just its text, and
   min-height holds the row steady so it does not jump when the button appears. */
.scrub-head {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    min-height: 26px; margin-bottom: 5px;
}
.scrub-label { min-width: 0; font-size: 12px; letter-spacing: 0.05em; color: var(--muted); }
.scrub-label::before { content: '⇆'; margin-right: 7px; color: var(--green); font-size: 13px; }
.scrub { display: flex; align-items: center; gap: 16px; }
.scrub-cap { font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--faint); white-space: nowrap; }
/* Wide enough for the longest string this can hold ("-12h 34m", about 74px). The old
   50px floor was under every real value, so the slider (flex:1) shrank as the label
   grew and the thumb crept out from under the pointer mid-drag. */
.scrub-now { color: var(--green); min-width: 84px; text-align: right; font-variant-numeric: tabular-nums; }
body[data-live="0"] .scrub-now { color: var(--amber); }
.live-btn {
    flex: none; display: inline-flex; align-items: center; gap: 6px; line-height: 1.2;
    pointer-events: auto; cursor: pointer; white-space: nowrap;
    background: rgba(46, 227, 155, 0.16); border: 1px solid var(--green); color: var(--green);
    border-radius: 999px; padding: 5px 13px; font-family: inherit; font-size: 11px;
    font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; transition: background .15s, transform .05s;
}
.live-btn:hover { background: rgba(46, 227, 155, 0.30); }
.live-btn:active { transform: scale(0.96); }
.live-btn[hidden] { display: none; }
/* No "outline: none" here. The ID selector outranks the global :focus-visible rule, so
   it stripped the ring even while focused, and appearance:none had already removed the
   UA one. Ring the 22px thumb rather than the 4px track. */
#time-slider { -webkit-appearance: none; appearance: none; flex: 1; height: 4px; background: linear-gradient(90deg, rgba(46,227,155,.35), var(--line)); border-radius: 4px; cursor: grab; }
#time-slider:focus-visible { outline: none; }
#time-slider:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--bg), 0 0 0 6px var(--green), 0 3px 8px rgba(0,0,0,.5); }
#time-slider:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 3px var(--bg), 0 0 0 6px var(--green), 0 3px 8px rgba(0,0,0,.5); }
#time-slider:active { cursor: grabbing; }
#time-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 22px; height: 22px; border-radius: 50%; background: #fff; border: 3px solid var(--green); box-shadow: 0 0 0 5px rgba(46,227,155,.18), 0 3px 8px rgba(0,0,0,.5); cursor: grab; }
#time-slider::-moz-range-thumb { width: 22px; height: 22px; border: 3px solid var(--green); border-radius: 50%; background: #fff; box-shadow: 0 0 0 5px rgba(46,227,155,.18), 0 3px 8px rgba(0,0,0,.5); cursor: grab; }

/* info link + modal */
.info-link {
    pointer-events: auto; display: inline-flex; align-items: center; gap: 6px;
    background: none; border: none; cursor: pointer; padding: 0;
    color: var(--green-dim); font-family: inherit; font-size: 13px; align-self: flex-start; margin-top: 14px; padding: 6px 0;
    border-bottom: 1px dashed var(--green-dim); transition: color .15s;
}
.info-link:hover { color: var(--green); }
/* Two secondary actions share one row. The guide link matters more than it looks: the
   only other path to the long form page is in the panel footer, below the fold of a
   scrolling column, where neither a reader nor a crawler weighing what this site is
   about will ever reach it. This one sits beside the h1 at every width. */
.hero-links {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px 22px;
    align-self: flex-start; margin-top: 14px; pointer-events: auto;
}
.hero-links .info-link { margin-top: 0; align-self: auto; }
.modal {
    position: fixed; inset: 0; z-index: 9999; display: none;
    align-items: center; justify-content: center; padding: 22px;
    background: rgba(3, 6, 13, 0.78); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
.modal.open { display: flex; animation: fade .2s ease-out; }
/* Set by initInfo while the dialog is open. Desktop already has overflow:hidden on the
   body; this is what stops the page drifting behind the dialog on mobile. */
body.modal-open { position: fixed; width: 100%; overflow: hidden; }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
.modal-card {
    position: relative; max-width: 560px; width: 100%; max-height: 86vh; overflow-y: auto;
    background: #0a0f17; border: 1px solid var(--line); border-radius: 16px;
    padding: 30px clamp(22px, 4vw, 36px); box-shadow: 0 30px 80px -20px rgba(0,0,0,.8);
}
.modal-card h2 { margin: 0 0 6px; font-size: 22px; font-weight: 600; }
.modal-intro { margin: 0 0 20px; color: var(--muted); font-size: 14.5px; line-height: 1.55; }
.modal-close {
    position: absolute; top: 10px; right: 12px;
    display: grid; place-items: center; width: 40px; height: 40px;
    background: none; border: none; border-radius: 9px;
    color: var(--muted); font-size: 19px; cursor: pointer; line-height: 1;
    transition: color .15s, background .15s;
}
.modal-close:hover { color: var(--ink); background: rgba(255, 255, 255, 0.06); }
.glossary { margin: 0; }
.g-row { display: grid; grid-template-columns: 116px 1fr; gap: 14px; padding: 13px 0; border-bottom: 1px solid var(--line-soft); }
.g-row:last-child { border-bottom: none; }
.g-row dt { font-weight: 600; color: var(--green); font-size: 14.5px; }
.g-row dd { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.5; }
.modal-foot { margin: 18px 0 0; font-size: 11.5px; color: var(--dim); letter-spacing: .05em; }
@media (max-width: 480px) { .g-row { grid-template-columns: 1fr; gap: 3px; } }

/* globe location pin (HTML overlay) + country tooltip */
.globe-pin { pointer-events: none; white-space: nowrap; }
.globe-pin .gp-dot { width: 11px; height: 11px; border-radius: 50%; background: #fff; border: 2px solid var(--green); box-shadow: 0 0 12px var(--green), 0 0 4px #fff; }
.globe-pin .gp-lbl { font-family: var(--sans); font-size: 12.5px; font-weight: 600; color: #fff; text-shadow: 0 1px 6px rgba(0,0,0,.9); letter-spacing: .02em; }
.globe-tip { font-family: var(--sans); font-size: 13px; font-weight: 600; color: #fff; background: rgba(6,10,18,.86); border: 1px solid var(--green-dim); padding: 5px 10px; border-radius: 7px; box-shadow: 0 6px 18px rgba(0,0,0,.6); }

/* ---------------- Control panel ---------------- */
.panel {
    position: fixed; top: 0; right: 0; z-index: 5;
    width: var(--panel-w); height: 100vh; overflow-y: auto;
    /* The top inset used to be clamp(70px, 9vh, 92px), reserving room for the clock that
       once sat in .top-right of the fixed topbar. That element is gone, the clock moved
       into .panel-clock below, and .topbar's only remaining child is the wordmark on the
       far left, so the band was clearing nothing and read as dead space. Matching the
       topbar's own inset instead puts the panel clock on the wordmark's line. */
    padding: clamp(15px, 2.4vw, 24px) clamp(16px, 1.4vw, 22px) 20px;
    display: flex; flex-direction: column; gap: 18px;
    /* The old ramp hit alpha 0 at the panel's own left edge, but horizontal padding is
       only ~20px, so text started where the gradient was about 8% opaque and sat almost
       unscrimmed over the globe's right limb (the search placeholder measured ~1.1:1).
       The feather now finishes inside the padding, so no text is ever below 0.90. */
    background:
        radial-gradient(130% 160px at 82% 0%, rgba(46, 227, 155, 0.09), transparent 68%),
        radial-gradient(90% 120px at 100% 30%, rgba(111, 200, 232, 0.05), transparent 70%),
        linear-gradient(to left, rgba(3, 6, 13, 0.94) 0%, rgba(3, 6, 13, 0.90) 88%, rgba(3, 6, 13, 0) 100%);
    border-left: 1px solid var(--line-soft);
    scrollbar-width: thin; scrollbar-color: var(--dim) transparent;
}
.panel::-webkit-scrollbar { width: 6px; }
.panel::-webkit-scrollbar-thumb { background: var(--dim); border-radius: 3px; }

/* search */
.search { display: flex; gap: 8px; position: relative; flex-wrap: wrap; }
.search input {
    flex: 1; min-width: 0; height: 44px; background: rgba(0, 0, 0, 0.30);
    border: 1px solid var(--line); border-radius: 12px; padding: 0 14px;
    color: var(--ink); font-family: inherit; font-size: 15px;
    transition: border-color .15s, background .15s, box-shadow .15s;
}
.search input::placeholder { color: var(--faint); }
.search input:focus {
    outline: none; border-color: var(--green-dim); background: rgba(0, 0, 0, 0.42);
    box-shadow: 0 0 0 3px rgba(46, 227, 155, 0.14);
}
.icon-btn {
    width: 44px; height: 44px; flex: none; display: grid; place-items: center;
    background: rgba(255, 255, 255, 0.06); border: 1px solid var(--line); border-radius: 12px;
    color: var(--ink); cursor: pointer; transition: color .15s, border-color .15s, background .15s, transform .05s;
}
.icon-btn:hover { border-color: var(--green-dim); background: rgba(46, 227, 155, 0.12); color: var(--green); }
.icon-btn:active { transform: scale(0.95); }

/* search suggestions */
.suggest {
    list-style: none; margin: 0; padding: 5px; position: absolute; top: calc(100% + 6px); left: 0; right: 0;
    z-index: 20; background: #0a0f17; border: 1px solid var(--line); border-radius: 10px;
    box-shadow: 0 18px 40px -12px rgba(0,0,0,.7); display: none; max-height: 280px; overflow-y: auto;
}
.suggest.open { display: block; }
.suggest li { padding: 9px 11px; border-radius: 7px; cursor: pointer; display: flex; flex-direction: column; gap: 1px; }
.suggest li:hover { background: rgba(46, 227, 155, 0.12); }
.suggest li b { font-weight: 600; font-size: 14.5px; color: var(--ink); }
.suggest li span { font-size: 12px; color: var(--faint); }

/* notify */
.notify-btn {
    display: flex; align-items: center; gap: 10px; width: 100%;
    padding: 13px 15px; border-radius: 12px; cursor: pointer;
    background: rgba(0, 0, 0, 0.28); border: 1px solid var(--line-soft);
    color: var(--ink); font-family: inherit; font-size: 14.5px; font-weight: 500;
    transition: background .15s, border-color .15s, color .15s, transform .05s, box-shadow .15s;
}
/* The one primary action in the panel. The aria-pressed styling below still outranks
   this class, so the enabled state keeps its solid green look. */
.notify-primary {
    background: linear-gradient(135deg, rgba(46, 227, 155, 0.16), rgba(111, 200, 232, 0.06));
    border-color: rgba(46, 227, 155, 0.30);
}
.notify-primary:hover { box-shadow: 0 6px 20px -10px rgba(46, 227, 155, 0.45); }
.notify-primary .bell { color: var(--green); }
.notify-btn:hover { background: rgba(46, 227, 155, 0.10); border-color: var(--green-dim); }
.notify-btn:active { transform: scale(0.99); }
.notify-btn .bell { color: var(--muted); transition: color .15s; }
.notify-btn[aria-pressed="true"] {
    background: rgba(46, 227, 155, 0.14); border-color: var(--green); color: var(--green);
}
.notify-btn[aria-pressed="true"] .bell { color: var(--green); animation: ring .5s ease; }
.notify-btn.denied { border-color: rgba(231, 192, 105, 0.5); color: var(--amber); }
@keyframes ring { 0%,100%{transform:rotate(0)} 25%{transform:rotate(12deg)} 75%{transform:rotate(-12deg)} }
/* No negative pull and no 2px indent. Those made this one row break an otherwise
   uniform 18px column rhythm and sit misaligned against every flush sibling. */
.notify-hint { margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--faint); }

.panel-block {
    display: flex; flex-direction: column; gap: 11px;
    padding: 14px; border-radius: 16px;
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.015));
    border: 1px solid var(--line-soft);
}
/* Wraps, because a head can now carry a label plus a toggle and the longest
   label/toggle pair does not fit a 300px panel on one line. */
.panel-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 4px 10px; }
.p-label {
    display: inline-flex; align-items: center; gap: 8px;
    font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted);
}
.p-label::before {
    content: ''; width: 4px; height: 11px; border-radius: 2px;
    background: linear-gradient(180deg, var(--green), rgba(46, 227, 155, 0.25));
}
/* position + z-index is what makes this a stacking context, and that is the point.
   Leaflet gives its own panes z-index 400. Without a context here those 400s competed
   directly with .suggest (z-index 20) in the panel, so the city dropdown was painted
   behind the minimap and its clicks landed on the map instead. Containing them means
   the whole map resolves at this element's level, under the dropdown, where it belongs. */
#map { position: relative; z-index: 0; width: 100%; height: clamp(220px, 26vh, 300px); border-radius: 11px; border: 1px solid var(--line-soft); overflow: hidden; background: #060a12; }
/* Base tiles only, never the light pollution overlay layered above them: that overlay's
   colours encode sky brightness, so filtering it would misstate the reading.
   contrast() pushes away from mid grey, which costs dark pixels more than it first looks:
   measured over a real tile, 0.45/1.12 lands the mean at 13/255 and dissolves the borders.
   0.60/1.12 lands it at 23, which is exactly where CARTO's tiles sat, and keeps the labels. */
#map .basemap-dark { filter: brightness(0.60) contrast(1.12); }
/* Not margin-top:auto. In a flex column with gap:18px that dumps all the leftover
   height into this single gap, so it differed on every viewport and visibly jumped
   each time the Advanced section opened or closed. */
.panel-foot {
    margin-top: 6px; padding-top: 14px; border-top: 1px solid var(--line-soft);
    display: flex; flex-direction: column; align-items: center; gap: 12px;
    font-size: 11px; letter-spacing: 0.09em; color: var(--faint);
    text-align: center; line-height: 1.6;
}
/* RavDev attribution. The wordmark carries its own crimson and light grey, so it is
   given a fixed box and dimmed at rest rather than recoloured. */
.foot-by {
    display: inline-flex; flex-direction: column; align-items: center; gap: 6px;
    text-decoration: none; padding: 6px 10px; border-radius: 9px;
    opacity: 0.65; transition: opacity .18s, background .18s;
}
.foot-by:hover, .foot-by:focus-visible { opacity: 1; background: rgba(255, 255, 255, 0.05); }
.foot-by-lbl {
    font-size: 9.5px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--dim);
}
.foot-by img { display: block; width: 86px; height: auto; }

/* Advanced trigger in the sidebar. Carries a two-number peek so the row is worth
   looking at even when the sheet is closed. */
.adv-open {
    display: flex; align-items: center; gap: 10px; width: 100%;
    padding: 14px 15px; border-radius: 14px; cursor: pointer;
    background: linear-gradient(135deg, rgba(46, 227, 155, 0.10), rgba(111, 200, 232, 0.04));
    border: 1px solid rgba(46, 227, 155, 0.26);
    color: var(--ink); font-family: inherit; text-align: left;
    transition: background .15s, border-color .15s, box-shadow .18s, transform .12s;
}
.adv-open:hover {
    border-color: var(--green-dim); transform: translateY(-1px);
    box-shadow: 0 8px 22px -10px rgba(46, 227, 155, 0.40);
}
.adv-open > svg { color: var(--green-dim); flex: none; }
.adv-open-label { flex: 1; font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); }
.adv-peek { font-size: 13px; font-weight: 600; color: var(--green); font-variant-numeric: tabular-nums; }

/* ---- The sheet ----
   Full screen on phones, a wide centred panel from 700px up. Ten readings plus a
   27 day strip were never going to be legible inside a 300 to 360px sidebar. */
/* No backdrop-filter here: below 700px the card covers the whole viewport, so an 8px
   blur over the live globe canvas would burn GPU every frame while being invisible.
   The blur is applied only in the >=700px branch, where the backdrop actually shows. */
.sheet {
    position: fixed; inset: 0; z-index: 9999; display: none;
    background: rgba(3, 6, 13, 0.86);
    overscroll-behavior: none;
}
.sheet:not([hidden]).open { display: flex; align-items: flex-end; justify-content: center; }
.sheet-card {
    display: flex; flex-direction: column;
    width: 100%; max-height: 100%; background: #080d16;
    border-top: 1px solid var(--line); border-radius: 18px 18px 0 0;
    animation: sheetUp .26s cubic-bezier(.22,.9,.3,1);
}
@keyframes sheetUp { from { transform: translateY(3%); opacity: 0; } to { transform: none; opacity: 1; } }
.sheet-head {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 18px clamp(18px, 5vw, 30px) 14px;
    border-bottom: 1px solid var(--line-soft); flex: none;
}
.sheet-title { min-width: 0; }
.sheet-head h2 { margin: 0; font-size: 17px; font-weight: 600; letter-spacing: 0.02em; }
.sheet-sub { margin: 3px 0 0; font-size: 12px; line-height: 1.4; color: var(--faint); }
.sheet-close {
    display: grid; place-items: center; width: 44px; height: 44px; flex: none;
    background: none; border: 1px solid var(--line); border-radius: 11px;
    color: var(--muted); cursor: pointer; transition: color .15s, background .15s;
}
.sheet-close:hover { color: var(--ink); background: rgba(255, 255, 255, 0.06); }
.sheet-body {
    overflow-y: auto; -webkit-overflow-scrolling: touch;
    /* Stops a flick at either end handing the scroll to the page behind the sheet. */
    overscroll-behavior: contain;
    padding: 4px clamp(18px, 5vw, 30px) 28px;
}

/* Section headings give the ten readings an order: tonight, now, the Sun. */
.adv-group {
    margin: 22px 0 10px; font-size: 11.5px; font-weight: 600;
    letter-spacing: 0.18em; text-transform: uppercase; color: var(--green);
}
.adv-group:first-of-type { margin-top: 6px; }

/* Cards, not rows. The old label/value pair sat at opposite edges of a 200px gap with
   the meaning hidden in a title tooltip that never appears on touch. */
.adv-grid { display: grid; grid-template-columns: 1fr; gap: 8px; }
.stat {
    display: flex; flex-direction: column; gap: 3px;
    padding: 13px 15px; border-radius: 12px;
    background: rgba(255, 255, 255, 0.035); border: 1px solid var(--line-soft);
}
.stat-k { font-size: 12.5px; color: var(--muted); letter-spacing: 0.01em; }
/* Scales down on narrow phones so a long value stays on one or two lines rather than
   three. Caps at 22px so it does not grow on desktop. */
.stat-v {
    font-size: clamp(18px, 5.5vw, 22px); font-weight: 600; color: var(--ink); line-height: 1.15;
    font-variant-numeric: tabular-nums; letter-spacing: -0.01em;
    overflow-wrap: anywhere;
}
.stat-d { font-size: 12px; line-height: 1.45; color: var(--faint); }
/* Three meanings, three colours, never overlapping:
   green  good news for tonight (storm underway, clear sky, dark window)
   amber  working against you tonight (cloud, moonlight)
   sky    notable but not directly actionable (flare class, radio flux, distant CME)
   Amber used to carry both "bad" and "notable", so a G1 storm looked like a warning. */
.stat-v.is-hot { color: var(--green); }
.stat-v.is-warn { color: var(--amber); }
.stat-v.is-note { color: var(--sky); }
.stat-v.is-off { color: var(--faint); font-weight: 500; }

.adv-alert {
    padding: 12px 15px; margin: 6px 0 2px; border-radius: 12px;
    background: rgba(231, 192, 105, 0.10); border: 1px solid rgba(231, 192, 105, 0.45);
}
.adv-alert[hidden] { display: none; }
.adv-alert-k { font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--amber); }
.adv-alert-msg { margin: 5px 0 0; font-size: 13.5px; line-height: 1.45; color: var(--ink); }
.adv-hint { margin: 0 0 10px; font-size: 12.5px; line-height: 1.5; color: var(--faint); }

@media (min-width: 700px) {
    /* The blur belongs only here, where the backdrop is actually visible around the card. */
    .sheet:not([hidden]).open {
        align-items: center; padding: 24px;
        backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
    }
    /* min(88dvh, 100%): landscape phones (844x390 etc) match this WIDTH query, and a
       fixed 88vh overran the 24px sheet padding on anything under 400px tall. 100%
       resolves against the padded content box, so the padding always holds. */
    .sheet-card { max-width: 720px; border-radius: 18px; border: 1px solid var(--line); max-height: min(88dvh, 100%); }
    .adv-grid { grid-template-columns: 1fr 1fr; gap: 10px; }
}

/* small inline toggle used in a panel-head (light pollution) */
.mini-toggle {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 5px 10px; border-radius: 999px; cursor: pointer;
    background: rgba(0, 0, 0, 0.22); border: 1px solid var(--line-soft);
    color: var(--faint); font-family: inherit; font-size: 10.5px;
    letter-spacing: 0.08em; text-transform: uppercase; transition: color .15s, border-color .15s, background .15s;
}
.mini-toggle:hover { color: var(--ink); }
.mini-toggle[aria-pressed="true"] { color: var(--amber); border-color: rgba(231, 192, 105, 0.5); background: rgba(231, 192, 105, 0.10); }

/* two-state segmented control (times: local | UTC) */
.seg { display: flex; gap: 3px; padding: 3px; border-radius: 10px; background: rgba(0, 0, 0, 0.28); border: 1px solid var(--line-soft); }
.seg button {
    border: none; background: transparent; color: var(--faint); cursor: pointer;
    font-family: inherit; font-size: 11.5px; letter-spacing: 0.05em;
    padding: 6px 10px; border-radius: 6px; transition: background .15s, color .15s;
}
.seg button:hover { color: var(--ink); }
.seg button.on {
    background: linear-gradient(180deg, rgba(46, 227, 155, 0.24), rgba(46, 227, 155, 0.11));
    color: var(--green); box-shadow: inset 0 0 0 1px rgba(46, 227, 155, 0.35);
}

/* 27 day outlook. Now inside the sheet, so the cards get real width: at 40px every
   day looked identical and the whole strip read as a row of "2"s. */
.out27 {
    display: flex; gap: 6px; max-width: 100%; overflow-x: auto; overflow-y: hidden;
    padding: 2px 0 10px; scroll-snap-type: x proximity;
    /* Keeps a horizontal swipe inside the strip: without this, reaching either end
       hands the gesture to the page and on iOS to the back-navigation swipe. */
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin; scrollbar-color: var(--dim) transparent;
}
.out27::-webkit-scrollbar { height: 6px; }
.out27::-webkit-scrollbar-thumb { background: var(--dim); border-radius: 3px; }
.d27 {
    flex: none; width: 66px; scroll-snap-align: start;
    display: flex; flex-direction: column; align-items: center; gap: 5px;
    padding: 11px 5px 10px; border-radius: 10px;
    border: 1px solid var(--line-soft); background: rgba(255, 255, 255, 0.035);
}
/* The best nights in the window, ranked on Kp against how much moonlight actually
   reaches the sky. This pairing is the whole point of the widget. */
.d27.is-pick { border-color: var(--green); background: rgba(46, 227, 155, 0.13); }
.d27-day { font-size: 10.5px; letter-spacing: 0.08em; color: var(--faint); text-transform: uppercase; white-space: nowrap; line-height: 1; }
.d27-num { font-size: 15px; font-weight: 600; color: var(--ink); white-space: nowrap; line-height: 1.1; letter-spacing: 0.01em; }
.d27-kp { font-size: 13px; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; line-height: 1; }
.d27.kp4 .d27-kp, .d27.kp5 .d27-kp, .d27.kp6 .d27-kp { color: var(--ink); }
.d27-bar { width: 7px; height: 42px; border-radius: 4px; background: rgba(255, 255, 255, 0.08); display: flex; align-items: flex-end; overflow: hidden; }
.d27-bar i { display: block; width: 100%; border-radius: 4px; background: var(--green); }
.d27.kp4 .d27-bar i { background: var(--amber); }
.d27.kp5 .d27-bar i { background: #f0913f; }
.d27.kp6 .d27-bar i { background: #ef5b5b; }
/* A drawn phase, not a fill level: app.js builds the terminator as an ellipse so
   crescents, quarters and gibbous all read correctly. */
.d27-moon { display: block; overflow: visible; }
.moon-dark { fill: #10151f; stroke: var(--line); stroke-width: 1; }
.moon-lit { fill: #dfe6f2; }
.out27-key { display: flex; flex-wrap: wrap; gap: 8px 16px; margin: 2px 0 0; font-size: 12px; color: var(--faint); }
.out27-key span::before { content: ''; display: inline-block; width: 9px; height: 9px; margin-right: 6px; border-radius: 2px; }
.out27-key .k-kp::before { background: var(--green); }
.out27-key .k-moon::before { background: #dfe6f2; border-radius: 50%; }

/* view switcher (N pole / planet / S pole): sidebar control row */
.view-switch {
    display: flex; gap: 3px; padding: 3px;
    background: rgba(0, 0, 0, 0.28); border: 1px solid var(--line-soft); border-radius: 12px;
}
.view-switch button {
    flex: 1; border: none; background: transparent; color: var(--muted); cursor: pointer;
    font-family: inherit; font-size: 11.5px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
    padding: 12px 4px; border-radius: 8px; white-space: nowrap; min-width: 0; letter-spacing: 0.04em; transition: background .15s, color .15s;
}
.view-switch button:hover { color: var(--ink); }
.view-switch button.on {
    background: linear-gradient(180deg, rgba(46, 227, 155, 0.24), rgba(46, 227, 155, 0.11));
    color: var(--green); box-shadow: inset 0 0 0 1px rgba(46, 227, 155, 0.35);
}

/* globe hint */
/* Aligned with the camera projection, without moving the canvas. */
.globe-hint {
    position: fixed; left: calc(50% + 4.5vw); bottom: 14px; transform: translateX(-50%); z-index: 3;
    font-size: 12px; letter-spacing: 0.05em; color: var(--dim); pointer-events: none;
    text-align: center;
    animation: hintFade 1s ease 2s both;
}
@keyframes hintFade { from { opacity: 0; } to { opacity: 1; } }

/* ---------------- Map pin / Leaflet ---------------- */
.pin-pulse { width: 16px; height: 16px; border-radius: 50%; background: #fff; border: 3px solid var(--green); box-shadow: 0 0 10px var(--green); animation: pin 1.6s infinite; }
@keyframes pin { 0%,100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(46,227,155,.55), 0 0 10px var(--green); } 50% { transform: scale(1.1); box-shadow: 0 0 0 10px transparent, 0 0 10px var(--green); } }
.leaflet-container { background: #060a12 !important; font-family: var(--sans) !important; }
.leaflet-control-attribution { background: rgba(0,0,0,.72) !important; color: var(--muted) !important; font-size: 11px !important; }
.leaflet-control-attribution a { color: var(--green-dim) !important; }
.leaflet-bar a { background: rgba(6,10,18,.92) !important; color: var(--ink) !important; border-color: var(--line) !important; }

/* storm flash */
.storm-flash { position: fixed; inset: 0; z-index: 9998; pointer-events: none;
    background: radial-gradient(ellipse at 60% 40%, rgba(46,227,155,.3), rgba(138,123,216,.16) 50%, transparent 78%);
    animation: storm 1.5s ease-out forwards; }
@keyframes storm { 0%{opacity:0} 14%{opacity:1} 100%{opacity:0} }

/* ---------------- Responsive ---------------- */
@media (max-width: 920px) {
    /* height:100% plus a scrolling body fights iOS Safari. Let the body grow instead. */
    html, body { height: auto; min-height: 100%; }
    body { overflow-x: hidden; overflow-y: auto; }
    /* dvh tracks the shrinking address bar; the vh line is the fallback for older
       browsers and must stay directly above it. */
    #globe { transform: none; height: 64vh; height: 64dvh; bottom: auto; }
    /* Kept in step with the desktop scrim above; the hero stacks under the globe here,
       so the vertical ramp does the work and the horizontal one is not needed. */
    .scrim { background: linear-gradient(to top, rgba(3,6,13,.86) 0%, rgba(3,6,13,.6) 34%, transparent 62%); }
    /* The hero carries its own backdrop here. .scrim is fixed to the viewport, but this
       hero SCROLLS over a fixed globe, so once scrolled the text passed through the
       band where the scrim has already faded to zero and dropped to about 1.1:1 over a
       bright Earth. A gradient on the hero itself travels with the text. Stops are
       anchored to the padding so the ramp always lands just above the first line. */
    .hero {
        position: relative; max-width: none;
        padding-top: 58vh; padding-top: 58dvh;
        background: linear-gradient(to bottom,
            rgba(3, 6, 13, 0) calc(58dvh - 140px),
            rgba(3, 6, 13, 0.72) calc(58dvh - 60px),
            rgba(3, 6, 13, 0.94) calc(58dvh - 8px),
            var(--bg) calc(58dvh + 48px));
    }
    .panel {
        position: relative; width: 100%; height: auto; overflow: visible;
        padding: 24px var(--pad) 40px; border-left: none; border-top: 1px solid var(--line-soft);
        background:
            radial-gradient(120% 140px at 80% 0%, rgba(46, 227, 155, 0.08), transparent 70%),
            rgba(3, 6, 13, 0.9);
    }
    .globe-hint { display: none; }
    /* Hidden on touch. It is a 96px always-on-top target whose click silently moves the
       user's location, which is far too easy to hit by accident while spinning the globe,
       and its orientation cue is redundant when the globe itself is unobstructed here.
       Search, GPS and the Leaflet map all still set location. */
    #minimap { display: none; }
    .head { font-size: clamp(36px, 10vw, 60px); }
    #map { height: 240px; }

    /* The page scrolls on mobile, but OrbitControls sets touch-action:none inline on the
       canvas, so a vertical swipe anywhere in the top 64dvh span rotated the globe and
       the page refused to move. pan-y hands vertical drags back to the browser while the
       canvas keeps horizontal ones, which is how you spin a globe anyway. The pole views
       remain reachable through the N/Planet/S switcher.
       !important is required: it is the only way a stylesheet beats an inline style. */
    #globe canvas { touch-action: pan-y !important; }

    /* iOS Safari force-zooms the page when a focused input is under 16px. */
    .search input { font-size: 16px; }

    /* The hero is pointer-events:none so the globe stays draggable through the
       transparent area above the text. But once scrolled, the hero's own opaque
       background slides over the fixed globe canvas, and taps still fell THROUGH the
       solid paint to the globe underneath, which relocates the user with no undo.
       The text blocks now absorb their own taps; the padding above them still does not,
       so the globe is reachable exactly where it is actually visible. */
    .hero > .place,
    .hero > .head,
    .hero > .subline,
    .hero > .rule,
    .hero > .metrics,
    .hero > .metrics-sub { pointer-events: auto; }
}
/* Two columns once four no longer fit without the values wrapping. */
@media (max-width: 560px) {
    .metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 22px; }
}
@media (max-width: 440px) {
    .metrics { gap: 16px 18px; }
    .metric .m-value { font-size: 18px; }
    .metrics-sub { gap: 14px 18px; }
}

/* Touch input: grow the small controls to a usable target regardless of viewport
   width, since a large tablet still gets fingers rather than a cursor. */
@media (pointer: coarse) {
    .seg button { padding: 11px 14px; }
    .mini-toggle { padding: 9px 10px; }
    .lang a { padding: 10px 12px; }
    .view-switch button { padding: 14px 4px; }
    .info-link { padding: 10px 0; }
    .modal-close { width: 44px; height: 44px; }
    .suggest li { padding: 13px 11px; }
    /* Font line metrics put this at 43.5 to 44.9px; pin it above the threshold. */
    .adv-open { min-height: 48px; }

    /* The 24h scrub is the main interaction and it was a 4px track with a 22px thumb.
       The row padding gives the whole strip a 44px catch area for a slightly-off touch. */
    .scrub { padding: 12px 0; }
    #time-slider { height: 6px; }
    #time-slider::-webkit-slider-thumb { width: 30px; height: 30px; border-width: 4px; }
    #time-slider::-moz-range-thumb { width: 30px; height: 30px; border-width: 4px; }
}


/* ---------------- Long form content (SEO) ----------------
   Server rendered below the app so it is crawlable without JS, and given an opaque
   background at a z-index above the fixed globe, hero and panel so it covers them as
   it scrolls up. margin-top keeps the interactive app as the first full screen.
   The desktop body is scrollable for this; the globe still owns the wheel while you
   are at the top, which is why the hero carries an explicit link down here. */
.page-copy {
    padding: clamp(40px, 6vw, 76px) var(--pad) clamp(56px, 8vw, 104px);
}
.copy-inner { position: relative; max-width: 760px; margin: 0 auto; }
.copy-lede {
    margin: 0 0 34px; font-size: clamp(17px, 2.2vw, 20px); line-height: 1.6; color: var(--ink);
}
.page-copy h2 {
    margin: 40px 0 12px; font-size: clamp(19px, 2.4vw, 23px); font-weight: 600;
    letter-spacing: -0.01em; color: var(--ink); line-height: 1.25;
}
.page-copy h2:first-of-type { margin-top: 0; }
.page-copy h3 {
    margin: 26px 0 8px; font-size: 15.5px; font-weight: 600; color: var(--ink);
}
.page-copy p { margin: 0 0 14px; font-size: 15.5px; line-height: 1.72; color: var(--muted); }
.page-copy ul { margin: 0 0 16px; padding-left: 20px; }
.page-copy li { margin: 0 0 7px; font-size: 15.5px; line-height: 1.65; color: var(--muted); }
.page-copy strong { color: var(--ink); font-weight: 600; }
.page-copy a { color: var(--green); text-decoration: none; border-bottom: 1px solid rgba(46, 227, 155, 0.35); }
.page-copy a:hover { border-bottom-color: var(--green); }

/* FAQ, marked up as a definition list and mirrored into FAQPage structured data. */
.copy-faq { margin-top: 46px; padding-top: 30px; border-top: 1px solid var(--line-soft); }
.faq-list { margin: 0; }
.faq-list > div { padding: 16px 0; border-bottom: 1px solid var(--line-soft); }
.faq-list > div:last-child { border-bottom: none; }
.faq-list dt { margin: 0 0 6px; font-size: 16px; font-weight: 600; color: var(--ink); line-height: 1.4; }
.faq-list dd { margin: 0; font-size: 15px; line-height: 1.68; color: var(--muted); }

.copy-foot {
    margin-top: 44px; padding-top: 24px; border-top: 1px solid var(--line-soft);
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px;
    font-size: 12.5px; color: var(--faint);
}
.copy-foot a { color: var(--faint); border-bottom: none; }
.copy-foot a:hover { color: var(--ink); }
.copy-by { display: inline-flex; align-items: center; gap: 9px; }
.copy-by img { display: block; width: 74px; height: auto; opacity: 0.8; }
.copy-by:hover img { opacity: 1; }

/* Reserved advertising slot. Never overlays the app, always keeps its box so enabling
   it cannot shift the layout. */
/* min-height reserves the box before the unit fills, so enabling ads cannot push the
   article around as it loads. No border: this holds a real ad, not a placeholder. */
.ad-slot {
    margin: 40px auto; max-width: 760px; min-height: 100px;
    display: flex; align-items: center; justify-content: center;
}




/* ---------------- Guide page (guide.php) ----------------
   A normal document, not the app. The app stays a fixed full screen surface with no
   page scroll; the long form copy lives on its own indexable URL instead. */
body.doc { overflow-x: hidden; overflow-y: auto; }
body.doc::before {
    content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none;
    background: radial-gradient(70% 45% at 50% 0%, rgba(46, 227, 155, 0.07), transparent 70%);
}
.doc-bar {
    position: sticky; top: 0; z-index: 10;
    display: flex; align-items: center; justify-content: space-between; gap: 16px;
    padding: 14px var(--pad);
    background: rgba(3, 6, 13, 0.86);
    backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--line-soft);
}
.doc-home {
    display: inline-flex; align-items: center; gap: 8px;
    color: var(--green-dim); text-decoration: none; font-size: 13.5px;
    padding: 8px 12px; border-radius: 10px;
    border: 1px solid rgba(46, 227, 155, 0.26);
    background: linear-gradient(135deg, rgba(46, 227, 155, 0.10), rgba(111, 200, 232, 0.04));
    transition: color .15s, border-color .15s;
}
.doc-home:hover { color: var(--green); border-color: var(--green-dim); }
.doc-kicker {
    margin: 0 0 10px; font-size: 11.5px; font-weight: 600;
    letter-spacing: 0.2em; text-transform: uppercase; color: var(--green-dim);
}
.doc-h1 {
    margin: 0 0 22px; font-size: clamp(28px, 4.4vw, 44px); font-weight: 600;
    line-height: 1.15; letter-spacing: -0.02em; color: var(--ink);
}
.page-copy { position: relative; z-index: 1; }
.doc-cta { margin: 40px 0 0; }
.doc-cta a {
    display: inline-block; padding: 13px 20px; border-radius: 12px;
    background: linear-gradient(135deg, rgba(46, 227, 155, 0.16), rgba(111, 200, 232, 0.06));
    border: 1px solid rgba(46, 227, 155, 0.30);
    color: var(--green); text-decoration: none; font-size: 14.5px; font-weight: 500;
}
.doc-cta a:hover { border-color: var(--green); }

/* The link from the app down to the guide, in the sidebar footer. */
.foot-guide {
    display: inline-flex; align-items: center; gap: 6px;
    color: var(--green-dim); text-decoration: none; font-size: 12px;
    padding: 6px 0; transition: color .15s;
}
.foot-guide:hover { color: var(--green); }

/* Shared map controls. The 2D view leaves the whole map responsive to pan/zoom. */
[hidden] { display: none !important; }
.scene-toolbar {
    position: fixed; z-index: 7; top: 14px; left: 50%; transform: translateX(-50%);
    display: flex; align-items: center; gap: 8px; padding: 5px;
    background: rgba(3,6,13,.92); border: 1px solid var(--line); border-radius: 13px;
}
.scene-toolbar button { font: inherit; font-size: 12px; cursor: pointer; }
.scene-toolbar > button { border: 1px solid var(--line); border-radius: 8px; padding: 9px 12px; color: var(--ink); background: transparent; min-height: 36px; }
.scene-toolbar button[aria-pressed="true"] { background: rgba(46,227,155,.17); color: var(--green); }
#map2d { position: fixed; inset: 0 var(--panel-w) 0 0; z-index: 1; }
body[data-view-mode="2d"] #minimap,
body[data-view-mode="2d"] .sky-marker,
body[data-view-mode="2d"] .earth-attribution,
body[data-view-mode="2d"] #stars-canvas,
body[data-view-mode="2d"] .scrim { display: none !important; }
body[data-view-mode="2d"] .hero { left: 16px; bottom: 36px; max-width: 380px; padding: 18px; border-radius: 14px; background: rgba(3,6,13,.9); }
body[data-view-mode="2d"] .head { font-size: 34px; }
body[data-view-mode="2d"] .subline { font-size: 13px; margin-top: 10px; }
body[data-view-mode="2d"] .rule { margin: 14px 0; }
body[data-view-mode="2d"] .globe-hint { display: none; }
.earth-attribution { position: fixed; bottom: 3px; right: calc(var(--panel-w) + 12px); z-index: 4; max-width: 50vw; font-size: 9px; color: var(--muted); text-align: right; }
.earth-attribution a, .layer-source { color: var(--muted); }
.layer-status { margin: 8px 0; font-size: 12px; line-height: 1.5; color: var(--sky); }
.layer-status[data-state="error"], .layer-status[data-state="stale"] { color: var(--amber); }
.layer-note, .history-note { font-size: 11px; line-height: 1.5; color: var(--muted); margin: 8px 0 0; }
.layer-source { font-size: 11px; }
.history-note { max-width: 48ch; color: var(--amber); }
#btn-night:disabled { opacity: .4; cursor: default; }
.aurora-map-legend { color: #eef3f4; background: rgba(3,6,13,.9); border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; font-size: 11px; line-height: 1.5; max-width: 230px; }
.satellite-tip { position: fixed; z-index: 8; pointer-events: none; background: #0a0f17; border: 1px solid var(--sky); border-radius: 9px; padding: 10px 12px; font-size: 12px; white-space: pre-line; line-height: 1.5; }
#view-error { position: fixed; top: 90px; left: 25%; z-index: 9; background: #0a0f17; color: var(--amber); padding: 16px; }
@media (max-width: 920px) {
    .scene-toolbar { top: 54px; }
    #map2d { right: 0; height: 57vh; height: 57dvh; bottom: auto; }
    body[data-view-mode="2d"] .hero { left: 0; bottom: auto; padding: calc(58dvh + 18px) var(--pad) 24px; max-width: none; border-radius: 0; background: linear-gradient(to bottom, transparent 57dvh, var(--bg) 57dvh); }
    body[data-view-mode="2d"] .head { font-size: clamp(36px, 10vw, 60px); }
    .earth-attribution { right: 8px; top: 51dvh; bottom: auto; max-width: 85vw; font-size: 8px; }
    .aurora-map-legend { max-width: 185px; font-size: 10px; padding: 5px 7px; }
}

/* Compact controls stay within ordinary laptop height; explanatory text is on demand. */
.notify-group { position: relative; flex: none; }
.notify-group .notify-hint {
    position: absolute; z-index: 25; top: calc(100% + 5px); left: 0; right: 0;
    padding: 10px 12px; border: 1px solid var(--line-soft); border-radius: 9px;
    background: #0a0f17; box-shadow: 0 8px 20px rgba(0,0,0,.35);
    opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .15s;
}
.notify-group:hover .notify-hint, .notify-group:focus-within .notify-hint { opacity: 1; visibility: visible; }
.settings-meta { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 8px; }
.settings-meta .lang-row { padding: 7px 8px; flex-direction: column; align-items: flex-start; gap: 5px; min-width: 0; }
.settings-meta .ctrl-lbl { font-size: 11px; }
.settings-meta .seg { width: 100%; padding: 2px; gap: 2px; }
.settings-meta .seg button { flex: 1; font-size: 11px; padding: 6px 5px; }
.settings-meta .lang { width: 100%; justify-content: space-between; font-size: 12px; }
.settings-meta .lang a { padding: 6px 10px; }
.starlink-details summary { list-style: none; display: grid; place-items: center; width: 22px; height: 22px; border: 1px solid var(--line-soft); border-radius: 50%; color: var(--muted); font-size: 12px; cursor: pointer; }
.starlink-details summary::-webkit-details-marker { display: none; }
.starlink-details summary:hover { color: var(--ink); border-color: var(--sky); }
.starlink-explanation { position: absolute; top: calc(100% - 1px); left: -1px; right: -1px; padding: 3px 12px 12px; background: #070d15; border: 1px solid var(--line-soft); border-top: 0; border-radius: 0 0 10px 10px; }
.starlink-explanation .layer-note { margin: 4px 0 8px; }
.starlink-explanation .layer-source { color: var(--sky); }
@media (min-width: 921px) {
    .panel { gap: 10px; padding: 17px 16px 14px; }
    .panel > * { flex-shrink: 0; }
    .panel-clock { padding-bottom: 8px; font-size: 12px; }
    .panel .search { gap: 6px; }
    .panel .search input { height: 38px; font-size: 14px; padding: 0 10px; border-radius: 9px; }
    .panel .icon-btn { width: 38px; height: 38px; border-radius: 9px; }
    .panel .notify-btn { padding: 9px 11px; min-height: 38px; font-size: 13px; border-radius: 9px; }
    .panel-block { padding: 10px; gap: 8px; border-radius: 12px; }
    .panel .p-label { font-size: 10px; letter-spacing: .1em; gap: 6px; }
    .panel-head { gap: 4px 6px; }
    .panel .mini-toggle { padding: 4px 7px; font-size: 9px; letter-spacing: .02em; }
    #map { height: 150px; border-radius: 8px; }
    .panel .view-switch { padding: 3px; border-radius: 9px; }
    .panel .view-switch button { padding: 8px 4px; font-size: 10.5px; border-radius: 6px; }
    .panel .settings-meta .lang-row { padding: 5px 7px; gap: 3px; }
    .panel .settings-meta .seg button { padding: 5px 4px; font-size: 10.5px; }
    .panel .settings-meta .lang a { padding: 5px 8px; }
    .panel .adv-open { padding: 10px 11px; border-radius: 10px; gap: 7px; min-height: 38px; }
    .panel .adv-open-label { font-size: 10px; letter-spacing: .08em; }
    .panel .adv-peek { font-size: 12px; }
    .panel-foot { padding-top: 9px; margin-top: 0; gap: 5px; font-size: 10px; letter-spacing: .015em; line-height: 1.45; }
    .foot-guide { padding: 2px 0; font-size: 11px; }
    .foot-by { flex-direction: row; gap: 7px; padding: 3px 0; }
    .foot-by-lbl { font-size: 8px; letter-spacing: .09em; }
    .foot-by img { width: 74px; }
}
@media (max-width: 920px) {
    .panel { gap: 14px; }
    .location-block #map { height: 185px; }
    .settings-block { gap: 9px; }
    .panel-foot { gap: 8px; padding-top: 12px; }
    .settings-meta .lang-row { align-items: stretch; }
    .settings-meta .seg button, .settings-meta .lang a { min-height: 36px; display: grid; place-items: center; }
}

/* Each topic shares the globe and location, with its own readings and time control. */
body:not([data-app-mode="starlink"]) .starlink-only,
body[data-app-mode="starlink"] .aurora-only { display: none !important; }
body[data-app-mode="starlink"] .scene-toolbar .app-mode-switch button[aria-pressed="true"] { color: var(--sky); background: rgba(111,200,232,.17); }
.starlink-hero .hero-h1 { color: var(--sky); }
.starlink-hero .head { font-size: clamp(30px, 3.4vw, 48px); line-height: 1.08; max-width: 13ch; }
.starlink-hero .subline { max-width: 42ch; }
.starlink-hero .rule { background: var(--sky); }
.starlink-metrics { grid-template-columns: repeat(2,minmax(0,1fr)); gap: 18px 30px; max-width: 360px; }
.starlink-metrics .m-value { font-size: 22px; color: var(--sky); }
.starlink-geometry-note { color: var(--muted); font-size: 11px; line-height: 1.5; max-width: 45ch; margin: 18px 0 0; }
.starlink-planner { border-color: rgba(111,200,232,.25); }
.starlink-planner .p-label::before { background: var(--sky); }
.starlink-planner .mini-toggle { color: var(--sky); }
.starlink-time-row { display: flex; align-items: center; gap: 10px; min-width: 0; }
#starlink-time-slider { min-width: 0; width: 100%; margin: 5px 0; accent-color: var(--sky); cursor: pointer; }
#starlink-time-display { flex: none; min-width: 48px; font-size: 11px; font-variant-numeric: tabular-nums; color: var(--sky); text-align: right; }
#starlink-pass-status { margin: 0; font-size: 11px; line-height: 1.4; color: var(--muted); }
.starlink-pass-list { padding: 0; margin: 0; list-style: none; display: grid; gap: 5px; }
.starlink-pass-list:empty { display: none; }
.starlink-pass-list button {
    display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; min-height: 33px;
    padding: 6px 8px; background: rgba(111,200,232,.04); border: 1px solid var(--line-soft);
    border-radius: 7px; color: var(--ink); font: inherit; font-size: 11px; text-align: left; cursor: pointer;
}
.starlink-pass-list button:hover, .starlink-pass-list button[aria-pressed="true"] { border-color: var(--sky); background: rgba(111,200,232,.11); }
.starlink-pass-list button span { min-width: 0; }
.starlink-pass-list button small { font-size: 10px; color: var(--muted); }
.starlink-telemetry { position: relative; padding-top: 7px; border-top: 1px solid var(--line-soft); }
.starlink-telemetry-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.starlink-telemetry .layer-status { margin: 0; font-size: 10px; line-height: 1.4; color: var(--muted); overflow-wrap: anywhere; }
.starlink-telemetry .starlink-details { flex: none; }
.starlink-telemetry .starlink-explanation { z-index: 30; top: calc(100% + 5px); left: 0; right: 0; border: 1px solid var(--line-soft); border-radius: 9px; padding: 9px 11px; box-shadow: 0 10px 25px rgba(0,0,0,.45); }
@media (min-width: 921px) {
    .settings-block { display: grid; grid-template-columns: minmax(0,1fr) 36px; gap: 7px; }
    .settings-block > .panel-head, .settings-block > .settings-meta { grid-column: 1 / -1; }
    .settings-block #btn-night { width: 36px; min-height: 36px; padding: 8px; justify-content: center; }
    .settings-block #btn-night span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
    body[data-app-mode="starlink"] .panel { gap: 8px; padding-top: 15px; }
    body[data-app-mode="starlink"] .location-block #map { height: 120px; }
    body[data-app-mode="starlink"] .panel-block { gap: 7px; }
    body[data-app-mode="starlink"] .panel-foot { gap: 4px; }
    body[data-app-mode="starlink"] .starlink-planner .panel-head .p-label { font-size: 9px; letter-spacing: .04em; }
    body[data-view-mode="2d"] .starlink-hero .head { font-size: 30px; }
}
@media (max-width: 920px) {
    .scene-toolbar { gap: 5px; }
    .scene-toolbar .seg button { padding: 8px 10px; }
    .starlink-hero .head { font-size: clamp(32px, 9vw, 46px); }
    .starlink-hero .starlink-metrics { margin-top: 4px; }
    .starlink-time-row { min-height: 38px; }
    .starlink-pass-list button { min-height: 44px; }
    .starlink-details summary { width: 30px; height: 30px; }
}
@media (min-width: 921px) {
    .location-block .leaflet-control-attribution { font-size: 9px; line-height: 1.2; letter-spacing: 0; }
    body[data-app-mode="starlink"] .panel { gap: 6px; padding-top: 13px; padding-bottom: 10px; }
    body[data-app-mode="starlink"] .location-block #map { height: 110px; }
    body[data-app-mode="starlink"] .panel-block { gap: 5px; }
    body[data-app-mode="starlink"] .starlink-pass-list { gap: 4px; }
    body[data-app-mode="starlink"] .starlink-pass-list button { padding: 5px 8px; }
    body[data-app-mode="starlink"] .starlink-planner .panel-head { gap: 3px; }
}

@media (min-width: 921px) and (max-height: 840px) {
    .starlink-pass-list { max-height: 176px; overflow-y: auto; scrollbar-width: thin; }
}

.foot-links { display: flex; justify-content: center; align-items: center; gap: 3px 12px; flex-wrap: wrap; }
body[data-app-mode="starlink"] .starlink-clock .dot { background: var(--sky); box-shadow: 0 0 9px rgba(111,200,232,.65); }
body[data-app-mode="starlink"] .foot-guide { color: var(--sky); }
.starlink-telemetry .layer-status { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

/* Language is a live segmented control, with ordinary links as its no-JS fallback. */
.lang-switch a { flex: 1; display: grid; place-items: center; border: 0; border-radius: 6px; padding: 6px 9px; color: var(--faint); background: transparent; font-size: 11.5px; line-height: 1.2; letter-spacing: .05em; text-decoration: none; transition: background .15s, color .15s; }
.lang-switch a:hover { color: var(--ink); }
.lang-switch a[aria-pressed="true"] { color: var(--green); background: linear-gradient(180deg, rgba(46,227,155,.24), rgba(46,227,155,.11)); box-shadow: inset 0 0 0 1px rgba(46,227,155,.35); }
.settings-meta .lang-switch { width: 100%; padding: 2px; gap: 2px; }
.settings-meta .lang-switch a { padding: 6px 5px; }
.starlink-observe-controls { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin-top: 16px; pointer-events: auto; }
.all-satellites-control { grid-column: 1 / -1; display: flex; align-items: center; gap: 7px; min-height: 23px; color: var(--muted); font-size: 11px; cursor: pointer; }
.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; }
#all-satellites { width: 15px; height: 15px; margin: 0; accent-color: var(--sky); }
#starlink-track { padding: 7px 10px; border: 1px solid rgba(111,200,232,.45); border-radius: 7px; color: var(--sky); background: rgba(111,200,232,.07); font: inherit; font-size: 11px; cursor: pointer; }
#starlink-track[aria-pressed="true"] { color: var(--ink); background: rgba(111,200,232,.2); border-color: var(--sky); }
.starlink-count { margin: 6px 0 0; color: var(--muted); font-size: 10px; font-variant-numeric: tabular-nums; }
.starlink-observe-controls ~ .starlink-geometry-note { margin-top: 10px; }
@media (min-width: 921px) { .panel .settings-meta .lang-switch a { padding: 5px 4px; font-size: 10.5px; } }
@media (max-width: 920px) {
    .settings-meta .lang-switch a { min-height: 36px; }
    .starlink-observe-controls { gap: 10px 16px; }
    #starlink-track { min-height: 36px; padding: 8px 12px; }
    .all-satellites-control { min-height: 36px; }
}

/* Follow mode keeps attention on the selected satellite. */
body[data-satellite-tracking="true"] .globe-tip { display: none; }

.all-satellites-status { grid-column: 1 / -1; margin: 0; color: var(--muted); font-size: 10px; line-height: 1.35; overflow-wrap: anywhere; }
.all-satellites-status[hidden] { display: none; }

/* Each train is selected together with its next pass. */
.starlink-pass-list .train-card { display: flex; flex-direction: column; align-items: stretch; justify-content: flex-start; gap: 4px; padding: 10px; border-radius: 9px; }
.train-card-heading { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.train-card-heading strong { font-size: 11px; font-weight: 600; }
.train-card-heading small { white-space: nowrap; }
.train-card-time { color: var(--sky); font-size: 17px; font-weight: 500; font-variant-numeric: tabular-nums; letter-spacing: .02em; }
.starlink-pass-list .train-card-details { font-size: 10px; line-height: 1.35; }
.starlink-pass-list .train-card-next { margin-top: 2px; padding-top: 5px; border-top: 1px solid var(--line-soft); font-size: 10px; }
.starlink-pass-list .train-card:focus-visible { outline: 2px solid var(--sky); outline-offset: -2px; }
.all-satellites-control { position: relative; justify-content: space-between; min-height: 34px; gap: 10px; padding: 5px 8px; border: 1px solid var(--line-soft); border-radius: 8px; background: rgba(3,8,13,.45); }
#all-satellites { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.satellite-switch-track { flex: none; position: relative; width: 32px; height: 18px; border: 1px solid var(--line); border-radius: 6px; background: #080d12; transition: background .15s, border-color .15s; }
.satellite-switch-track::after { content: ''; position: absolute; top: 3px; left: 3px; width: 10px; height: 10px; border-radius: 3px; background: var(--faint); transition: transform .15s, background .15s; }
#all-satellites:checked + .satellite-switch-track { border-color: rgba(111,200,232,.6); background: rgba(111,200,232,.18); }
#all-satellites:checked + .satellite-switch-track::after { transform: translateX(14px); background: var(--sky); }
.all-satellites-control:has(#all-satellites:focus-visible) { outline: 2px solid var(--sky); outline-offset: 2px; }
@media (max-width: 920px) {
    .starlink-pass-list .train-card { padding: 12px; gap: 5px; }
    .train-card-heading strong { font-size: 13px; }
    .train-card-time { font-size: 20px; }
    .starlink-pass-list .train-card-details, .starlink-pass-list .train-card-next { font-size: 11px; }
    .all-satellites-control { min-height: 42px; padding: 7px 10px; font-size: 12px; }
}

/* CSS2D positions the 11px anchor; label width must never shift its dot. */
.globe-pin { display: block; position: relative; width: 11px; height: 11px; }
.globe-pin .gp-dot { display: block; box-sizing: border-box; width: 11px; height: 11px; }
.globe-pin .gp-lbl { position: absolute; left: 18px; top: 50%; transform: translateY(-50%); white-space: nowrap; }
