:root {
    --bg: #0b1020;
    --panel: #141a2e;
    --text: #e6e9f2;
    --muted: #8a93ad;
    --line: #263056;
    --accent: #4da3ff;
    /* Ratings read as ratings only if they are the colour everything else on the web uses for one. */
    --star: #f5c451;
    --warn: #ff6b6b;
    --ok: #3ddc84;
}

* { box-sizing: border-box; }

body { margin: 0; background: var(--bg); color: var(--text);
    font: 15px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* --- Header ------------------------------------------------------------------------------------ */
/* Sticky, because the map and the sighting list are both long scrolls and losing the nav halfway down
   is the most common way people end up using the browser back button as navigation. */
.topbar {
    position: sticky; top: 0; z-index: 900;
    display: flex; align-items: center; gap: 18px;
    padding: 12px 24px;
    background: rgba(7, 11, 23, .82);
    backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--line);
}
/* A hairline of accent under the bar: enough to separate it from a dark map tile, which the flat
   1px border alone did not do. */
.topbar::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
    background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--accent) 45%, transparent), transparent);
}

.brand { display: inline-flex; align-items: center; gap: 10px; flex: none; }
.brand:hover { text-decoration: none; }
.brand .mark { width: 26px; height: 26px; flex: none; color: var(--accent); }
.brand .mark circle { fill: none; stroke: var(--muted); stroke-width: 1.4; opacity: .55; }
.brand .mark .sweep { fill: url(#rsweep); stroke: none; }
.brand .mark .beam { stroke: var(--accent); stroke-width: 1.2; stroke-linecap: round; opacity: .75; fill: none; }
.brand .mark .blip { fill: var(--accent); stroke: none; }

/* A scope that actually sweeps. Both animations run on the same 4s clock and the blip sits on the
   beam's leading edge, so the return lights up as the beam reaches it and then decays over the rest of
   the revolution — which is the behaviour the shape was always implying and never doing.

   transform-box: view-box is what makes transform-origin resolve against the 32x32 viewBox rather than
   the group's own bounding box, whose centre is out at the rim. */
@keyframes radar-sweep { to { transform: rotate(360deg); } }
@keyframes radar-blip {
    0%   { opacity: 1; }
    18%  { opacity: .55; }
    60%  { opacity: .1; }
    99%  { opacity: .1; }
    100% { opacity: 1; }
}
.brand .mark .sweeper {
    transform-box: view-box;
    transform-origin: 16px 16px;
    animation: radar-sweep 4s linear infinite;
}
.brand .mark .blip { animation: radar-blip 4s linear infinite; }
.brand:hover .mark circle { opacity: .8; }

.wordmark { font-size: 18px; font-weight: 700; letter-spacing: -.2px; color: var(--text); white-space: nowrap; }
.wordmark span { color: var(--accent); }

.tagline { color: var(--muted); font-size: 13px; }

/* Horizontal scroll rather than a hamburger: five links fit on a phone if they are allowed to slide,
   and a menu that has to be opened hides the map link behind a tap. */
.nav { display: flex; align-items: center; gap: 4px; overflow-x: auto; scrollbar-width: none; }
.nav::-webkit-scrollbar { display: none; }
.nav a {
    position: relative; color: var(--muted); font-size: 14px; white-space: nowrap;
    padding: 7px 11px; border-radius: 7px; transition: color .15s, background .15s;
}
.nav a:hover { color: var(--text); background: rgba(255,255,255,.05); text-decoration: none; }
/* NavLink stamps .active on the current route, so the header answers "where am I" without a title. */
.nav a.active { color: var(--text); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.nav a.active::after {
    content: ''; position: absolute; left: 11px; right: 11px; bottom: 2px; height: 2px;
    border-radius: 2px; background: var(--accent);
}
.nav a.mod { color: var(--warn); }

.authnav { margin-left: auto; display: flex; align-items: center; gap: 12px; font-size: 14px; flex: none; }
.authnav .who { display: inline-flex; align-items: center; gap: 8px; color: var(--text); }
.authnav .who:hover { text-decoration: none; }
.authnav .who:hover .whoname { text-decoration: underline; }
.avatar {
    width: 26px; height: 26px; border-radius: 50%; flex: none;
    display: inline-grid; place-items: center;
    font-size: 12px; font-weight: 700; color: var(--bg);
    background: linear-gradient(135deg, var(--accent), #7ec4ff);
}
.btn.ghost {
    padding: 6px 14px; border-radius: 999px; font-size: 14px; font-weight: 600;
    color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.btn.ghost:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); text-decoration: none; }

@media (max-width: 760px) {
    .topbar { gap: 12px; padding: 10px 14px; flex-wrap: wrap; }
    .nav { order: 3; width: 100%; padding-bottom: 2px; }
    .authnav { gap: 10px; }
    .authnav .whoname { display: none; }   /* the avatar still identifies who is signed in */
}
.linkbtn { background: none; border: none; color: var(--accent); font-size: 14px; cursor: pointer; padding: 0; }
.linkbtn:hover { text-decoration: underline; }
.linkbtn.vote { color: var(--ok); font-size: 15px; margin-top: 8px; }
.linkbtn.danger { color: var(--warn); }
.report { margin-top: 18px; }
.report summary { color: var(--muted); font-size: 13px; cursor: pointer; }
.reportbox { display: flex; align-items: center; gap: 10px; margin-top: 8px; flex-wrap: wrap; }
.reportbox input { background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
    padding: 8px 10px; color: var(--text); font-size: 14px; min-width: 220px; }

.authform { display: flex; flex-direction: column; gap: 6px; max-width: 320px; margin: 16px 0; }
.authform label { font-size: 13px; color: var(--muted); margin-top: 6px; }
.authform .hint { color: var(--muted); opacity: .8; font-weight: 400; }
.authform input { background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
    padding: 11px 12px; color: var(--text); font-size: 15px; }
.authform button { margin-top: 10px; }

/* Visually hidden, still announced by screen readers — for inputs that can't carry a visible label. */
.vh { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
.authform button { background: var(--accent); color: #06101f; border: none; border-radius: 8px;
    padding: 12px; font-size: 15px; font-weight: 600; cursor: pointer; }
.banner { padding: 10px 14px; border-radius: 8px; margin: 12px 0; max-width: 420px; }
.banner.error { background: #3a1420; color: #ffb3b3; border: 1px solid #7a2233; }
.banner.warn { background: #3a2c10; color: #ffd98a; border: 1px solid #7a5a1c; }
.chk { display: flex; align-items: flex-start; gap: 8px; color: var(--muted); font-size: 13px; }
.chk input { width: auto; margin-top: 2px; }

.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; margin: 16px 0; }
.tile { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; }
.tile .k { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }
.tile .v { font-size: 24px; font-weight: 700; margin-top: 3px; }
.tile .v.ok { color: var(--ok); }

main { padding: 20px 24px; max-width: 1200px; margin: 0 auto; }
.foot { color: var(--muted); font-size: 12px; text-align: center; padding: 20px; border-top: 1px solid var(--line); }
.footlinks { margin-top: 6px; }
.social { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 8px; margin-bottom: 12px; }
.social-label { color: var(--muted); font-size: 13px; margin-right: 4px; }
.social-pill { display: inline-block; padding: 6px 14px; border: 1px solid var(--line); border-radius: 999px;
    color: var(--text, inherit); font-size: 13px; text-decoration: none; min-height: 20px; }
.social-pill:hover, .social-pill:focus-visible { border-color: var(--accent, #3b82f6); color: var(--accent, #3b82f6); }
.foothelp { margin-bottom: 8px; }
.footsep { color: var(--muted); margin: 0 6px; }

h1 { font-size: 22px; margin: 0 0 4px; }
h2 { font-size: 14px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin: 20px 0 10px; }
.sub { color: var(--muted); margin: 0 0 16px; }

#map { height: 460px; width: 100%; border-radius: 12px; border: 1px solid var(--line);
    background: #0a0f1e; overflow: hidden; }
.leaflet-container { background: #0a0f1e; }
/* Popup still — small on purpose: a map view can open many of these. */
.popupthumb { display: block; width: 190px; height: 104px; object-fit: cover; border-radius: 6px;
    background: #0a0f1e; margin-bottom: 6px; }

.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 12px; margin-top: 16px; }
.card { display: block; background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
    padding: 14px; color: var(--text); }
.card:hover { border-color: var(--accent); text-decoration: none; }
.card .when { color: var(--muted); font-size: 12px; }
.card .label { font-size: 16px; font-weight: 600; margin: 4px 0; }
.tag { display: inline-block; font-size: 11px; padding: 2px 7px; border-radius: 999px;
    border: 1px solid var(--line); color: var(--muted); margin-right: 6px; }
.tag.maneuver { color: var(--warn); border-color: var(--warn); }
.tag.multiple { color: #ffb86b; border-color: #ffb86b; }
.tag.eligible { color: var(--ok); border-color: var(--ok); }
.tag.corroborated { color: #0b1020; background: var(--ok); border-color: var(--ok); font-weight: 600; }

/* Sighting stills (ADR 0023 §2). Fixed box + object-fit so a grid of mixed capture resolutions still
   lines up, and so one oversized frame can't stretch a card. */
.thumb { display: block; width: 100%; height: 132px; object-fit: cover; border-radius: 8px;
    background: #0a0f1e; border: 1px solid var(--line); margin-bottom: 10px; }
.thumb.ph { display: flex; align-items: center; justify-content: center; color: var(--muted);
    font-size: 11px; letter-spacing: .06em; text-transform: uppercase; }
/* Capped in both directions: the source frame's aspect ratio is whatever the capture head produced, and
   an unconstrained height:auto lets a square or portrait still swallow the whole page. */
.detailthumb { display: block; width: 100%; max-width: 640px; max-height: 420px; height: auto;
    object-fit: contain; border-radius: 8px; background: #0a0f1e; margin: 0 auto; }
.trackplot video { display: block; width: 100%; max-width: 640px; max-height: 420px; margin: 0 auto;
    border-radius: 8px; background: #0a0f1e; }

/* Clip availability notes — three genuinely different situations, told apart at a glance (ADR 0023 /
   "Option E"): archived and reliable, relayed and maybe, or simply absent. */
.clipnote { margin: 10px 0 0; font-size: 13px; }
.clipnote.ok { color: var(--ok); }
.clipnote.wait { color: #ffc34d; }

.corrbox { background: rgba(61,220,132,.08); border: 1px solid var(--ok); border-radius: 10px;
    padding: 14px 16px; margin: 12px 0 4px; }
.corrhead { color: var(--ok); font-weight: 700; font-size: 16px; }
.corrbox table.meta thead th { color: var(--muted); text-align: left; font-weight: 500; font-size: 12px; padding: 6px 8px; }

.empty { color: var(--muted); background: var(--panel); border: 1px dashed var(--line);
    border-radius: 10px; padding: 24px; text-align: center; margin-top: 16px; }

.credbar { height: 5px; background: var(--line); border-radius: 3px; margin-top: 10px; overflow: hidden; }
.credbar.wide { height: 8px; max-width: 320px; }
.credbar span { display: block; height: 100%; border-radius: 3px;
    background: linear-gradient(90deg, #ff6b6b, #ffc34d 55%, #3ddc84); }
.credpct { color: var(--muted); font-size: 11px; margin-top: 4px; }
.credbox { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 14px 16px; }
.credbig { font-size: 28px; font-weight: 700; }

table.meta { width: 100%; border-collapse: collapse; max-width: 560px; }
table.meta td { padding: 7px 8px; border-bottom: 1px solid var(--line); }
table.meta td:first-child { color: var(--muted); width: 42%; }

.trackplot { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 12px; }
.back { display: inline-block; margin-bottom: 12px; }
.mono { font-family: ui-monospace, Consolas, monospace; font-size: 12px; color: var(--muted); word-break: break-all; }

/* Keyboard focus. The custom .linkbtn/.card/nav styling leaves the UA default outline, which is close to
   invisible on this dark background — make every focusable thing show the same obvious ring. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.card:focus-visible { outline-offset: 3px; border-color: var(--accent); }
.authform input:focus-visible, .reportbox input:focus-visible { outline-offset: 0; border-color: var(--accent); }

/* Circuit failure / dropped WebSocket. Blazor flips this to display:block; without it an unhandled
   event-handler exception or a lost connection just freezes the page with no feedback at all. */
#blazor-error-ui { display: none; position: fixed; left: 0; right: 0; bottom: 0; z-index: 1000;
    background: #3a1420; color: #ffb3b3; border-top: 1px solid #7a2233;
    padding: 12px 20px; font-size: 14px; text-align: center;
    box-shadow: 0 -6px 24px rgba(0, 0, 0, .45); }
#blazor-error-ui .reload { color: #ffd7d7; text-decoration: underline; margin-left: 10px; }
#blazor-error-ui .dismiss { cursor: pointer; float: right; padding: 0 6px; color: #ffb3b3; }

/* --- Set up a camera (/setup) ------------------------------------------------------------------- */
.lede { color: var(--muted); font-size: 15px; max-width: 70ch; margin: 0 0 18px; }
.panel { background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
         padding: 16px 20px; margin: 0 0 16px; }
.panel h2 { margin-top: 0; }
.panel p { max-width: 74ch; }
.panel .muted { color: var(--muted); font-size: 13px; }
.onestep { max-width: 74ch; margin: 10px 0 14px; padding: 14px 16px; overflow-x: auto;
  background: #0d1426; border: 1px solid var(--line); border-radius: 8px; font-size: 16px; }
.onestep code { background: none; border: 0; padding: 0; font-size: inherit; user-select: all; }
.panel details { max-width: 74ch; }
.panel details summary { cursor: pointer; font-weight: 600; margin: 6px 0; }
.reqs, .steps { max-width: 74ch; padding-left: 20px; }
.reqs li, .steps li { margin: 8px 0; font-size: 14px; }
.steps li { line-height: 1.55; }
.panel code { background: #0d1426; border: 1px solid var(--line); border-radius: 5px;
              padding: 1px 6px; font-family: ui-monospace, Consolas, monospace; font-size: 13px; }
.btn { display: inline-block; border-radius: 8px; padding: 9px 18px; font-size: 14px;
       border: 1px solid var(--line); background: var(--panel); color: var(--text);
       text-decoration: none; cursor: pointer; }
.btn.primary { background: #1f2a4d; border-color: #3ddc84; color: #3ddc84; font-weight: 600; }
.btn.primary:hover { background: #26325c; }

/* --- Auth pages (login / register / password reset) ---------------------------------------------- */
/* Centred in the viewport rather than pinned to the top-left: these pages hold one short form, and a
   320px form in the corner of a wide screen reads as an unfinished page. The min-height subtracts the
   header and footer so the block is centred within the visible area, not the document. */
.authpage { min-height: calc(100vh - 230px); display: flex; flex-direction: column;
            align-items: center; justify-content: center; text-align: center; padding: 3vh 0 6vh; }
.authpage h1 { margin-bottom: 2px; }
.authpage .banner { max-width: 360px; }
/* The fields keep their left-aligned labels — only the block is centred. */
.authpage .authform { width: 320px; max-width: 100%; text-align: left; margin: 12px auto; }
.authpage .sub, .authpage > p { max-width: 380px; }
/* Honeypot field: off-screen rather than display:none, since some bots skip hidden inputs. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* --- Map type filters -------------------------------------------------------------------------- */
/* Chips read as switches rather than links: a filter that looks clickable but not stateful gets
   toggled twice by people checking whether it did anything. */
.filters { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; }

.chip {
    display: inline-flex; align-items: center; gap: 8px;
    background: var(--panel); color: var(--muted);
    border: 1px solid var(--line); border-radius: 999px;
    padding: 7px 13px; font-size: 13px; font-weight: 500;
    cursor: pointer; transition: color .15s, border-color .15s, background .15s, box-shadow .15s;
}
.chip:hover { color: var(--text); border-color: #3a446f; }
.chip .dot {
    width: 9px; height: 9px; border-radius: 50%;
    background: currentColor; opacity: .45; transition: opacity .15s, box-shadow .15s;
}
.chip .n {
    font-variant-numeric: tabular-nums; font-size: 11px;
    background: rgba(255,255,255,.06); border-radius: 999px; padding: 1px 7px; color: var(--muted);
}

/* On: the type's own colour carries the state, so the legend and the markers agree without a key. */
.chip.on { color: var(--tone); border-color: var(--tone); background: color-mix(in srgb, var(--tone) 12%, var(--panel)); }
.chip.on .dot { opacity: 1; box-shadow: 0 0 8px var(--tone); }
.chip.on .n { background: color-mix(in srgb, var(--tone) 22%, transparent); color: var(--text); }

/* A type with nothing to show still appears — its absence is information — but recedes. */
.chip.none { opacity: .45; }
.chip.reset { color: var(--muted); border-style: dashed; }

.chip.t-witness     { --tone: #7bd88f; }
.chip.t-uap         { --tone: #ff4fd8; }
.chip.t-maneuver    { --tone: #46e6b0; }
.chip.t-unexplained { --tone: #ff9f4d; }
.chip.t-unknown     { --tone: #8a93ad; }
.chip.t-star        { --tone: #ffd85e; }
.chip.t-satellite   { --tone: #4da3ff; }
.chip.t-aircraft    { --tone: #b48cff; }
.chip.t-meteor      { --tone: #ff6b6b; }
.chip.t-cloud       { --tone: #8d99a4; }
.chip.t-inconclusive{ --tone: #6f7a84; }
.chip.t-lightning   { --tone: #d6dcff; }

/* Leaflet popups inherit the site's palette rather than Leaflet's white default. */
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
    background: var(--panel); color: var(--text);
    border: 1px solid var(--line); box-shadow: 0 8px 24px rgba(0,0,0,.45);
}
.leaflet-popup-content { margin: 10px 12px; font-size: 13px; line-height: 1.5; }
.leaflet-popup-content a { color: var(--accent); }
.leaflet-container a.leaflet-popup-close-button { color: var(--muted); }

/* ============================================================================================
   The globe hero
   ============================================================================================
   Height is reserved in CSS and never depends on the script. That is a Core Web Vitals decision,
   not a styling one: the globe is lazy-loaded, so if this box sized itself to its content it would
   be 0px until the bundle arrived and then shove the whole page down — a large layout shift, and
   CLS is the one vital that a decorative element can wreck all on its own.
   ---------------------------------------------------------------------------------------------- */

.globe-hero {
    position: relative;
    width: 100%;
    height: 520px;            /* reserved up front — see above */
    margin: 8px 0 4px;
    border-radius: 16px;
    overflow: hidden;
    background:
        radial-gradient(ellipse at 50% 45%, rgba(77, 163, 255, .10), transparent 62%),
        radial-gradient(ellipse at 20% 12%, rgba(180, 140, 255, .08), transparent 55%),
        #05070f;
    /* A starfield, so the box is never an empty rectangle while the bundle is still in flight. */
    box-shadow: inset 0 0 120px rgba(0, 0, 0, .8);
    contain: layout paint;
}

.globe-hero::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image:
        radial-gradient(1px 1px at 12% 22%, #fff, transparent),
        radial-gradient(1px 1px at 68% 14%, #cfe3ff, transparent),
        radial-gradient(1px 1px at 84% 62%, #fff, transparent),
        radial-gradient(1px 1px at 32% 78%, #dfe9ff, transparent),
        radial-gradient(1px 1px at 52% 38%, #fff, transparent),
        radial-gradient(1px 1px at 8% 58%, #b9d4ff, transparent),
        radial-gradient(1px 1px at 92% 30%, #fff, transparent),
        radial-gradient(1px 1px at 44% 88%, #cfe3ff, transparent);
    opacity: .55;
    pointer-events: none;
}

.globe-hero canvas { display: block; }

/* The script never got there, or the CDN was down. The page is still complete without it. */
.globe-failed::after,
.globe-empty::after {
    content: "";
}

.globe-cap {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    padding: 26px 18px 12px;
    background: linear-gradient(to top, rgba(5, 7, 15, .92), transparent);
    font-size: 12px;
    color: var(--muted);
    text-align: center;
    pointer-events: none;
}

@media (max-width: 700px) {
    .globe-hero { height: 360px; }
}

/* ---- The craft ------------------------------------------------------------------------------
   Each sighting arrives as a saucer that drops out of orbit, flashes a beam, and leaves a marker.
   globe.gl positions the wrapper at the sighting's coordinates; everything below is CSS, which is
   why it costs no frames on the render loop and honours reduced-motion for free.
   -------------------------------------------------------------------------------------------- */

.ufo {
    position: relative;
    width: 0; height: 0;
    cursor: pointer;
    --tone: #8a93ad;
}

.ufo-craft {
    position: absolute;
    left: -13px; top: -20px;
    width: 26px; height: 14px;
    pointer-events: auto;
}

.ufo-hull {
    position: absolute;
    left: 0; top: 6px;
    width: 26px; height: 7px;
    border-radius: 50%;
    background: linear-gradient(to bottom, #dfe7f5 0%, #94a3bd 45%, #4a5468 100%);
    box-shadow: 0 0 10px 1px var(--tone), 0 0 22px rgba(255, 255, 255, .25);
}

.ufo-dome {
    position: absolute;
    left: 8px; top: 0;
    width: 10px; height: 9px;
    border-radius: 50% 50% 40% 40%;
    background: radial-gradient(circle at 40% 30%, #fff, var(--tone) 70%);
    opacity: .95;
}

/* Three underside lights, chasing. */
.ufo-lights {
    position: absolute;
    left: 3px; top: 10px;
    width: 20px; height: 3px;
    background-image: radial-gradient(circle, var(--tone) 40%, transparent 45%);
    background-size: 7px 3px;
    background-repeat: repeat-x;
    animation: ufo-blink 1.1s steps(3) infinite;
}

/* The beam only exists during arrival: a cone that stabs down at the coordinates, then is gone. */
.ufo-beam {
    position: absolute;
    left: -9px; top: -8px;
    width: 18px; height: 30px;
    clip-path: polygon(38% 0%, 62% 0%, 100% 100%, 0% 100%);
    background: linear-gradient(to bottom, var(--tone), transparent 85%);
    opacity: 0;
    transform-origin: top center;
    pointer-events: none;
}

/* What is left behind once the craft has gone: the actual data point. */
.ufo-mark {
    position: absolute;
    left: -3px; top: -3px;
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--tone);
    box-shadow: 0 0 8px 2px var(--tone);
}

/* A sighting a person watched and stood behind wears a ring. Verification is a different claim from
   classification (ADR 0040), so it gets a different visual channel — shape, not colour. */
.ufo-verified .ufo-mark {
    box-shadow: 0 0 8px 2px var(--tone), 0 0 0 3px rgba(123, 216, 143, .85), 0 0 14px 4px rgba(123, 216, 143, .5);
}

@keyframes ufo-blink { to { background-position: 21px 0; } }

/* How many sightings share this spot. One camera produces all of its detections at one coordinate, so
   for most sites this number IS the story — it says "this is a watched place", not "this is a busy sky". */
.ufo-count {
    position: absolute;
    left: 7px; top: -9px;
    min-width: 17px; height: 17px;
    padding: 0 4px;
    border-radius: 999px;
    background: rgba(5, 7, 15, .92);
    border: 1px solid var(--tone);
    color: var(--tone);
    font: 600 10px/16px system-ui, sans-serif;
    text-align: center;
    pointer-events: none;
    white-space: nowrap;
}

/* ---- Orbs -----------------------------------------------------------------------------------
   The other craft. Blue, white or red — the three colours people actually use when describing one.
   Note the orb's colour is atmosphere and carries NO data: the marker it leaves behind is the
   classification tone, and that is the only colour on the globe that means anything.
   -------------------------------------------------------------------------------------------- */

.ufo-orb-core {
    position: absolute;
    left: 5px; top: 1px;
    width: 16px; height: 16px;
    border-radius: 50%;
    background: radial-gradient(circle at 38% 32%, #fff 0%, var(--orb) 48%, color-mix(in srgb, var(--orb) 55%, #000) 100%);
    box-shadow:
        0 0 10px 2px var(--orb),
        0 0 26px 6px color-mix(in srgb, var(--orb) 60%, transparent),
        inset 0 0 6px rgba(255, 255, 255, .7);
    animation: orb-breathe 2.4s ease-in-out infinite;
}

/* The soft bloom a bright point picks up through a lens — the thing that makes a star look like a disc. */
.ufo-orb-halo {
    position: absolute;
    left: -3px; top: -7px;
    width: 32px; height: 32px;
    border-radius: 50%;
    background: radial-gradient(circle, color-mix(in srgb, var(--orb) 40%, transparent) 0%, transparent 68%);
    pointer-events: none;
    animation: orb-breathe 2.4s ease-in-out infinite reverse;
}

@keyframes orb-breathe {
    0%, 100% { transform: scale(1);    opacity: .95; }
    50%      { transform: scale(1.14); opacity: 1; }
}

/* An orb's beam takes the orb's own colour rather than the classification tone — it is light coming
   off the craft, so it belongs to the craft's channel. */
.ufo-orb .ufo-beam { background: linear-gradient(to bottom, var(--orb), transparent 85%); }

/* Arrival. The craft starts far above and off to one side — reading as "out of space" — falls into
   place, fires the beam, then fades out and hands over to the marker. */
@keyframes ufo-descend {
    0%   { transform: translate3d(var(--drift, 90px), -560px, 0) scale(.15); opacity: 0; }
    12%  { opacity: 1; }
    72%  { transform: translate3d(0, 0, 0) scale(1); opacity: 1; }
    88%  { transform: translate3d(0, 0, 0) scale(1); opacity: 1; }
    100% { transform: translate3d(0, -6px, 0) scale(.6); opacity: 0; }
}

@keyframes ufo-beam-fire {
    0%, 70% { opacity: 0; transform: scaleY(.2); }
    78%     { opacity: .85; transform: scaleY(1); }
    100%    { opacity: 0; transform: scaleY(1.1); }
}

@keyframes ufo-mark-land {
    0%, 74% { opacity: 0; transform: scale(.2); }
    82%     { opacity: 1; transform: scale(2.2); }
    100%    { opacity: 1; transform: scale(1); }
}

.ufo-arriving .ufo-craft { animation: ufo-descend 3.4s cubic-bezier(.4, 0, .2, 1) var(--delay) both; }
.ufo-arriving .ufo-beam  { animation: ufo-beam-fire 3.4s ease-out var(--delay) both; }
.ufo-arriving .ufo-mark  { animation: ufo-mark-land 3.4s ease-out var(--delay) both; }

/* Anything not arriving is already landed: marker only, no craft. */
.ufo:not(.ufo-arriving) .ufo-craft,
.ufo:not(.ufo-arriving) .ufo-beam { display: none; }

@media (prefers-reduced-motion: reduce) {
    /* A continuously rotating mark in a sticky header is exactly the kind of thing this setting is
       asking us to stop. It parks as the static scope it was before, blip lit. */
    .brand .mark .sweeper, .brand .mark .blip { animation: none !important; }
    .ufo-craft, .ufo-beam { display: none !important; }
    .ufo-orb-core, .ufo-orb-halo { animation: none !important; }
    .ufo-mark { animation: none !important; opacity: 1; transform: none; }
    .ufo-lights { animation: none; }
}

/* ============================================================================================
   Home page
   ============================================================================================ */

.hero { margin-bottom: 34px; }
.hero h1 { font-size: clamp(26px, 4.2vw, 42px); line-height: 1.15; max-width: 18ch; margin: 18px 0 12px; }
.hero .lede { font-size: clamp(15px, 1.6vw, 18px); max-width: 62ch; line-height: 1.6; }
.cta { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; }

.pillars {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px;
    margin: 0 0 40px;
}
.pillar {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: 12px;
    padding: 18px 20px;
}
.pillar h2 { font-size: 17px; margin: 0 0 10px; }
.pillar p { margin: 0 0 10px; font-size: 14px; line-height: 1.6; }
.pillar .fine { color: var(--muted); font-size: 13px; margin-bottom: 0; }

.verdicts { margin-bottom: 40px; }
.verdict-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 12px;
}
.verdict {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: 14px 16px;
    font-size: 14px;
}
.verdict b { display: block; margin: 6px 0 4px; }
.verdict span:not(.dot) { color: var(--muted); font-size: 13px; line-height: 1.55; }
.verdict .dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%;
                background: var(--tone, var(--muted)); box-shadow: 0 0 8px 1px var(--tone, transparent); }

/* Set apart from the grid above it: this is the one label a person applies, not one the engine
   reaches, and it should not read as a seventh machine verdict. */
.owner-verdict {
    margin-top: 12px;
    border-color: color-mix(in srgb, #ff4fd8 35%, var(--line));
    background: linear-gradient(180deg, color-mix(in srgb, #ff4fd8 6%, var(--panel)), var(--panel));
}

/* Reuse the chip tones so the same colour means the same thing everywhere on the site. */
.dot.t-uap { --tone: #ff4fd8; }
.dot.t-maneuver { --tone: #46e6b0; }
.dot.t-unexplained { --tone: #ff9f4d; }
.dot.t-satellite { --tone: #4da3ff; }
.dot.t-aircraft { --tone: #b48cff; }
.dot.t-meteor { --tone: #ff6b6b; }
.dot.t-cloud { --tone: #8d99a4; }
.dot.t-inconclusive { --tone: #6f7a84; }
.dot.t-lightning { --tone: #d6dcff; }
.dot.t-star { --tone: #ffd85e; }

.tag.verified { color: #7bd88f; border-color: #7bd88f; }

.recent { margin-bottom: 40px; }
.more { margin-top: 16px; font-size: 14px; }

.faq { margin-bottom: 48px; max-width: 78ch; }
.faq details {
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--panel);
    padding: 12px 16px;
    margin-bottom: 8px;
}
.faq summary { cursor: pointer; font-weight: 600; font-size: 15px; }
.faq summary::marker { color: var(--accent); }
.faq details p { color: var(--muted); font-size: 14px; line-height: 1.65; margin: 10px 0 2px; }

/* ---- Long-form content pages (how-it-works, methodology) ---- */
.prose { max-width: 76ch; }
.prose h2 { font-size: 20px; margin: 34px 0 10px; }
.prose h3 { font-size: 16px; margin: 22px 0 8px; }
.prose p, .prose li { font-size: 14.5px; line-height: 1.7; color: var(--text); }
.prose .muted { color: var(--muted); }
.prose ul, .prose ol { padding-left: 20px; }
.prose li { margin-bottom: 6px; }
.prose blockquote {
    border-left: 3px solid var(--accent);
    margin: 16px 0; padding: 4px 0 4px 16px;
    color: var(--muted); font-size: 14px;
}
.steps { counter-reset: step; list-style: none; padding: 0; }
.steps > li {
    counter-increment: step;
    position: relative;
    padding: 14px 16px 14px 54px;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: 10px;
    margin-bottom: 10px;
}
.steps > li::before {
    content: counter(step);
    position: absolute; left: 16px; top: 14px;
    width: 26px; height: 26px; border-radius: 50%;
    display: grid; place-items: center;
    background: #1f2a4d; color: #3ddc84;
    font-weight: 700; font-size: 13px;
}
.steps b { display: block; margin-bottom: 4px; }

/* Source stamp beside a capture time. Quiet on the page, but it survives the screenshot — which is the
   only place it matters, since that is how these images travel. */
.src {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 999px;
    border: 1px solid var(--line);
    color: var(--muted);
    font-size: 10.5px;
    letter-spacing: .02em;
    vertical-align: middle;
    white-space: nowrap;
}

.popupsrc {
    margin-top: 8px;
    padding-top: 6px;
    border-top: 1px solid var(--line);
    color: var(--muted);
    font-size: 10.5px;
    letter-spacing: .03em;
}

/* ---- Get-the-app banner (phones only; see AppDownloads.Detect) ---------------------------------- */
.getapp {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 0 auto 14px;
    padding: 10px 14px;
    max-width: 1180px;
    border: 1px solid var(--line, #2a3346);
    border-radius: 12px;
    background: linear-gradient(180deg, rgba(90, 170, 255, .10), rgba(90, 170, 255, .04));
    color: inherit;
    text-decoration: none;
}
.getapp:hover { border-color: var(--accent, #5aaaff); }
.getapp-icon { font-size: 22px; line-height: 1; width: 22px; text-align: center; }
.getapp-text { display: flex; flex-direction: column; line-height: 1.25; }
.getapp-text small { opacity: .72; font-size: 12px; }
.getapp-go { margin-left: auto; opacity: .6; }

/* --- Community star ratings -------------------------------------------------------------------
   Stars are the ordering the map leads with, so they have to read at card size as well as on the
   detail page. One glyph set, two sizes. */
.stars { color: var(--star); letter-spacing: 2px; font-size: 15px; }
.stars.big { font-size: 26px; letter-spacing: 3px; }

.ratebox .ratehead { display: flex; align-items: baseline; gap: 12px; }
.ratebox .ratenum { font-size: 22px; font-weight: 700; }
.ratebox .ratenum .of { font-size: 13px; font-weight: 400; color: var(--muted); margin-left: 4px; }

.cardrate { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.cardrate .ratecount { font-size: 12px; color: var(--muted); }

/* --- Thumbs tally --------------------------------------------------------------------------------
   Up and down are the two things a reader can check for themselves, so they are the numbers on the
   card. The weighted score that actually drives the ordering is deliberately not shown as a figure:
   it is not the same number, and printing both invites the reader to expect them to agree. */
.tally { display: flex; align-items: baseline; gap: 20px; font-size: 26px; font-weight: 700; }
.tally .up { color: var(--ok, #3ddc84); }
.tally .down { color: var(--muted); }

.cardrate .up { color: var(--ok, #3ddc84); font-size: 13px; font-weight: 600; }
.cardrate .down { color: var(--muted); font-size: 13px; font-weight: 600; }

.thumbs { display: flex; align-items: center; gap: 10px; margin-top: 12px; flex-wrap: wrap; }
.thumb {
  background: none; border: 1px solid var(--line); border-radius: 8px;
  color: var(--text); font-size: 14px; padding: 8px 14px; cursor: pointer;
  transition: border-color .12s ease, background .12s ease, color .12s ease;
}
.thumb:hover:not(:disabled) { border-color: var(--accent); }
.thumb:disabled { cursor: default; opacity: .6; }
.thumb:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The chosen side is filled, so "what did I already say" is answerable at a glance rather than by
   remembering. Both buttons stay live — changing your mind is one click, not a withdraw-then-vote. */
.thumb.up.on { background: var(--ok, #3ddc84); border-color: var(--ok, #3ddc84); color: var(--bg); }
.thumb.down.on { background: var(--muted); border-color: var(--muted); color: var(--bg); }

/* The picker. Buttons rather than a radio group because the choice is revisable and re-pressing the
   same value should be a no-op, not a toggle into an unrated state — "clear" is its own control. */
.starpick { display: flex; align-items: center; gap: 4px; margin-top: 10px; flex-wrap: wrap; }
.starbtn {
  background: none; border: 1px solid transparent; border-radius: 6px;
  color: var(--line); font-size: 24px; line-height: 1; padding: 2px 4px; cursor: pointer;
  transition: color .12s ease, transform .08s ease;
}
.starbtn:hover:not(:disabled) { color: var(--star); transform: translateY(-1px); }
.starbtn.on { color: var(--star); }
.starbtn:disabled { cursor: default; opacity: .6; }
.starbtn:focus-visible { outline: 2px solid var(--star); outline-offset: 2px; }

/* Set apart from the stars: 0 is a judgement ("nothing unusual"), not the low end of a slider. */
.starbtn.zero {
  font-size: 14px; font-weight: 700; border-color: var(--line);
  color: var(--muted); margin-left: 10px; padding: 5px 9px;
}
.starbtn.zero.on { color: var(--bg); background: var(--muted); border-color: var(--muted); }

.linkbtn.clearrate { font-size: 12px; margin-left: 10px; color: var(--muted); }

/* Sort chips share the chip shell but are an ordering, not a filter — the separator says so. */
.sortsep { width: 1px; align-self: stretch; background: var(--line); margin: 0 6px; }
.chip.sort { font-weight: 600; }
.chip:disabled { opacity: .45; cursor: default; }

.sub.note { max-width: 62ch; margin-top: 4px; }

/* --- My cameras (ADR 0050) ----------------------------------------------------------------------- */
.camnote { max-width: 640px; }
.tile .v.small { font-size: 18px; }
.tile .v.warn { color: var(--warn, #ffc34d); }
.tile .v.bad { color: var(--bad, #ff6b6b); }

/* The camera's night summary (from Status.lastNight). Numbers, the label mix, the captures worth a look. */
.panel.nightcard strong { font-size: 16px; }
.panel.nightcard .nighttiles { margin: 10px 0 8px; }
.panel.nightcard .nightlabels { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.panel.nightcard .nightlabels .tag b { margin-left: 4px; }
.panel.nightcard .cards.nighttop { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
.sel { background: var(--panel); color: var(--text); border: 1px solid var(--line); border-radius: 8px;
    padding: 7px 10px; font-size: 13px; }
.chk.inline { display: inline-flex; align-items: center; gap: 6px; margin-right: 14px; color: var(--text); font-size: 14px; }
.panel summary { cursor: pointer; }
.panel .muted { color: var(--muted); font-size: 13px; }
.livefr { display: block; width: 100%; max-width: 760px; margin-top: 12px; border-radius: 8px;
    border: 1px solid var(--line); background: #0a0f1e; image-rendering: auto; }
.liverow { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-top: 10px; }
.liverow .muted { color: var(--muted); font-size: 13px; }

/* A card that opens a player is a button, not a link: reset the button chrome so it matches the rest. */
.card.evcard { text-align: left; font: inherit; cursor: pointer; width: 100%; }
.card.evcard .label { font-size: 15px; }

.modalback { position: fixed; inset: 0; z-index: 950; background: rgba(3, 6, 14, .82);
    display: flex; align-items: flex-start; justify-content: center; padding: 24px 16px; overflow-y: auto; }
.modal { background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 16px 18px;
    width: 100%; max-width: 980px; }
.modalhead { display: flex; justify-content: space-between; gap: 12px; align-items: flex-start; margin-bottom: 12px; }
.modalhead .label { font-size: 18px; font-weight: 600; }
.modalhead .when { color: var(--muted); font-size: 13px; }
.navbtns { display: flex; gap: 6px; flex: none; }
.navbtns .btn { padding: 6px 12px; }
.btn:disabled { opacity: .45; cursor: default; }
.player { display: block; width: 100%; max-height: 70vh; background: #000; border-radius: 8px; object-fit: contain; }
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 14px;
    padding-top: 12px; border-top: 1px solid var(--line); }
.actions .confirm { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 8px; color: var(--warn); font-size: 13px; }
.okmsg { color: var(--ok); font-size: 14px; }
table.trash { border-collapse: collapse; width: 100%; max-width: 760px; font-size: 14px; }
table.trash td { padding: 6px 10px 6px 0; border-top: 1px solid var(--line); }
.errmsg { color: var(--warn); font-size: 14px; }

.setgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px 16px; margin: 12px 0; }
.setgrid label { display: flex; flex-direction: column; gap: 5px; color: var(--muted); font-size: 13px; }
.setgrid input { background: #0d1426; color: var(--text); border: 1px solid var(--line); border-radius: 8px;
    padding: 7px 10px; font-size: 14px; }
.setchecks, .setkeep { margin: 10px 0; }
.setkeep .k { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; margin-bottom: 6px; }

@media (max-width: 700px) {
    .modalback { padding: 0; }
    .modal { border-radius: 0; min-height: 100%; padding: 12px; }
    .modalhead { flex-direction: column; }
}

/* --- Analytics consent ---------------------------------------------------------------------------
   One row, two buttons, no overlay: it must not get between a visitor and the map. Declining is as
   prominent as allowing, because the site is identical either way. */
.consent {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
    margin: 0 auto 14px;
    padding: 12px 16px;
    max-width: 1180px;
    border: 1px solid var(--line, #2a3346);
    border-radius: 12px;
    background: var(--panel, #141a2e);
}
.consent-text { display: flex; flex-direction: column; gap: 2px; flex: 1 1 320px; font-size: 14px; line-height: 1.35; }
.consent-text span { color: var(--muted); }
.consent-text a { color: var(--accent); }
.consent-actions { display: flex; gap: 8px; margin-left: auto; }
@media (max-width: 640px) {
    .consent { padding: 12px; }
    .consent-actions { width: 100%; margin-left: 0; }
    .consent-actions .btn { flex: 1; }
}

/* --- Where a camera is needed (/coverage, ADR 0057) ------------------------------------------- */
.covwhy p { max-width: 74ch; margin: 0 0 14px; }
.legend { display: flex; flex-wrap: wrap; gap: 8px 18px; margin: 0 0 12px; color: var(--muted); font-size: 13px; }
.legend span { display: inline-flex; align-items: center; gap: 8px; max-width: 100%; }
.legend .sw { flex: none; width: 14px; height: 14px; border-radius: 3px; border: 1.5px solid; }
.legend .sw.wanted       { border-color: #ff9f4d; background: color-mix(in srgb, #ff9f4d 34%, transparent); }
.legend .sw.watched      { border-color: #4da3ff; background: color-mix(in srgb, #4da3ff 26%, transparent); }
.legend .sw.corroborated { border-color: #3ddc84; background: color-mix(in srgb, #3ddc84 34%, transparent); }
.covcta { margin-top: 18px; }
.covcta .ctas { display: flex; flex-wrap: wrap; gap: 10px; margin: 12px 0 14px; }
.covcta code { user-select: all; }
@media (max-width: 640px) {
    .legend { flex-direction: column; gap: 6px; }
    .covcta .ctas .btn { flex: 1 1 100%; text-align: center; }
}
