/* colinjones.com — 2026 redesign
   Static stylesheet for the home page and CV.

   Palette and type are lifted from the Ms. Pac-man cabinet: marquee yellow
   and the maze's cyan, on the near-black of the screen. The display face is
   the arcade bitmap font; body copy stays a console monospace so the CV
   remains readable. Dark only — an arcade has no light mode.

   Colour is two-tier: the cabinet hues are primitives referenced only inside
   :root, and rules read the semantic names below them. That keeps @media
   print a single override point rather than a per-rule rewrite. */

/* Press Start 2P — SIL Open Font License 1.1 */
@font-face {
    font-family: 'Press Start 2P';
    src: url('/content/fonts/press-start/press-start-2p-latin.woff2') format('woff2');
    font-weight: normal;
    font-style: normal;
    font-display: swap;
}

:root {
    --bg: #0b0b12;
    --surface: #14141f;
    --surface-2: #1d1d2c;
    --text: #f2f2f7;
    --text-muted: #9a9ab4;
    --border: #2b2b40;

    /* Cabinet primitives — referenced only by the semantic names below. */
    --cyan: #29d8ff;
    --yellow: #ffd400;

    /* Semantic tier: everything outside :root reads these. */
    --accent: var(--yellow);        /* names and titles */
    --accent-alt: var(--cyan);      /* section headings, links, data */
    --accent-strong: #fff06a;       /* hover */

    --font-display: 'Press Start 2P', 'Courier New', monospace;

    /* CRT overlay tuning */
    --scanline-gap: 3px;    /* pitch of the horizontal lines */
    --scanline-dark: .3;    /* how black the gaps go */
    --grille: .05;          /* aperture-grill tint strength */

    --shadow: 0 1px 3px rgba(0, 0, 0, .5), 0 10px 30px rgba(0, 0, 0, .45);
    color-scheme: dark;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    /* Console face: monospace needs a touch less size and more leading than
       a proportional sans to stay comfortable over paragraphs. */
    font: 15px/1.75 ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace;
    -webkit-font-smoothing: antialiased;
}

a { color: var(--accent-alt); text-decoration: none; }
a:hover { color: var(--accent-strong); text-decoration: underline; }

img { max-width: 100%; }

/* Lit-marquee treatment shared by both page titles. */
.hero h1, .cv-header h1 {
    color: var(--accent);
    text-shadow: 0 0 24px color-mix(in srgb, var(--accent) 28%, transparent);
}

/* Arcade bitmap display face. It is wide and has no lowercase rhythm, so it
   is used only for short headings, never body copy. */
.display {
    font-family: var(--font-display);
    font-weight: normal;
    letter-spacing: .02em;
    text-transform: uppercase;
    line-height: 1.15;
    margin: 0;
}

/* z-index lifts page content above the fixed arcade backdrop. */
.wrap {
    max-width: 1060px;
    margin: 0 auto;
    padding: 0 1.25rem;
    position: relative;
    z-index: 1;
}

/* ---------- Arcade backdrop ----------
   #arcade, #content and #tilesets are a fixed contract: pacmanicjs.min.js
   appends its canvas to #content and its sprite sheets to #tilesets by id.
   Do not rename them. See assets/arcade.js. */

#arcade {
    position: fixed;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}

/* Cover the viewport at the arcade's own proportions, cropping whichever axis
   overflows. On a landscape screen that means filling the width and cropping
   height evenly, which lands the crop on the ghost house at the maze's centre;
   on a portrait phone it fills the height and crops the sides instead. */
#arcade #content {
    width: max(100vw, calc(100vh * 490 / 670));
    aspect-ratio: 490 / 670;
}

/* Fade the canvas rather than the wrapper: nesting two opacities would force
   the animating canvas into an intermediate composited surface for the whole
   transition. !important beats the inline width/height Pixi writes. */
#arcade canvas {
    display: block;
    width: 100% !important;
    height: 100% !important;
    opacity: 0;
    transition: opacity 1.6s ease;
    image-rendering: pixelated;
}

