/* =========================================================
   Subsignal — mobile-first: base styles are for phones,
   min-width queries add the heavier desktop effects.
   ========================================================= */

@font-face {
    font-family: 'UAV OSD Sans Mono';
    src: url('UAV-OSD-Sans-Mono.woff2') format('woff2'),
         url('UAV-OSD-Sans-Mono.ttf') format('truetype');
    font-display: swap;
}

:root {
    /* colour */
    --lime: #b2ed00;
    --lime-90: color-mix(in srgb, var(--lime) 90%, transparent);
    --lime-60: color-mix(in srgb, var(--lime) 60%, transparent);
    --lime-35: color-mix(in srgb, var(--lime) 35%, transparent);
    --lime-18: color-mix(in srgb, var(--lime) 18%, transparent);
    --lime-5: color-mix(in srgb, var(--lime) 5%, transparent);
    --ink: #050604;
    --panel: rgb(0 0 0 / 0.75);                 /* dark backing behind text on the busy artwork */
    --field: rgb(0 0 0 / 0.85);
    --text-soft: rgb(255 255 255 / 0.8);
    --placeholder: rgb(255 255 255 / 0.55);
    --error: #ff5a5a;
    --glitch-cyan: #00e5ff;
    --glitch-magenta: #ff2bd6;

    /* type and spacing */
    --font: 'UAV OSD Sans Mono', ui-monospace, monospace;
    --text: max(1rem, 1.5vw);                   /* base text size; grows with screen width */
    --hud-text: max(0.7rem, 0.8vw);             /* corner readouts */
    --bar-height: calc(var(--text) * 4);        /* ticker + footer; explicit so it means the same inside the smaller overlay text */
    --dither: clamp(2.5rem, 5vw, 4.5rem);       /* pixel edge below/above the bars */
    --tap: 2.75rem;                             /* 44px minimum tap target */

    --bg-art: url('/static/bg-1500.png');
    --mx: 0;                                    /* mouse position, -0.5 … 0.5 (set by home.js) */
    --my: 0;

    /* Motion switch: the pause control, or a reduced-motion preference, flips it. */
    --motion: running;
}

.motion-paused {
    --motion: paused;
}

@media (prefers-reduced-motion: reduce) {
    :root:not(.motion-on) {
        --motion: paused;
    }
}

*,
*::before,
*::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    /* !important because every `animation:` shorthand below would otherwise reset this to running */
    animation-play-state: var(--motion) !important;
}

/* Fill the screen using the page's own height rather than vh/svh units,
   which some phones miscalculate in "desktop site" mode. */
html {
    height: 100%;
    overflow-x: clip;
}

body {
    position: relative;                         /* the overlay is positioned against the page */
    display: flex;
    flex-direction: column;
    align-items: center;
    min-height: 100%;
    font-family: var(--font);
    font-size: var(--text);
    color: #fff;
    background-color: var(--ink);
}

/* Background art: slightly oversized so it can drift with the mouse.
   Layers: lime bloom, dark centre tint, edge vignette, artwork. */
body::before {
    content: '';
    position: fixed;
    inset: -4%;
    z-index: -1;
    background:
        radial-gradient(ellipse 45% 32% at 50% 48%, var(--lime-18), transparent 70%),
        radial-gradient(ellipse 70% 55% at 50% 50%, rgb(0 0 0 / 0.55), rgb(0 0 0 / 0.15) 75%),
        radial-gradient(ellipse at center, transparent 55%, rgb(0 0 0 / 0.7) 100%),
        var(--bg-art) center 70% / cover no-repeat,
        var(--ink);
    transform: translate(calc(var(--mx) * -28px), calc(var(--my) * -18px));
    transition: transform 0.5s ease-out;
}

/* CRT scanlines */
body::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 50;
    pointer-events: none;
    background: repeating-linear-gradient(transparent 0 2px, rgb(0 0 0 / 0.22) 2px 3px);
}

/* Author display rules (flex, grid) would otherwise beat the hidden attribute. */
[hidden] {
    display: none !important;
}

::selection {
    background: var(--lime);
    color: var(--ink);
}

/* Readable by screen readers, invisible on screen. The members link swaps
   its two labels with the same technique. */
