/* Fonts are hosted on this server (fonts/, SIL Open Font License, see the OFL-*.txt files there),
   so no visitor data goes to Google. Latin + Latin Extended subsets only. */
@font-face {
    font-family: 'Jersey 10';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/jersey-10-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Jersey 10';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/jersey-10-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Jersey 10 has no → and ←, so this tiny self-made font (two glyphs, same pixel grid; ← is → mirrored)
   fills them in, centred on the digits/caps instead of falling back to another font.
   ?v= changes whenever the font file changes, so browsers don't keep an old copy. */
@font-face {
    font-family: 'Jersey 10';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/jersey-10-arrow.woff2?v=2') format('woff2');
    unicode-range: U+2190, U+2192;
}

:root {
  color-scheme: dark;

  /* ---------- Colors: dark (the default) ---------- */
  --color-background: #0f1117;   /* no longer used for the body: the dotted page colour fills the window */
  --color-page: #282C33;         /* the page and the nav */
  --color-dots: #3A3F48;         /* dot grid on the page */
  --color-primary: #FFFFFF;      /* text */
  --color-secondary: #9C9C9C;    /* quieter text, inactive nav links */
  --color-accent: #EE5959;
  --color-border: #0D99FF;       /* the one bright colour: buttons, headings on the résumé, active tab marker */

  /* Panels that sit on the page (project cards, the console) */
  --color-card: #1B1F26;         /* panel body: deeper than the page */
  --color-card-bar: #30353E;     /* panel title bar: lifted above the page */
  --color-line: #3A3F48;         /* panel borders and dividers */
  --color-text-soft: #C8CBD1;    /* running text on panels: brighter than secondary, quieter than white */
  --color-screen: #0f1117;       /* frame around screenshots */
  --color-live: #4CD964;         /* "runs in your browser" */

  --color-shadow: #0f1117;                        /* hard pixel shadows */
  --shadow-soft: 0 4px 12px rgba(0, 0, 0, 0.4);   /* nav bar, photos, floating buttons */
  --shadow-soft-up: 0 -4px 12px rgba(0, 0, 0, 0.4); /* the tab bar at the bottom */
  --icon-filter: none;                            /* nav icons (drawn in --color-secondary grey) */
  --icon-filter-active: brightness(0) invert(1);  /* the active one: white */

  /* Font */
  --font-primary: 'Jersey 10', monospace;

  /* Layout */
  --page-width: 1440px;           /* the page never grows wider than this */
  --nav-height: 64px;
  --gutter: 40px;                   /* side padding of page content: 40px, 24px on tablets, 16px on phones (below) */
  --bottom-bar: 0px;                /* height of the bottom nav bar on phones (0 while the bar is at the top) */

  /* ---------- Type scale: the same sizes on every page ----------
     Fixed steps (smaller on phones, below) instead of sizes that slide with the window: the pixel font
     Jersey 10 stays sharp only at whole sizes. Line heights are multiples of 16px (chips: with padding). */
  --fs-h1: 40px;                      /* page title, exactly one per page */
  --lh-h1: 48px;
  --fs-h2: 32px;                      /* sections */
  --lh-h2: 48px;
  --fs-h3: 28px;                      /* titles of cards, projects, jobs */
  --lh-h3: 32px;
  --fs-text: 22px;                    /* running text */
  --lh-text: 32px;
  --fs-small: 20px;                   /* dates, captions, notes, secondary details */
  --lh-small: 24px;                   /* also for one-line lists (e.g. features): 32px rows looked like gaps */
  --fs-chip: 18px;                    /* tags and categories */
  --lh-chip: 24px;                    /* with 2px padding and 2px border: chips are 32px tall */
  --fs-button: 24px;

  /* ---------- Spacing: all multiples of 16px (half the large 32px grid), so edges inside the boxes, chips and
     text rows land on the 16px or 32px lines too ---------- */
  --space-title: 16px;                /* from a heading to the text below it */
  --space-gap: 32px;                  /* between columns and between panels (16px on phones) */
  --space-section: 48px;              /* between sections of a page */
  --page-top: 32px;                   /* from the nav to the first thing on a page */
  --page-bottom: 48px;                /* after the last thing on a page */
  --panel-pad: 32px;                  /* inside panels (projects, posts, forms, résumé); 16px on phones */
  /* Width of the content column: 1184px at most (37 x 32), otherwise the page width minus the gutters, rounded
     down to a multiple of 32px: its edges stay on the grid, centred boxes whose width is a multiple of 16px
     (e.g. the 896px column of a blog post) do too, and half of it is a multiple of the 16px dot pattern, so the
     column's left edge always falls on a dot. Used inside <main> (100% = its width). */
  --column: min(1184px, round(down, min(100%, var(--page-width)) - 2 * var(--gutter), 32px));
  /* Dot pattern of the page: a dot every 16px (one per two grid squares), each in the middle of its 16px tile.
     The pattern is shifted so the dots sit on the grid: horizontally centred on the page like the content
     column, vertically starting right below the nav. */
  --dots: radial-gradient(var(--color-dots) 1.5px, transparent 1.5px);
  --dots-size: 16px 16px;
  /* Padding to set on a panel: its 2px border counts towards --panel-pad, so the content starts exactly
     --panel-pad from the panel's outer edge, on the grid */
  --panel-inset: calc(var(--panel-pad) - 2px);

  /* ---------- Hard pixel shadows of panels (smaller on phones, see below) ---------- */
  --shadow-hard: 6px 6px 0 var(--color-shadow);
  --shadow-hard-hover: 8px 8px 0 var(--color-shadow);   /* a clickable panel lifted under the mouse */
  --shadow-hard-active: 3px 3px 0 var(--color-shadow);  /* ... and pressed */
}

/* Tablets: narrower page margins */
@media screen and (max-width: 999px) {
  :root {
    --gutter: 24px;
  }
}

/* Phones: one step smaller headings, tighter spacing, lighter shadows */
@media screen and (max-width: 600px) {
  :root {
    --gutter: 16px;
    --fs-h1: 32px;
    --lh-h1: 48px;
    --fs-h2: 28px;
    --lh-h2: 32px;
    --fs-h3: 24px;
    --lh-h3: 32px;
    --space-gap: 16px;
    --panel-pad: 16px;
    --shadow-hard: 4px 4px 0 var(--color-shadow);
    --shadow-hard-hover: 6px 6px 0 var(--color-shadow);
    --shadow-hard-active: 2px 2px 0 var(--color-shadow);
  }
}

/* ---------- Colors: light ----------
   Used when the visitor's system is set to light and they have not chosen otherwise on the site,
   or when they switched to light with the sun/moon button (data-theme, set by theme.js).
   Same roles as above; the blue and red are a step darker so text in them stays readable on light. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;
    --color-background: #D3D7DE;
    --color-page: #F2F3F6;
    --color-dots: #D6D9DF;
    --color-primary: #15171C;
    --color-secondary: #666C77;
    --color-accent: #D23C3C;
    --color-border: #0A6FBF;
    --color-card: #FFFFFF;
    --color-card-bar: #E4E7EC;
    --color-line: #C6CBD3;
    --color-text-soft: #3A3F4A;
    --color-screen: #E1E4EA;
    --color-live: #1F9A3D;
    --color-shadow: #B7BDC7;
    --shadow-soft: 0 4px 12px rgba(20, 24, 32, 0.14);
    --shadow-soft-up: 0 -4px 12px rgba(20, 24, 32, 0.14);
    --icon-filter: brightness(0.68);
    --icon-filter-active: brightness(0);
  }
}

:root[data-theme="light"] {
  color-scheme: light;
  --color-background: #D3D7DE;
  --color-page: #F2F3F6;
  --color-dots: #D6D9DF;
  --color-primary: #15171C;
  --color-secondary: #666C77;
  --color-accent: #D23C3C;
  --color-border: #0A6FBF;
  --color-card: #FFFFFF;
  --color-card-bar: #E4E7EC;
  --color-line: #C6CBD3;
  --color-text-soft: #3A3F4A;
  --color-screen: #E1E4EA;
  --color-live: #1F9A3D;
  --color-shadow: #B7BDC7;
  --shadow-soft: 0 4px 12px rgba(20, 24, 32, 0.14);
  --shadow-soft-up: 0 -4px 12px rgba(20, 24, 32, 0.14);
  --icon-filter: brightness(0.68);
  --icon-filter-active: brightness(0);
}

/* Short landscape screens (phones held sideways): a slimmer bar leaves more room for the page */
@media screen and (max-height: 500px) {
  :root {
    --nav-height: 48px;
  }
}

html {
    overscroll-behavior: none;
    /* iOS Safari inflates text in landscape without this */
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    /* In-page anchors are not hidden under the fixed nav */
    scroll-padding-top: var(--nav-height);
}

body {
  margin: 0;
  /* One background for the whole window: the page colour with a dot grid (as in the test/ design) */
  background-color: var(--color-page);
  background-image: var(--dots);
  background-size: var(--dots-size);
  background-position: 50% calc(var(--nav-height) - 8px);  /* a dot exactly in the middle and right below the nav */
  color: var(--color-primary);
  font-family: var(--font-primary);
}

img {
  max-width: 100%;   /* an image is never wider than its container */
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
}

/* ---------- Navigation ---------- */
nav {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    display: flex;
    align-items: center;
    width: 100%;
    max-width: var(--page-width);
    height: var(--nav-height);
    margin: 0 auto;
    padding: 0 16px;
    box-sizing: border-box;
    z-index: 10;
    background-color: var(--color-page);
    /* The same light edge as the phone tab bar, on the side that faces the page */
    border-bottom: 2px solid var(--color-line);
    border-radius: 0 0 12px 12px;
    box-shadow: var(--shadow-soft);
}

.home_button {
    display: flex;
    align-items: center;
    justify-content: center;
}

.home_button a {
    display: flex;
    align-items: center;
    min-height: 44px;  /* comfortable tap target */
    padding: 0 4px;
}

/* 13 x 11 px pixel art (same house as in the phone tab bar), drawn at 3px per pixel */
.home_button img {
    width: 39px;
    height: 33px;
    image-rendering: pixelated;
}

.home_button img {
    filter: var(--icon-filter);
}

/* On the homepage: same colour as the active nav link */
.home_button a.active img {
    filter: var(--icon-filter-active);
}

.page_selection {
    display: flex;
    gap: 48px;
    margin-left: auto;  /* links sit at the right end, the house stays left */
}

.page_selection a {
    display: flex;
    align-items: center;
    min-height: 44px;  /* comfortable tap target */
    font-family: var(--font-primary);
    font-size: 28px;
    color: var(--color-secondary);
    text-decoration: none;
    white-space: nowrap;
}

.page_selection a.active {
    color: var(--color-primary);
    text-shadow: 0 0 0.5px currentColor, 0 0 0.5px currentColor;
}

/* Tablets: one step smaller, so all links fit next to each other */
@media screen and (max-width: 999px) {
    .page_selection {
        gap: 24px;
    }

    .page_selection a {
        font-size: 24px;
    }

}

/* Desktop: the house is the only icon, the pages are text links. (The pixel icons and the "Home" label
   belong to the phone tab bar below.) */
.home_button span,
.page_selection img {
    display: none;
}

/* Light / dark switch: a pixel sun or moon after the links (see theme.js) */
.theme_toggle[hidden] {
    display: none;  /* until theme.js has run: without it there is nothing to switch */
}

/* A 48px square with the icon exactly in its middle. The divider to the links is drawn separately, 8px to the
   left of the button (in its margin), so it does not push the icon off centre. */
.theme_toggle {
    position: relative;
    display: grid;
    place-items: center;
    flex: none;
    width: 48px;
    height: 48px;
    margin-left: 40px;  /* 32px from the links to the divider */
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.theme_toggle::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: -8px;
    width: 2px;
    background-color: var(--color-line);
}

/* Tablets: closer to the links (after the rule above, so it wins) */
@media screen and (max-width: 999px) {
    .theme_toggle {
        margin-left: 24px;  /* 16px from the links to the divider, the divider 8px before the button */
    }
}

.theme_toggle img {
    display: block;
    width: 26px;
    height: 22px;
    image-rendering: pixelated;
    filter: var(--icon-filter);
}

.theme_toggle:focus-visible {
    outline: 2px solid var(--color-border);
    outline-offset: 2px;
}

@media (hover: hover) {
    .theme_toggle:hover img {
        filter: var(--icon-filter-active);
    }
}

/* ---------- Phones: tab bar at the bottom, within thumb reach ----------
   One pixel icon + label per page, like an app. --bottom-bar tells the pages how much room to leave. */
@media screen and (max-width: 760px) {
    :root {
        --bottom-bar: var(--nav-height);
    }

    /* No env(safe-area-inset-bottom) here on purpose: the page does not use viewport-fit=cover, so the browser already
       keeps it clear of the system bars. Firefox on Android reports the Android navigation bar's height anyway,
       which made the tab bar twice as tall there (icons on top, empty space below). */
    nav {
        height: var(--bottom-bar);
        padding: 0 4px;
        border-top: 2px solid var(--color-line);
        border-bottom: 0;
        border-radius: 12px 12px 0 0;
        box-shadow: var(--shadow-soft-up);
    }

    /* The two groups dissolve, so all six tabs share the width evenly */
    .home_button,
    .page_selection {
        display: contents;
    }

    .home_button a,
    .page_selection a {
        position: relative;
        flex: 1 1 0;
        align-self: stretch;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 5px;
        min-width: 0;
        min-height: 0;
        padding: 0;
        color: var(--color-secondary);
        font-size: 15px;
        line-height: 1;
        text-decoration: none;
        white-space: nowrap;
        -webkit-tap-highlight-color: transparent;
    }

    /* 13 x 11 px pixel art, drawn at 2px per pixel */
    .home_button img,
    .page_selection img {
        display: block;
        width: 26px;
        height: 22px;
        margin: 0;
        image-rendering: pixelated;
        filter: var(--icon-filter);
    }

    .home_button span,
    .page_selection span {
        display: block;
        overflow: hidden;
        max-width: 100%;
        text-overflow: ellipsis;
    }

    /* "Contact Me" becomes "Contact" in the bar: short labels, as in apps */
    .page_selection .wide {
        display: none;
    }

    .home_button a.active,
    .page_selection a.active {
        color: var(--color-primary);
    }

    .home_button a.active img,
    .page_selection a.active img {
        filter: var(--icon-filter-active);
    }

    /* Pixel marker on the top edge of the current tab */
    .home_button a.active::before,
    .page_selection a.active::before {
        content: "";
        position: absolute;
        top: -2px;
        left: 50%;
        width: 26px;
        height: 4px;
        translate: -50% 0;
        background-color: var(--color-border);
    }

}

/* Phones held sideways: the bar is only 48px but wide, so the label sits next to the icon */
@media screen and (max-width: 760px) and (max-height: 500px) {
    .home_button a,
    .page_selection a {
        flex-direction: row;
        gap: 8px;
    }
}

/* ---------- Headings and text: defaults from the type scale ----------
   :where() keeps these at the lowest specificity, so a page's own rules can still adjust them.
   Screen only: the résumé's A4 print layout has its own sizes and spacing. */
@media screen {
    :where(main) :where(h1, h2, h3) {
        margin: 0 0 var(--space-title);
        color: var(--color-primary);
        font-weight: normal;  /* Jersey 10 has one weight only; "bold" would be faked by the browser */
    }

    :where(main) h1 {
        font-size: var(--fs-h1);
        line-height: var(--lh-h1);
    }

    :where(main) h2 {
        font-size: var(--fs-h2);
        line-height: var(--lh-h2);
    }

    :where(main) h3 {
        font-size: var(--fs-h3);
        line-height: var(--lh-h3);
    }
}

/* A page title for screen readers and search engines on pages that show no title of their own */
.visually_hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* Keyboard focus: the same blue frame on every link and button (pages may refine it) */
:where(a, button, summary):focus-visible {
    outline: 2px solid var(--color-border);
    outline-offset: 2px;
}

/* ---------- Snapped to the grid (see script/snap-to-grid.js) ----------
   Chips, buttons and pictures whose size comes from their content get a rounded size from the script.
   Screen only: the print layout keeps the natural sizes. */
@media screen {
    [style*="--snap-width"] {
        box-sizing: border-box;
        width: var(--snap-width);
        text-align: center;
        justify-content: center;  /* buttons laid out with flex */
        white-space: nowrap;      /* one line, one 32px row */
    }

    /* Rows of chips: each chip keeps its own height (one 32px row), not stretched to its neighbours */
    .chips,
    .tags {
        align-items: flex-start;
    }

    [style*="--snap-pad"] {
        padding-bottom: var(--snap-pad);
    }

    [style*="--snap-height"] {
        box-sizing: border-box;
        height: var(--snap-height);
        object-fit: cover;
    }
}

/* ---------- Page ---------- */
main {
    width: 100%;
    max-width: var(--page-width);
    /* svh = the screen with the browser's address bar visible. Unlike vh/dvh it never changes while the
       address bar slides away, so the page does not re-flow (and the tab bar does not jump) while scrolling,
       and short pages are not scrollable by the bar's height (Firefox on Android) */
    min-height: 100vh;
    min-height: 100svh;
    margin: 0 auto;
    padding-top: var(--nav-height);
    box-sizing: border-box;
    /* No background of its own: the dotted body shows through, so there are no darker side strips */
}

/* Phones: the document itself does not scroll, the page scrolls inside <main> and the tab bar sits below it in
   normal flow (not position: fixed). Reason: Firefox on Android collapses its address bar only when the document
   scrolls, and each time it does so it re-anchors fixed elements with a visible jump. With the document pinned to
   the screen height the address bar stays, and the tab bar never moves - in every browser.
   This block comes after the main rule above on purpose: same specificity, the later rule wins. */
@media screen and (max-width: 760px) {
    html,
    body {
        height: 100%;
        overflow: hidden;
    }

    body {
        display: flex;
        flex-direction: column;
        /* No dots on the body here: <main> fills the screen above the tab bar and carries its own scrolling dots.
           With both, the body's dots showed through <main> at a different offset and doubled every dot. */
        background-image: none;
    }

    /* <main> is the scroll area here: it carries the dots itself ("local"), so they scroll with the page */
    main {
        background-image: var(--dots);
        background-size: var(--dots-size);
        background-position: 50% -8px;  /* centred like the column, from the top of the scrolling page */
        background-attachment: local;
        flex: 1 1 auto;
        min-height: 0;
        padding-top: 64px;  /* the row the light/dark switch floats in (8 + 48 + 8), so it never covers the first thing on a page */
        padding-bottom: 0;
        overflow-y: auto;
        overscroll-behavior-y: contain;
    }

    /* Light / dark switch: a small pixel button in the top right corner, above the page.
       position: fixed is safe here because the document itself never scrolls on phones. */
    .theme_toggle {
        position: fixed;
        top: 8px;
        right: 12px;
        z-index: 5;
        width: 48px;
        height: 48px;
        margin: 0;
        padding: 0;
        border: 2px solid var(--color-line);
        border-radius: 8px;
        background-color: var(--color-page);
        box-shadow: 3px 3px 0 var(--color-shadow);
        transition: translate 0.12s steps(2), box-shadow 0.12s steps(2);
    }

    .theme_toggle::before {
        display: none;
    }

    .theme_toggle:active {
        translate: 2px 2px;
        box-shadow: 1px 1px 0 var(--color-shadow);
    }

    nav {
        position: static;
        order: 2;
        flex: none;
    }
}

/* No animated transitions for visitors who turned animations off in their system settings */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
    }
}

/* ---------- Page switches ---------- */
/* Cross-fade between pages instead of a blank flash (Chrome, Edge, Safari 18.2+; other browsers switch as before).
   The nav bar has its own name, so it stays in place while only the page content fades. */
@view-transition {
    navigation: auto;
}

nav {
    view-transition-name: site-nav;
}

::view-transition-old(root),
::view-transition-new(root) {
    animation-duration: 150ms;
}

@media (prefers-reduced-motion: reduce) {
    @view-transition {
        navigation: none;
    }
}