body.arcade-ready #arcade canvas { opacity: .26; }

/* CRT overlay: scanlines, an aperture-grill triad, and a vignette, stacked as
   three backgrounds on ONE pseudo-element so the whole treatment is a single
   static layer composited over the animating canvas rather than three.
   First background paints on top, so the vignette darkens the grille too.
   Invisible if the backdrop never loads (it fades to --bg over --bg). */
#arcade::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    background:
        radial-gradient(ellipse 75% 65% at 50% 50%,
            transparent 0%,
            transparent 30%,
            color-mix(in srgb, var(--bg) 70%, transparent) 70%,
            var(--bg) 100%),
        repeating-linear-gradient(to right,
            rgba(255, 0, 0, var(--grille)) 0 1px,
            rgba(0, 255, 0, var(--grille)) 1px 2px,
            rgba(0, 0, 255, var(--grille)) 2px 3px),
        repeating-linear-gradient(to bottom,
            transparent 0 calc(var(--scanline-gap) - 1px),
            rgba(0, 0, 0, var(--scanline-dark)) calc(var(--scanline-gap) - 1px) var(--scanline-gap));
}

#tilesets { display: none; }

/* ---------- Home ---------- */

.hero {
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 4rem 0 3rem;
}

/* The bitmap face is very wide per character, so display sizes are small. */
.hero h1 { font-size: clamp(1.6rem, 5.2vw, 3.4rem); }

.contact-row {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    align-items: center;
    margin-top: 1.75rem;
}

.btn {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    padding: .6rem 1.1rem;
    border: 2px solid var(--border);
    background: var(--surface);
    color: var(--text);
    font-weight: 600;
    box-shadow: var(--shadow);
    transition: transform .12s ease, border-color .12s ease;
}

.btn:hover { transform: translateY(-2px); border-color: var(--accent); text-decoration: none; color: var(--text); }

.btn svg { width: 18px; height: 18px; fill: currentColor; }

/* ---------- Cards ---------- */

.section-title {
    font-size: 1rem;
    color: var(--accent-alt);
    margin: 0 0 1.5rem;
}

.cards {
    display: grid;
    /* Capped max so a lone card stays card-sized instead of spanning the row. */
    grid-template-columns: repeat(auto-fit, minmax(230px, 360px));
    gap: 1.25rem;
    padding: 0;
    margin: 0 0 4rem;
    list-style: none;
}

.card {
    background: var(--surface);
    border: 2px solid var(--border);
    overflow: hidden;
    box-shadow: var(--shadow);
    transition: transform .15s ease, border-color .15s ease;
}

.card:hover { transform: translateY(-4px); border-color: var(--accent); }

.card a { display: block; color: inherit; }
.card a:hover { text-decoration: none; color: inherit; }

.card img {
    width: 100%;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    display: block;
    background: var(--surface-2);
}

.card .card-body { padding: .9rem 1rem 1rem; }
.card h3 { margin: 0 0 .25rem; font-size: 1.05rem; }
.card p { margin: 0; color: var(--text-muted); font-size: .9rem; }

/* ---------- CV ---------- */

.cv-header {
    padding: 3.5rem 0 2rem;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
}

.cv-header h1 { font-size: clamp(1.4rem, 4.4vw, 2.6rem); }

/* Reads as the second line of an arcade title screen, not as body copy. */
.cv-header .occupation {
    font-family: var(--font-display);
    font-size: .62rem;
    text-transform: uppercase;
    letter-spacing: .1em;
    color: var(--accent-alt);
    margin: 1.1rem 0 0;
}

.cv-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: 2.5rem;
    align-items: start;
}

.panel {
    background: var(--surface);
    border: 2px solid var(--border);
    box-shadow: var(--shadow);
    padding: 1.5rem 1.75rem;
    margin-bottom: 1.5rem;
}

