/* ==========================================================================
   base.css — shared by every page
   Fonts, design tokens, the CRT background treatment, and the footer.
   Page-specific styling lives in home.css / portfolio.css.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Fonts
   Only the faces the site actually renders are declared. Monocraft ships in
   nine weights; nothing here sets a weight other than normal or bold, so
   declaring the rest just adds parse work for faces no browser would ever
   request. The unused .otf files are still in assets/fonts/ if a future
   design wants them — add the matching @font-face block and they'll load.
   -------------------------------------------------------------------------- */

@font-face {
    font-family: 'Monocraft';
    src: url('../fonts/Monocraft.otf') format('opentype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Monocraft';
    src: url('../fonts/Monocraft-Italic.otf') format('opentype');
    font-weight: 400;
    font-style: italic;
    font-display: swap;
}

@font-face {
    font-family: 'Monocraft';
    src: url('../fonts/Monocraft-Bold.otf') format('opentype');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Monocraft';
    src: url('../fonts/Monocraft-Bold-Italic.otf') format('opentype');
    font-weight: 700;
    font-style: italic;
    font-display: swap;
}

@font-face {
    font-family: 'VCR OSD Mono';
    src: url('../fonts/VCR_OSD_MONO.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

/* --------------------------------------------------------------------------
   Tokens
   --accent-rgb is the single source of truth for the accent colour. Every
   glow, border and shadow below derives from it, so a page can re-theme
   itself by overriding one line (see portfolio.css).
   -------------------------------------------------------------------------- */

:root {
    --bg: #030303;
    --fg: #e8e8e8;
    --fg-dim: #8a8a8a;
    --fg-faint: #5f5f5f;

    --surface: #0a0a0a;
    --surface-hover: #120a0d;
    --line: #1c1c1c;
    --line-soft: #2a2a2a;

    --accent-rgb: 220, 20, 60;
    --accent: rgb(var(--accent-rgb));
    --accent-glow: rgba(var(--accent-rgb), 0.45);

    /* Atmosphere palette — deliberately separate from --accent-rgb.
       --accent-rgb has to stay bright enough for borders, focus rings and
       hover states to read against near-black. The background glow wants the
       opposite: deep, desaturated, closer to ink than to neon. Splitting them
       lets the ambient light go as deep as it likes without dragging the UI
       chrome down with it. Home is crimson; portfolio.css overrides all three
       to its blue. */
    --glow-1: 176, 14, 48;    /* deep crimson — primary mass */
    --glow-2: 104, 8, 36;     /* wine — the shadow side of the glow */
    --glow-3: 214, 34, 78;    /* lifted crimson — the highlight that drifts */

    --font-body: 'Monocraft', 'Courier New', Courier, monospace;
    --font-display: 'VCR OSD Mono', 'Courier New', Courier, monospace;

    --ease: cubic-bezier(0.16, 1, 0.3, 1);
    --speed: 0.4s;
    --speed-fast: 0.25s;
}

/* --------------------------------------------------------------------------
   Reset
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
    box-sizing: border-box;
    -webkit-tap-highlight-color: transparent;
}

html,
body {
    min-height: 100%;
    margin: 0;
    padding: 0;
    color: var(--fg);
    font-family: var(--font-body);
    overscroll-behavior-x: none;
    touch-action: pan-y pinch-zoom;
}

html {
    overflow-x: hidden;
    min-height: 100%;
    color-scheme: dark;

    /* ONE background, and it lives here.
       ------------------------------------------------------------------
       The root element's background is the only one in the document that
       propagates to the canvas, which is the surface painted across the
       whole viewport — including the strip iOS reveals during rubber-band
       overscroll, and the area exposed when the URL bar collapses. Nothing
       can sit behind it, because there is no "behind": the canvas is the
       bottom of the world. That is why the glow is here and not on a fixed
       pseudo-element. A fixed layer is viewport-sized and stops at the
       viewport edge, so anything past it falls through to flat black, and
       the black reads as a second, lower background — which is exactly what
       it is.

       Two things make this work where the earlier root version couldn't:

       1. The glow layers TILE (background-repeat below). A repeating root
          background paints across the entire canvas, so there is no edge to
          run out of — it covers a 200px page and a 6000px one identically,
          and keeps going past the document into the overscroll void. The
          three layers use different tile dimensions that share no common
          factor, so their blobs land in a different relative arrangement
          every repeat and the grid never becomes legible.

       2. There is no vignette and no base gradient. The old vignette faded
          to rgba(0,0,0,0.85) at the edges, which ringed the glow in solid
          black and made it look like a lit patch floating on a separate
          dark surface. Removing it lets the colour run to every edge.

       Only background-position animates. Each layer travels a whole number
       of its own tiles per cycle, so the loop point is invisible and the
       drift is continuous rather than a back-and-forth. */
    background-color: #000;
    background-repeat: repeat;
    /* Tiles are deliberately smaller than the viewport on at least one axis.
       At 118vmax x 92vmax a single tile more than filled a 1440x900 screen,
       so each layer contributed exactly one blob and the corners stayed flat
       black no matter how bright the blob was. Dropping to ~60-90vmax puts two
       or more copies of every layer on screen at once, and since neighbouring
       copies overlap by design the field reads as continuous light rather than
       isolated spots. */
    background-size:
        auto,
        82vmax 68vmax,
        68vmax 76vmax,
        94vmax 58vmax;
    background-image:
        /* Scanlines sit on top so the CRT texture reads across the glow
           rather than under it. repeating-linear-gradient tiles on its own,
           so this covers the canvas too. */
        repeating-linear-gradient(
            0deg,
            rgba(255, 255, 255, 0.055) 0px,
            rgba(255, 255, 255, 0.055) 1px,
            rgba(0, 0, 0, 0) 1px,
            rgba(0, 0, 0, 0) 3px
        ),
        /* Each blob fades out at 74% of an ellipse that is 72% of its own
           tile, so it goes transparent at ~53% of the tile — just past the
           halfway point where the next copy begins. That overlap is what
           keeps the tiling seamless. It has to hold on BOTH axes: at 66%
           vertical the shortest tile (78vmax) went transparent at 38vmax
           against a 39vmax half-height, opening a thin dark band across every
           tile row. Below about 68% on either axis, seams appear. */
        radial-gradient(
            ellipse 72% 72% at 50% 50%,
            rgba(var(--glow-1), 0.17) 0%,
            rgba(var(--glow-1), 0.08) 46%,
            rgba(0, 0, 0, 0) 74%
        ),
        radial-gradient(
            ellipse 72% 72% at 50% 50%,
            rgba(var(--glow-2), 0.16) 0%,
            rgba(var(--glow-2), 0.075) 47%,
            rgba(0, 0, 0, 0) 74%
        ),
        radial-gradient(
            ellipse 72% 72% at 50% 50%,
            rgba(var(--glow-3), 0.13) 0%,
            rgba(var(--glow-3), 0.06) 48%,
            rgba(0, 0, 0, 0) 74%
        );
    background-blend-mode: overlay, screen, screen, screen;
    animation: auroraDrift 75s linear infinite;
}

body {
    min-height: 100dvh;
    position: relative;
    isolation: isolate;
    background: transparent;
}

/* Safari-specific safeguard: if iOS exposes the root during overscroll,
   it is still the same black animated canvas rather than a white UA canvas. */
@supports (-webkit-touch-callout: none) {
    html {
        background-color: #000;
    }
}

/* Buttons and form controls don't inherit font-family on their own */
button,
input,
select,
textarea {
    font-family: inherit;
}

img {
    -webkit-user-drag: none;
    user-select: none;
    -webkit-touch-callout: none;
}

/* <picture> is a source selector, not a box. Taking it out of the layout
   means the <img> inherits its real parent as a containing block, so
   percentage sizes and flex/grid placement behave exactly as they would
   with a bare <img>. */
picture {
    display: contents;
}

/* Normally <source> generates no box because <picture>/<video> ignore their
   non-media children. display:contents above bypasses that, and Chromium
   then hands the <source> to the parent flex container as a real (empty)
   item — which quietly adds one extra `gap` to the layout. Saying it
   outright costs nothing and removes the surprise. */
source {
    display: none;
}

/* Screen-reader-only text: present in the accessibility tree, invisible on screen */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* --------------------------------------------------------------------------
   Background treatment — the drift

   Three tiled glow layers, each travelling a whole number of its own tiles
   per cycle. Because the travel is an exact multiple of the tile size, the
   position at 100% paints identically to the position at 0% — so the loop is
   invisible even though every layer has been moving in one direction the
   whole time. That is what allows `linear` timing and a continuous drift
   instead of the back-and-forth ease that a bounded translation forces.

   The layers decorrelate by distance rather than by duration. They must share
   one animation, since background-position is a single property, so instead
   they cover different numbers of tiles in the same 75s: the crimson mass
   crosses one tile diagonally, the wine two horizontally, the highlight three
   vertically. Their apparent speeds and directions all differ, and the
   composite takes many minutes to repeat.

   Sizes are in vmax, so the tiles scale with the viewport. A phone and a
   desktop see the same composition at the same relative scale, and no
   phone-specific override is needed.
   -------------------------------------------------------------------------- */

@keyframes auroraDrift {
    from {
        background-position:
            0 0,
            0 0,
            0 0,
            0 0;
    }
    to {
        background-position:
            0 0,
            82vmax -68vmax,
            -136vmax 76vmax,
            94vmax 174vmax;
    }
}

/* --------------------------------------------------------------------------
   Footer, shared by both pages
   -------------------------------------------------------------------------- */

.copyright {
    color: var(--fg-faint);
    font-size: 11px;
    letter-spacing: 1px;
    text-align: center;
    text-transform: lowercase;
    margin: 4px 0 14px;
}

.footer-image {
    display: block;
    width: min(220px, 60vw);
    height: auto;
    margin: 6px auto 0;
    filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.6));
}

/* --------------------------------------------------------------------------
   Accessibility
   -------------------------------------------------------------------------- */

/* On-brand focus ring for keyboard navigation. :focus-visible means it never
   appears on mouse clicks, only on keyboard/AT interaction. */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 4px;
}

@media (prefers-reduced-motion: no-preference) {
    html {
        scroll-behavior: smooth;
    }
}

/* Honour the OS "reduce motion" setting: strip transitions and animations
   rather than merely shortening them. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* --------------------------------------------------------------------------
   Small screens
   -------------------------------------------------------------------------- */

/* No phone-specific rules for the background any more. The tiles are sized in
   vmax, so they scale with the viewport on their own, and painting gradients
   into the canvas costs the same whatever the screen is — unlike the blurred
   fixed layers this replaced, which each allocated a buffer proportional to
   overscan x device pixel ratio and were the single heaviest thing on the
   page for a phone. */

@media (max-width: 480px) {
    .footer-image {
        filter: drop-shadow(0 5px 12px rgba(0, 0, 0, 0.4));
    }
}
