/* ==========================================================================
   site.css
   The one place the site's vocabulary is defined. Every page loads this
   first, then its own stylesheet for page-specific components.

   It exists because the CV and the apps section had grown two names for the
   same thing — --background and --bg were both #111, --text-secondary and
   --text-muted were both "the grey one". Nothing forced them to agree, so
   they drifted. One token set, one header, one footer; pages may add
   components but must not redefine the ground.

   Dark only. The front page is a black WebGL scene and cannot have a light
   mode, so a light theme on the CV alone only made the site change character
   between pages.
   ========================================================================== */

:root {
    /* ——— ground ————————————————————————————————————— */
    --bg: #111111;
    --surface: #1e1e24;
    --surface-2: #232126;
    --surface-3: #2a2830;

    /* ——— type ——————————————————————————————————————— */
    --text: #ffffff;
    --text-dim: rgba(255, 255, 255, 0.7);
    --text-muted: #999999;      /* was #999 on the CV and #888 in apps — settled here */

    --line: rgba(255, 255, 255, 0.1);
    --line-strong: rgba(255, 255, 255, 0.2);

    /* Neutral by default. App detail pages override --accent with their own
       product colour; nothing else should, or the CV inherits a hue that
       belongs to a piece of software. */
    --accent: #9aa7b0;

    --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

    /* ——— measure ————————————————————————————————————
       Two widths, one frame. --page is the outer frame: the header, footers
       and card grids all span it. --measure is for running prose and is
       deliberately much narrower — reading wants 65–75 characters a line, so
       the CV at the full 1100 would be harder to read, not better. */
    --page: 1100px;
    --measure: 780px;
    --pad: 3rem;
    --spacing: 2rem;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
    font-family: var(--font);
    background-color: var(--bg);
    color: var(--text);
    line-height: 1.6;
    min-height: 100vh;
    -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }

.container {
    max-width: var(--page);
    margin: 0 auto;
    padding: 0 var(--pad);
}

/* Prose column. Same outer padding as .container, so the page edges line up
   across every page — only the text column narrows. */
.container.measure { max-width: var(--measure); }

/* --------------------------------------------------------------------------
   Site header — one component, every page

   Structure is the apps section's (name left, links right, sticky, blurred).
   The type is the front page's (uppercase, letterspaced, no boxes), so the
   landing page's full-screen menu reads as the same navigation writ large.
   -------------------------------------------------------------------------- */

.sticky-nav {
    position: sticky;
    top: 0;
    z-index: 100;
    background-color: rgba(17, 17, 17, 0.92);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--line);
    /* Roomy on arrival, compact once you start reading — set by site.js. With
       JS off it simply stays roomy, which is the better of the two states. */
    padding: 2rem 0;
    transition: padding 0.28s ease;
}

.sticky-nav.compact { padding: 0.8rem 0; }

/* The contextual row stays put on scroll. An earlier version hid the in-page
   anchors once the header compacted, which had it exactly backwards: those
   anchors are most useful while you are moving through the page, and a deep
   link like #privacy arrives already scrolled, so they vanished before they
   had ever been seen. */

.nav-wrapper {
    display: flex;
    align-items: center;
    gap: 2rem;
    flex-wrap: wrap;
}

.nav-title {
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text);
    text-decoration: none;
    white-space: nowrap;
    transition: opacity 0.25s ease;
}

.nav-title:hover { opacity: 0.72; }

.nav-links {
    display: flex;
    gap: 1.75rem;
    flex-wrap: wrap;
    margin-left: auto;
}

/* Plain text link by default. This class is used in footers as well as the
   header, so the box treatment the apps section used to give it has gone —
   the header adds its own emphasis below. */
.styled-link {
    color: var(--text-muted);
    text-decoration: none;
    transition: color 0.25s ease;
    white-space: nowrap;
}

.styled-link:hover { color: var(--text); }

.nav-links .styled-link {
    position: relative;
    font-size: 0.72rem;
    font-weight: 500;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    padding: 0.4rem 0;
}

