/* ------------------------------------------------------------
   PALETTE: muted green phosphor (P1-ish) on near-black glass
   ------------------------------------------------------------ */
:root {
    --bg:      #070a07;   /* deepest black: page root, inverse-video text */
    --chassis: #0c120c;   /* case around the panels */
    --panel:   #151c15;   /* lit screen: dark olive-grey, not black, to avoid halation */
    --panel-2: #1a231a;   /* thumbnail wells / placeholder fill */
    /* Rules are translucent phosphor so the scanlines beneath show through
       the border lines themselves. On the panel they resolve to roughly
       #2d4129 and #46663d. */
    --line:    rgba(143, 212, 122, 0.20);   /* faint rule */
    --line-2:  rgba(143, 212, 122, 0.40);   /* strong rule */
    --frame-glow: 0 0 0.6em rgba(143, 212, 122, 0.18), inset 0 0 0.6em rgba(143, 212, 122, 0.10);
    --dim:     #7a9a71;   /* secondary text (5.5:1 on panel) */
    --text:    #b5bfad;   /* body text: low saturation, ~9:1 on panel */
    --bright:  #e2f5d6;   /* headings */
    --accent:  #8fd47a;   /* links, cursor, highlights (10.9:1) */
    --glow:    0 0 0.35em rgba(143, 212, 122, 0.35);   /* headings only, never body text */
    /* CRT texture. Used as *background* layers only, so it always paints
       behind text and images, never in front of them. */
    --scan:     repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.28) 0 1px, transparent 1px 3px);
    --vignette: radial-gradient(ellipse at center, transparent 50%, rgba(0, 0, 0, 0.6) 100%);
    --page: 54rem;   /* content width: 14rem thumb + 1.5rem gap + a 35em measure */
    /* A system sans stack: nothing is downloaded, so there is no network
       cost, no FOUT and no layout shift. Nothing on this page needs
       column alignment, and a proportional face reads better than a
       monospace one at the small sizes used for meta lines. The leading
       entries apply only where already installed; in practice this
       resolves to Segoe UI on Windows, the system face on Apple, Roboto
       on Android and Noto/DejaVu on Linux. */
    --ui: "Inter", "Source Sans 3", "Segoe UI", Roboto, "Helvetica Neue",
          Arial, "Noto Sans", "DejaVu Sans", sans-serif;
}

*, *::before, *::after { box-sizing: border-box; }

/* The chassis texture lives on the root so it propagates to the canvas.
   body stays background-free so the fixed vignette layer below can sit
   between the canvas and the content. */
html {
    background-color: var(--chassis);
    background-image:
        var(--scan),
        radial-gradient(ellipse at 50% 0%, rgba(143, 212, 122, 0.10), transparent 60%);
    color: var(--text);
    font-family: var(--ui);
    font-size: 100%;
    line-height: 1.6;
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}
body {
    margin: 0;
    min-height: 100vh;
}
/* Chassis vignette: a viewport-sized fixed layer behind all content.
   This does not rely on background-attachment: fixed, which iOS Safari
   ignores, so the chassis darkens toward the viewport edges everywhere. */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background: var(--vignette);
}
/* Panels carry the same scanlines plus a viewport-fixed vignette so they
   read as one piece of glass with the chassis. Where fixed attachment is
   unsupported (iOS Safari) the vignette degrades to per-panel corner
   shading; scanlines and text are unaffected. */
.crt {
    background-color: var(--panel);
    background-image: var(--vignette), var(--scan);
    background-attachment: fixed, scroll;
    background-clip: border-box;   /* texture continues under the frame */
    box-shadow: var(--frame-glow);
}