.visually-hidden,
.hud-link .hud-link-reveal,
.hud-link:is(:hover, :focus-visible) .hud-link-idle {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ================= ticker (top bar) ================= */
.ticker {
    position: relative;
    z-index: 10;
    flex-shrink: 0;
    width: 100%;
    height: var(--bar-height);
    color: var(--lime);
    background: #000;
    white-space: nowrap;
    overflow-x: clip;                           /* clip the scrolling text, but let the pixel edge hang below */

    /* pixel dissolve into the background */
    &::after {
        content: '';
        position: absolute;
        inset: calc(100% - 1px) 0 auto;
        height: var(--dither);
        background: url('/static/subsig-pixel-tile.png') 0 0 / auto 100% repeat-x;
        transform: scaleY(-1);
        pointer-events: none;
    }
}

/* Two identical copies side by side; sliding by -50% makes a seamless loop. */
.ticker-track {
    display: inline-flex;
    align-items: center;
    height: 100%;
    animation: ticker 22s linear infinite;

    & p {
        padding-right: 1.5em;
        text-shadow: 0 0 6px var(--lime), 0 0 18px var(--lime-60);
    }

    & span {
        margin: 0 0.75em;
        color: #fff;
        text-shadow: 0 0 8px #fff;
    }
}

/* ================= camera overlay =================
   Three layers share this geometry: the decorative readouts (.hud-decor),
   the pause control and the members link (.hud-controls). */
.hud {
    --edge: 0.9em;                              /* readout distance from the top/bottom of the frame */
    --gap: 3em;                                 /* readout distance from the sides, leaving room for the bracket */
    --bracket: 2.5em;

    position: absolute;
    inset: calc(var(--bar-height) + var(--dither) * 0.9) 0.6rem;
    z-index: 5;
    pointer-events: none;
    font-size: var(--hud-text);
    color: var(--lime);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    text-shadow: 0 0 6px var(--lime-60);

    /* each readout sits in a corner and draws that corner's bracket */
    & > * {
        position: absolute;
        display: flex;
        align-items: center;
        gap: 0.5em;
        padding: 0.25em 0.5em;
        background: var(--panel);

        &::before {
            content: '';
            position: absolute;
            width: var(--bracket);
            height: var(--bracket);
            border: 2px solid var(--lime);
            filter: drop-shadow(0 0 4px var(--lime));
            opacity: 0.85;
        }
    }

    & .hud-tl { top: var(--edge); left: var(--gap); }
    & .hud-tr { top: var(--edge); right: var(--gap); }
    & .hud-bl { bottom: var(--edge); left: var(--gap); }
    & .hud-br { bottom: var(--edge); right: var(--gap); }

    & .hud-tl::before { top: calc(-1 * var(--edge)); left: calc(-1 * var(--gap)); border-right: 0; border-bottom: 0; }
    & .hud-tr::before { top: calc(-1 * var(--edge)); right: calc(-1 * var(--gap)); border-left: 0; border-bottom: 0; }
    & .hud-bl::before { bottom: calc(-1 * var(--edge)); left: calc(-1 * var(--gap)); border-right: 0; border-top: 0; }
    & .hud-br::before { bottom: calc(-1 * var(--edge)); right: calc(-1 * var(--gap)); border-left: 0; border-top: 0; }
}

/* Narrow phones: two readouts per row don't fit, so the decorative ones go. */
.hud-decor > :is(.hud-tr, .hud-bl) {
    display: none;
}

.hud-controls {
    z-index: 11;                                /* above <main> so the controls can be used */
}

.hud-pulse {
    position: relative;
    width: 0.7em;
    height: 0.7em;
    border-radius: 50%;
    background: var(--lime);
    box-shadow: 0 0 8px var(--lime);

    /* expanding ring, like a radar ping */
    &::after {
        content: '';
        position: absolute;
        inset: 0;
        border-radius: 50%;
        border: 1px solid var(--lime);
        animation: ping 1.6s ease-out infinite;
    }
}

/* "scanning" with dots that count up: scanning. .. ...
   Separate dots fading in (opacity) keep the work off the main thread. */
.hud-dots span {
    opacity: 0;

    &:nth-child(1) { animation: dot-1 1.6s linear infinite; }
    &:nth-child(2) { animation: dot-2 1.6s linear infinite; }
    &:nth-child(3) { animation: dot-3 1.6s linear infinite; }
}

.hud-blink {
    animation: blink 1.6s steps(1) infinite;
}

.hud-bars {
    display: inline-flex;
    align-items: flex-end;
    gap: 2px;
    height: 1em;

    & span {
        width: 4px;
        background: var(--lime);
        box-shadow: 0 0 4px var(--lime);
        animation: fade 1.8s ease-in-out infinite;
    }

    & span:nth-child(1) { height: 20%; }
    & span:nth-child(2) { height: 40%; animation-delay: 0.15s; }
    & span:nth-child(3) { height: 60%; animation-delay: 0.3s; }
    & span:nth-child(4) { height: 80%; animation-delay: 0.45s; }
    & span:nth-child(5) { height: 100%; animation-delay: 0.6s; }
}

/* after a successful signup or unlock the brackets "lock on" */
.is-locked .hud > *::before {
    animation: lock 0.5s ease-out 3;
}

/* ---------- interactive readouts: members link + pause control ---------- */
.hud-control {
    pointer-events: auto;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: inherit;
    text-decoration: none;
    border: 0;
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s;

    /* invisible padding so the small readout is still a 44px tap target */
    &::after {
        content: '';
        position: absolute;
        inset: calc(50% - var(--tap) / 2) -0.5rem;
    }

    &:hover,
    &:focus-visible {
        color: var(--ink);
        background: var(--lime);
        outline: none;
        text-shadow: none;
        box-shadow: 0 0 18px var(--lime);

        & .hud-bars span {
            background: var(--ink);
            box-shadow: none;
        }
    }
}

/* The pause control takes the top-right corner once motion.js has shown it;
   without JavaScript the decorative "sig" readout stays there instead. */
body:has(.motion-toggle:not([hidden])) .hud-decor > .hud-tr {
    display: none;
}

/* Under 360px the bars would push the control into the top-left readout. */
.motion-toggle .hud-bars {
    display: none;
}

/* ================= main ================= */
main {
    position: relative;
    z-index: 10;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    flex-grow: 1;
    width: 90%;
    max-width: 90rem;
    /* keep content clear of the corner readouts and pixel edges; on short
       screens the page grows and scrolls instead of overlapping */
    padding-block: calc(var(--dither) * 0.9 + var(--hud-text) * 3);
}

/* ---------- logo: glow, float, RGB-split glitch bursts (desktop) ---------- */
.logo {
    position: relative;
    width: 100%;
    line-height: 0;
    transform:
        translate(calc(var(--mx) * 18px), calc(var(--my) * 12px))
        rotate(calc(var(--mx) * 1.5deg));
    transition: transform 0.3s ease-out;
    animation: boot 0.9s steps(1) both, float 6s ease-in-out infinite;

    & img {
        display: block;
        width: 100%;
        height: auto;
        filter: drop-shadow(0 0 10px var(--lime-60));
    }

    /* cyan and magenta copies that flash in for each burst (started by
       motion.js). z-index keeps both above the logo image, whose filter
       would otherwise draw it over ::before. */
    &::before,
    &::after {
        content: '';
        position: absolute;
        inset: 0;
        z-index: 1;
        display: none;
        background: center / contain no-repeat;
        background-image: image-set(url('/static/logo-1200.png') 1x, url('/static/logo-2400.png') 2x);
        mix-blend-mode: screen;
        opacity: 0;
        pointer-events: none;
    }

    &::before { filter: hue-rotate(110deg) saturate(3); }
    &::after { filter: hue-rotate(230deg) saturate(3); }

    &.is-glitching::before { animation: glitch-a 0.68s steps(1); }
    &.is-glitching::after { animation: glitch-b 0.68s steps(1); }
}

/* ---------- forms (signup + passphrase) ---------- */
.signal-form {
    width: min(100%, 32em);
    margin-top: 1.5em;
    font-size: 1rem;                            /* 16px or more stops iPhones and iPads zooming in on a tapped field */
}

.field-label {
    display: inline-block;
    margin-bottom: 0.35em;
    padding: 0.15em 0.5em;
    font-size: 0.8em;
    color: var(--lime);
    letter-spacing: 0.08em;
    background: var(--panel);
    text-shadow: 0 0 6px var(--lime-60);
}

.field-row {
    display: flex;
    flex-direction: column;
    gap: 0.75em;

    & input,
    & button {
        min-height: var(--tap);
        padding: 0.45em 0.6em;
        font: inherit;
        border-radius: 0.25em;
    }

    & input {
        flex: 1;
        min-width: 0;
        color: #fff;
        background: transparent;
        border: 0;
        outline: 0;
        caret-color: var(--lime);

        &::placeholder { color: var(--placeholder); }
    }

    & button {
        cursor: pointer;
        color: var(--ink);
        background: var(--lime);
        border: 2px solid var(--lime);
        box-shadow: 0 0 16px var(--lime-60), 4px 4px 0 #000;
        transition: transform 0.08s, box-shadow 0.15s, background-color 0.15s, color 0.15s;

        &:hover,
        &:focus-visible {
            color: var(--lime);
            background: #000;
            outline: none;
            box-shadow: 0 0 26px var(--lime-90), 4px 4px 0 var(--lime);
        }

        &:active {
            transform: translate(3px, 3px);
            box-shadow: 0 0 12px var(--lime-60), 1px 1px 0 var(--lime);
        }

        &:disabled {
            opacity: 0.7;
            cursor: progress;
        }
    }
}

.field {
    flex: 1;
    display: flex;
    align-items: center;
    background: var(--field);
    border: 2px solid var(--lime-35);
    border-radius: 0.25em;
    transition: border-color 0.2s, box-shadow 0.2s;

    &:focus-within {
        border-color: var(--lime);
        box-shadow: 0 0 0 1px var(--lime), 0 0 24px var(--lime-60);
    }
}

.field-prompt {
    padding-left: 0.5em;
    color: var(--lime);
    text-shadow: 0 0 6px var(--lime);
}

/* the prompt blinks like a cursor only while you're typing */
.field:focus-within .field-prompt {
    animation: blink 1.1s steps(1) infinite;
}

/* off-screen for people, still in the form for bots that fill every field */
.bot-trap {
    position: absolute;
    left: -10000px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.status-line,
.signal-note,
.terminal-message {
    width: fit-content;
    max-width: 100%;
    text-align: center;
    text-wrap: balance;
    background: var(--panel);
    text-shadow: 0 2px 0 #000;
}

.status-line {
    min-height: 1.4em;
    margin-top: 1em;
    padding: 0.2em 0.6em;

    &[data-state="ok"] {
        color: var(--lime);
        text-shadow: 0 0 10px var(--lime);
    }

    &[data-state="error"] {
        color: var(--error);
        text-shadow: 0 0 10px color-mix(in srgb, var(--error) 60%, transparent);
    }

    /* blinking cursor after a typed message */
    &:is([data-state="ok"], [data-state="error"]) .status-line-typed::after {
        content: '_';
        animation: blink 1s steps(1) infinite;
    }
}

.signal-note {
    margin-top: 0.6em;
    padding: 0.15em 0.6em;
    font-size: 0.7em;
    color: var(--text-soft);
}

/* ---------- members page and fallback message pages ---------- */
.terminal-title {
    font-size: clamp(2rem, 7vw, 5.5rem);
    font-weight: 400;
    line-height: 1.1;
    text-align: center;
    color: var(--lime);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-shadow: 0 0 6px var(--lime), 0 0 24px var(--lime-60), 0 4px 0 #000;
    animation: boot 0.9s steps(1) both;

    &.is-glitching {
        animation: title-glitch 0.68s steps(1);
    }
}

.terminal-message {
    max-width: 30em;
    margin-top: 1em;
    padding: 0.2em 0.6em;
    font-size: 110%;
    line-height: 1.5;
}

/* ================= footer ================= */
footer {
    position: relative;
    z-index: 10;
    flex-shrink: 0;
    width: 100%;
    min-height: var(--bar-height);
    background: #000;
    font-size: min(1em, 4vw);                   /* narrow phones: the three links still fit one row */

    &::before {
        content: '';
        position: absolute;
        inset: auto 0 calc(100% - 1px);
        height: var(--dither);
        background: url('/static/subsig-pixel-tile.png') 0 0 / auto 100% repeat-x;
        pointer-events: none;
    }

    & ul {
        display: flex;
        flex-wrap: wrap;
        justify-content: space-around;
        align-items: center;
        min-height: var(--bar-height);
        max-width: 120em;
        margin: 0 auto;
        padding: 0 1em;
        list-style: none;
    }

    & li:only-child {
        margin-inline: auto;
    }

    & a {
        position: relative;
        display: inline-flex;
        align-items: center;
        min-height: var(--tap);
        padding: 0 0.4em;
        color: var(--lime);
        text-decoration: none;
        text-shadow: 0 0 6px var(--lime-60);
        transition: color 0.15s, background-color 0.15s;

        /* hover brackets sit outside the link so they take up no room */
        &::before,
        &::after {
            position: absolute;
            opacity: 0;
            transition: opacity 0.15s;
        }

        &::before { content: '['; right: calc(100% + 0.2em); }
        &::after { content: ']'; left: calc(100% + 0.2em); }

        &:hover,
        &:focus-visible {
            color: var(--ink);
            background: var(--lime);
            outline: none;
            text-shadow: none;
            box-shadow: 0 0 18px var(--lime);

            &::before,
            &::after {
                color: var(--lime);
                text-shadow: 0 0 6px var(--lime-60);
                opacity: 1;
            }
        }
    }
}

/* ================= wider screens ================= */
@media (min-width: 360px) {
    .motion-toggle .hud-bars {
        display: inline-flex;
    }
}

@media (min-width: 480px) {
    .hud-decor > :is(.hud-tr, .hud-bl) {
        display: flex;
    }
}

@media (min-width: 641px) {
    main {
        width: 80%;
    }

    .hud {
        --gap: 3.5em;
        inset-inline: 1.25rem;
    }

    .logo {
        & img {
            filter:
                drop-shadow(0 0 2px var(--lime-90))
                drop-shadow(0 0 22px var(--lime-60))
                drop-shadow(0 10px 24px rgb(0 0 0 / 0.8));
        }

        &::before,
        &::after { display: block; }
    }

    .signal-form {
        font-size: max(1rem, 0.85em);
    }

    .field-row {
        flex-direction: row;
    }

    /* slow rolling refresh bar; animated with transform so it doesn't repaint the screen */
    html::after {
        content: '';
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        z-index: 50;
        height: 18vh;
        pointer-events: none;
        background: linear-gradient(transparent, var(--lime-5), transparent);
        transform: translateY(-20vh);
        animation: roll 7s linear infinite;
    }
}

@media (min-width: 901px) {
    :root {
        --bg-art: url('/static/bg-2250.png');
    }
}

/* Portrait tablets and phones in "desktop site" mode: size from the width
   so the logo and form fill the screen instead of floating small. */
@media (orientation: portrait) {
    :root {
        --text: max(1rem, 2.6vw);
        --hud-text: max(0.7rem, 1.5vw);
    }

    main {
        width: 90%;
    }
}

/* ================= animations ================= */
@keyframes ticker {
    to { transform: translateX(-50%); }
}

@keyframes roll {
    to { transform: translateY(110vh); }
}

@keyframes ping {
    from { transform: scale(1); opacity: 0.9; }
    to { transform: scale(2.8); opacity: 0; }
}

@keyframes dot-1 { 0%, 24.9% { opacity: 0; } 25%, 100% { opacity: 1; } }
@keyframes dot-2 { 0%, 49.9% { opacity: 0; } 50%, 100% { opacity: 1; } }
@keyframes dot-3 { 0%, 74.9% { opacity: 0; } 75%, 100% { opacity: 1; } }

@keyframes blink {
    50% { opacity: 0; }
}

@keyframes fade {
    50% { opacity: 0.25; }
}

@keyframes lock {
    50% { transform: scale(0.8); border-color: #fff; }
}

@keyframes float {
    50% { translate: 0 -1.2%; }
}

/* Starts fully visible, so the logo counts as painted straight away and a
   paused page never freezes it half-faded. */
@keyframes boot {
    0%, 55%, 100% { opacity: 1; }
    10% { opacity: 0.15; }
    20% { opacity: 1; }
    35% { opacity: 0.2; }
}

/* One glitch burst: four ~170ms flickers. motion.js starts one every 6s
   (every 3.5s while hovered). */
@keyframes title-glitch {
    0% { transform: translateX(-0.06em); text-shadow: -0.06em 0 var(--glitch-cyan), 0.06em 0 var(--glitch-magenta), 0 0 24px var(--lime-60); }
    25% { transform: translateX(0.05em); text-shadow: 0.07em 0 var(--glitch-cyan), -0.05em 0 var(--glitch-magenta), 0 0 24px var(--lime-60); }
    50% { transform: translateX(-0.03em); text-shadow: -0.04em 0 var(--glitch-cyan), 0.05em 0 var(--glitch-magenta), 0 0 24px var(--lime-60); }
    75% { transform: translateX(0.02em); text-shadow: 0.03em 0 var(--glitch-cyan), -0.03em 0 var(--glitch-magenta), 0 0 24px var(--lime-60); }
    100% { transform: none; }
}

@keyframes glitch-a {
    0% { opacity: 0.85; transform: translateX(-1.2%); clip-path: inset(12% 0 58% 0); }
    25% { opacity: 0.85; transform: translateX(1.5%); clip-path: inset(55% 0 20% 0); }
    50% { opacity: 0.85; transform: translateX(-0.6%); clip-path: inset(30% 0 40% 0); }
    75% { opacity: 0.7; transform: translateX(0.8%); clip-path: inset(70% 0 5% 0); }
    100% { opacity: 0; }
}

@keyframes glitch-b {
    0% { opacity: 0.8; transform: translateX(1.4%); clip-path: inset(40% 0 35% 0); }
    25% { opacity: 0.8; transform: translateX(-1.6%); clip-path: inset(5% 0 70% 0); }
    50% { opacity: 0.8; transform: translateX(0.9%); clip-path: inset(62% 0 12% 0); }
    75% { opacity: 0.6; transform: translateX(-0.5%); clip-path: inset(20% 0 55% 0); }
    100% { opacity: 0; }
}