.panel > h2 {
    font-family: var(--font-display);
    font-weight: normal;
    text-transform: uppercase;
    font-size: .82rem;
    letter-spacing: .02em;
    line-height: 1.4;
    margin: 0 0 1.4rem;
    color: var(--accent-alt);
}

.about-flex { display: flex; gap: 1.5rem; align-items: flex-start; }
.about-flex .portrait { width: 150px; flex: none; filter: grayscale(1); }

.job { padding: 1.1rem 0; border-top: 2px solid var(--border); }
.job:first-of-type { border-top: 0; padding-top: 0; }

.job-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem 1rem; }
.job-head h3 { margin: 0; font-size: 1.15rem; color: var(--accent); }

.date {
    font-family: var(--font-display);
    font-size: .58rem;
    color: var(--accent-alt);
    background: color-mix(in srgb, var(--accent-alt) 13%, transparent);
    padding: .35rem .7rem;
    white-space: nowrap;
}

.role { color: var(--text-muted); font-weight: 600; margin: .35rem 0 .5rem; }

.job ul { padding-left: 1.2rem; margin: .5rem 0; }
.job li { margin-bottom: .5rem; }

.keywords {
    font-size: .85rem;
    color: var(--text-muted);
    border-left: 4px solid var(--border);
    padding-left: .8rem;
    margin: .75rem 0 0;
}

/* Collapsible extra detail */
.more { display: none; }
.job.expanded .more { display: revert; }

.more-link {
    background: none;
    border: 0;
    padding: 0;
    color: var(--accent-alt);
    font: inherit;
    font-size: .9rem;
    font-weight: 600;
    cursor: pointer;
}
.more-link:hover { color: var(--accent-strong); text-decoration: underline; }

/* Label/value facts (Personal panel) */
.facts { margin: 0; }

.facts dt {
    font-family: var(--font-display);
    font-size: .55rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-muted);
    margin-bottom: .5rem;
}

.facts dd { margin: 0 0 1.2rem; }
.facts dd:last-child { margin-bottom: 0; }

/* Screenshots / galleries */
.shots {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: .9rem;
    padding: 0;
    margin: 0;
    list-style: none;
}

.shots img {
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    border: 2px solid var(--border);
    display: block;
    transition: transform .12s ease;
    background: #fff;
}

.shots a:hover img, .shots a:focus-visible img { transform: scale(1.04); border-color: var(--accent); }

/* In the narrow sidebar, gallery tiles sit two-up */
aside .shots { grid-template-columns: repeat(2, 1fr); }

/* Footer */
.site-footer {
    border-top: 2px solid var(--border);
    margin-top: 3rem;
    padding: 1.5rem 0 2.5rem;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 1rem;
    color: var(--text-muted);
    font-size: .88rem;
}

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

@media (max-width: 820px) {
    .hero { padding-top: 3rem; }
    .cv-grid { grid-template-columns: 1fr; }
    .about-flex { flex-direction: column; }
}

/* ---------- Print (CV to PDF) ---------- */

@media print {
    /* Overriding the semantic tier is enough — no rule reads a cabinet
       primitive directly, so this re-themes the whole page. */
    :root {
        --bg: #fff; --surface: #fff; --surface-2: #fff;
        --text: #000; --text-muted: #444;
        --border: #bbb;
        --accent: #000; --accent-alt: #000; --accent-strong: #000;
        --shadow: none;
    }

    body { font-size: 12px; }
    .no-print, .more-link { display: none; }
    .more { display: revert; }
    .hero h1, .cv-header h1 { text-shadow: none; }
    .panel { border: 0; padding: 0; margin-bottom: 1.2rem; }
    .cv-grid { grid-template-columns: 1fr; }
    .cv-header { padding-top: 0; }
    .page-break { break-before: page; }
    .job, .panel { break-inside: avoid-page; }
    /* Scoped to beat `aside .shots`, which is where every gallery now lives. */
    aside .shots { grid-template-columns: repeat(5, 1fr); }
}