::selection { background: var(--accent); color: var(--bg); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

a {
    color: var(--accent);
    text-decoration: underline;
    text-decoration-color: var(--line-2);
    text-underline-offset: 0.2em;
}
/* Inline links brighten on hover. The solid inverse-video block is kept
   for isolated controls (nav cells, expand buttons, title-panel links)
   where it cannot swallow a line of prose. */
a:hover, a:focus-visible {
    color: var(--bright);
    text-decoration-color: var(--accent);
}
.links a:hover, .links a:focus-visible {
    background: var(--accent);
    color: var(--bg);
    text-decoration: none;
    text-shadow: none;
}

h1, h2, h3 {
    color: var(--bright);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    line-height: 1.2;
    margin: 0;
    text-shadow: var(--glow);
}
p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }
code { font: inherit; color: var(--bright); }
.dim { color: var(--dim); }

.skip {
    position: absolute;
    left: -200vw;
    top: 0.5rem;
    padding: 0.5rem 1rem;
    background: var(--accent);
    color: var(--bg);
    z-index: 200;
}
.skip:focus { left: 0.5rem; }

/* ------------------------------------------------------------
   WIDTH: every panel column shares one measure
   ------------------------------------------------------------ */
/* The header, the nav and the panel column are all laid out by the same
   rule so their edges cannot drift apart. Each is a --page-wide box with
   1rem of side padding, and the panels are its full-width children; the
   header wrapper adds no padding of its own for the same reason main
   does not. */
.boot-wrap, .menubar ul, main {
    max-width: var(--page);
    margin-inline: auto;
    padding-inline: 1rem;
}

/* ------------------------------------------------------------
   HEADER: title panel
   ------------------------------------------------------------ */
.boot-wrap { margin-block: 1.5rem; }
.boot {
    padding: 1.5rem 1.25rem;
    border: 3px double var(--line-2);
}
.boot > * { --i: 0; }
h1.name {
    font-size: clamp(2rem, 7vw, 3.75rem);
    letter-spacing: 0.12em;
    margin: 0 0 0.5rem;
}
.tagline {
    color: var(--text);
    font-size: 1.05rem;
    margin: 0 0 1.5rem;
    max-width: 34em;
}
.links {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.2rem 1.5ch;
    margin: 0;
    padding-top: 1.25rem;
    border-top: 1px solid var(--line);
}
.links dt {
    color: var(--dim);
    font-size: 0.85rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    align-self: center;
}
.links dd { margin: 0; overflow-wrap: anywhere; }

/* ------------------------------------------------------------
   NAV: menu bar
   ------------------------------------------------------------ */
.menubar {
    position: sticky;
    top: 0;
    z-index: 50;
    border-top: 1px solid var(--line-2);
    border-bottom: 1px solid var(--line-2);
}
.menubar ul {
    padding-block: 0.5rem;
    list-style: none;
    display: flex;
    gap: 0.5rem;
    overflow-x: auto;
    white-space: nowrap;
    scrollbar-width: none;
}
.menubar ul::-webkit-scrollbar { display: none; }
/* Each destination is a discrete outlined cell, matching the expand
   controls, so the bar reads as a row of buttons rather than a line of
   spaced words. */
.menubar a {
    display: inline-block;
    padding: 0.4rem 1ch;
    border: 1px solid var(--line-2);
    color: var(--accent);
    background: var(--panel);
    text-decoration: none;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    font-size: 0.85rem;
}
.menubar a::before { content: "\203A\00a0"; color: var(--dim); }
.menubar a:hover, .menubar a:focus-visible {
    background: var(--accent);
    color: var(--bg);
    border-color: var(--accent);
}
.menubar a:hover::before, .menubar a:focus-visible::before { color: var(--bg); }

/* ------------------------------------------------------------
   MAIN: segments (panels) and records (entries)
   ------------------------------------------------------------ */
main {
    padding-bottom: 3rem;
}
/* Panels are fieldsets so the section label can use a <legend>. A legend
   carves a genuine gap in the border rather than painting over it, so
   there is nothing to align and nothing to round: it stays exact at any
   zoom or device pixel ratio. Masking approaches (a background strip,
   mask-composite, border-image) all failed at fractional ratios or
   could not render a double border. The fieldset is presentational
   only — role="presentation" keeps it out of the accessibility tree as
   a form group, and the real <h2> lives inside the legend. */