.nav-links .styled-link.active,
.nav-links .styled-link[aria-current="page"] { color: var(--text); }

/* App detail pages put an external "Open app ↗" in the nav. Losing the pill
   cost it its prominence, so the one link that leaves the site gets the
   emphasis instead of a box. */
.nav-links .styled-link[target="_blank"] { color: var(--text); }

.nav-links .styled-link.active::after,
.nav-links .styled-link[aria-current="page"]::after {
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 1px;
    background: var(--text);
}

/* Contextual row — app detail pages only.
   These are in-page anchors, not site navigation, and they used to REPLACE the
   site nav in the bar, which meant the header changed shape and contents
   depending on which page you were on. Demoting them keeps the primary bar
   identical everywhere while losing none of them. */
.nav-sub {
    display: flex;
    gap: 1.5rem;
    margin-top: 0.8rem;
    padding-top: 0.8rem;
    border-top: 1px solid var(--line);
    overflow-x: auto;              /* too many to wrap on a phone */
    scrollbar-width: none;
}
.nav-sub::-webkit-scrollbar { display: none; }

.nav-sub a {
    font-size: 0.8rem;
    color: var(--text-muted);
    text-decoration: none;
    white-space: nowrap;
    transition: color 0.25s ease;
}
.nav-sub a:hover { color: var(--text); }

/* Back out to the listing. Set apart from the in-page anchors beside it —
   it is the only one that leaves the page. */
.nav-back {
    color: var(--text-dim) !important;
    padding-right: 1.5rem;
    border-right: 1px solid var(--line);
}
.nav-back:hover { color: var(--text) !important; }
.nav-back-arrow {
    display: inline-block;
    transition: transform 0.2s ease;
}
.nav-back:hover .nav-back-arrow { transform: translateX(-3px); }

@media (prefers-reduced-motion: reduce) {
    .nav-back-arrow { transition: none; }
    .nav-back:hover .nav-back-arrow { transform: none; }
}
.nav-sub a[target="_blank"] { color: var(--text); }
.nav-sub a:focus-visible { outline: 1px solid var(--line-strong); outline-offset: 4px; }

.nav-links .styled-link:focus-visible,
.nav-title:focus-visible {
    outline: 1px solid var(--line-strong);
    outline-offset: 5px;
}

/* --------------------------------------------------------------------------
   Shared editorial furniture
   -------------------------------------------------------------------------- */

/* The small uppercase label above a heading. Already the apps section's
   device; now shared, because it is the cheapest way to make the CV and the
   apps pages read as one hand — and it echoes the front page's type. */
.eyebrow {
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.18em;
    margin-bottom: 1rem;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.lede {
    font-size: 1.25rem;
    font-weight: 300;
    color: var(--text-muted);
    margin-top: 1.5rem;
    max-width: 60ch;
}

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

footer {
    padding: 5rem 0 7rem;   /* deep bottom so the fixed Ko-fi button clears it */
    border-top: 1px solid var(--line);
    margin-top: 4rem;
}

footer .container {
    display: flex;
    justify-content: space-between;
    gap: 2rem;
    flex-wrap: wrap;
    align-items: center;
}

footer p { font-size: 0.9rem; color: var(--text-muted); }

footer a {
    color: var(--text-dim);
    text-decoration: none;
    border-bottom: 1px solid var(--line-strong);
}

footer a:hover { color: var(--text); }

.footer-links { display: flex; gap: 1.5rem; font-size: 0.9rem; }
.footer-links a { border-bottom: none; }

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */

@media (max-width: 820px) {
    :root { --pad: 1.5rem; }

    .sticky-nav { padding: 1.1rem 0; }
    .sticky-nav.compact { padding: 0.65rem 0; }

    .nav-wrapper { gap: 0.75rem; }
    .nav-links { margin-left: 0; width: 100%; gap: 1.25rem; }

    footer .container { flex-direction: column; align-items: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    .sticky-nav { transition: none; }
}