.seg-wrap { scroll-margin-top: 4rem; }
.seg {
    border: 3px double var(--line-2);
    margin: 2.5rem 0 0;
    padding: 1.25rem;
    min-width: 0;   /* fieldsets default to min-content; without this
                       wide children (pre, long URLs) blow out the page */
    scroll-margin-top: 4rem;
}
/* <legend> only allows phrasing content, so it cannot wrap an <h2>.
   It carries role="heading" aria-level="2" instead and is styled to
   match the other headings. */
.seg > legend {
    margin-left: 0.6rem;
    padding: 0 0.6ch;
    color: var(--bright);
    font-weight: 700;
    font-size: 1.2rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    line-height: 1.2;
    /* Doubled glow: at this size a single shadow reads much fainter
       than it does on the large name in the title panel. */
    text-shadow: var(--glow), var(--glow);
}
.seg > .lede { max-width: 34em; }
.seg > .lede + .entry { border-top: 1px solid var(--line); margin-top: 1.25rem; padding-top: 1.25rem; }

/* An entry is one grid: thumbnail, text, then the expandable
   record. On wide screens the record sits under the text in the same
   column, so opening it never shifts the prose sideways. .entry-head is
   a semantic grouping only and contributes no box of its own. */
.entry {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1rem 1.5rem;
    align-items: start;
    border-top: 1px solid var(--line);
    padding: 1.25rem 0 0.25rem;
    margin-top: 1.25rem;
    scroll-margin-top: 4rem;
}
.entry:first-of-type { border-top: 0; margin-top: 0; padding-top: 0; }
.entry-head { display: contents; }
.entry h3 {
    font-size: 1.05rem;
    margin: 0 0 0.25rem;
}
.meta {
    color: var(--dim);
    font-size: 0.85rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    margin: 0 0 0.75rem;
}
/* Items are divided by a border rule rather than a glyph, so the divider
   is bounded to the text height and never wraps on its own. Every item
   carries a left border; the container clips 1ch on the left so the
   border of whichever item starts a line falls outside the box. */
.meta:has(> span) {
    display: flex;
    flex-wrap: wrap;
    overflow: hidden;
    margin-left: -1ch;
}
.meta > span {
    padding: 0 1ch;
    margin-left: -1px;
    border-left: 1px solid var(--line-2);
}
.entry-body { max-width: 34em; }

/* Thumbnails: shown as-is, no tint, overlay or crop. A fixed square
   well keeps every row the same height; images that are not square
   letterbox against the well colour rather than being cut. */
.thumb {
    position: relative;
    display: block;
    aspect-ratio: 1 / 1;
    border: 1px solid var(--line-2);
    background: var(--panel-2);
    overflow: hidden;
}
.thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}
a.thumb:hover, a.thumb:focus-visible { border-color: var(--accent); }

/* Expandable record body: pure HTML details/summary. */
details { margin: 0; }
summary {
    list-style: none;
    cursor: pointer;
    display: inline-block;
    padding: 0.35rem 0.9rem;
    border: 1px solid var(--line-2);
    color: var(--accent);
    font-size: 0.85rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    user-select: none;
}
summary::-webkit-details-marker { display: none; }
summary::before { content: "[+] "; color: var(--dim); }
details[open] summary::before { content: "[\2212] "; }
summary .open-label { display: none; }
details[open] summary .closed-label { display: none; }
details[open] summary .open-label { display: inline; }
summary:hover, summary:focus-visible { background: var(--accent); color: var(--bg); }
summary:hover::before, summary:focus-visible::before { color: var(--bg); }
.more {
    margin-top: 1rem;
    padding: 1rem 0 0.5rem 1rem;
    border-left: 2px solid var(--line-2);
    max-width: 35em;
}
.more img, .more iframe {
    display: block;
    max-width: 100%;
    height: auto;
    margin: 0.5rem 0 1rem;
    border: 1px solid var(--line-2);
    background: var(--panel-2);
}
.more figure { margin: 0 0 1em; }
.more figcaption { color: var(--dim); font-size: 0.9rem; }
.frame {
    aspect-ratio: 16 / 9;
    width: 100%;
    max-width: 40rem;
    margin: 0.5rem 0 1rem;
}
.frame iframe { width: 100%; height: 100%; margin: 0; }

/* Writing list */
.docs { list-style: none; margin: 0; padding: 0; }
.docs li {
    padding: 0.9rem 0;
    border-top: 1px solid var(--line);
}
.docs li:first-child { border-top: 0; padding-top: 0; }
.docs li::before { content: "\25AA "; color: var(--accent); }
.docs .meta { display: inline; margin: 0; }
.docs li > p { max-width: 35em; margin: 0; }

/* ------------------------------------------------------------
   TIERS: compact records, work records
   ------------------------------------------------------------ */
/* Compact records: no thumbnail column, tighter spacing, smaller title.
   Same details/summary body; they read as a list until opened. */
.entry.compact {
    padding-top: 0.9rem;
    margin-top: 0.9rem;
    gap: 0.5rem 1.5rem;
}
.entry.compact h3 { font-size: 0.95rem; }
.entry.compact .meta { margin-bottom: 0.4rem; }
.entry.compact .entry-body { color: var(--text); }
.entry.compact > details { margin-top: -0.25rem; }
/* A work record: one meta line per role, stacked tight. */
.entry.roles .meta { margin-bottom: 0.3rem; }
.pending { color: var(--dim); }
/* The smaller-projects panel is a single always-visible list. */
.also .docs { margin-top: 1rem; }
.also .docs li { padding: 0.5rem 0; max-width: 40em; }
.also .docs li::before { content: none; }
.also .docs strong { color: var(--bright); font-weight: 700; }

/* ------------------------------------------------------------
   MOTION: staged reveal of the title panel
   ------------------------------------------------------------ */
@keyframes appear { from { opacity: 0; } to { opacity: 1; } }
.boot > * {
    animation: appear 0.05s steps(1) both;
    animation-delay: calc(var(--i) * 140ms);
}
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    .boot > * { animation: none; }
}

/* ------------------------------------------------------------
   LAYOUT: wider screens get side-by-side records
   ------------------------------------------------------------ */
@media (min-width: 40em) {
    .entry.has-thumb { grid-template-columns: 14rem minmax(0, 1fr); }
    .entry.has-thumb > details { grid-column: 2; }
    .seg { padding: 2rem 1.75rem 1.5rem; }
}
@media (min-width: 60em) {
    .boot { padding: 2rem 1.75rem; }
    .boot-wrap { margin-top: 2.5rem; }
}
@media (max-width: 40em) {
    /* Tighten the cells so all four fit without clipping at 360px+;
       the bar still scrolls below that. */
    .menubar ul { padding-block: 0.4rem; gap: 0.3rem; }
    .menubar a { padding: 0.35rem 0.5ch; font-size: 0.75rem; letter-spacing: 0.05em; }
    .menubar a::before { content: none; }
    .links { grid-template-columns: 1fr; gap: 0; }
    .links dd { margin-bottom: 0.5rem; }
    .seg { padding-left: 0.9rem; padding-right: 0.9rem; }
    .more { padding-left: 0.75rem; }
}
@media (prefers-contrast: more) {
    :root { --dim: #9ab98f; --line: rgba(143, 212, 122, 0.4); --text: #d6ddd0; }
}
@media print {
    /* Browsers drop backgrounds by default, so every explicit light
       colour has to be reset or it prints invisible on white. */
    *, *::before, *::after {
        color: #000 !important;
        background: none !important;
        text-shadow: none !important;
        box-shadow: none !important;
        animation: none !important;
    }
    html { background: #fff !important; }
    .menubar, body::before { display: none; }
    a { text-decoration: underline; }
}
