@import url(fonts.css);

/* Paper treatment, wired up to test.html via _layouts/paper.liquid. index.html
   still uses main.css and is untouched by anything in here. */

:root {
    /* The page is one sheet of dark green ledger stock lying on a near-black
       desk. Every colour below is either the stock, the desk, or ink on it. */
    --desk: #080c09;
    --stock: #17271b;
    --stock-shade: #1e3324;
    --rule: #2f4b35;

    /* The two lines printed on the blank stock, before any ink goes down. */
    --margin-line: rgba(212, 145, 34, 0.4);
    --ledger-line: rgba(231, 225, 200, 0.045);

    --text-body: #e7e1c8;
    --text-muted: #a8b79c;
    --text-heading: #8fcb7a;
    --accent: #e3a53a;
    --link: #e3a53a;
    --focus-ring: #e3a53a;
    /* Margin notes are pencil, not ink: quieter than body text on purpose so
       they read as added afterwards rather than as part of the document. */
    --pencil: #9db393;

    --sheet-edge: rgba(255, 255, 255, 0.055);
    --sheet-shadow: 0 2px 3px rgba(0, 0, 0, 0.5), 0 26px 60px -18px rgba(0, 0, 0, 0.9);

    /* Fine tooth is light on dark stock, because fibre catches light; the same
       noise has to multiply on cream stock or it just fogs the sheet. */
    --fiber-blend: screen;
    --fiber-opacity: 0.05;
    --mottle-opacity: 0.9;

    /* Where the printed margin rule sits, measured from the sheet's edge. */
    --margin-x: 4.75rem;
    /* Ledger ruling pitch. Also the tile height of the ruling background. */
    --ledger-pitch: 1.75rem;

    /* High-frequency tooth. Reads as the surface of the stock up close. */
    --tex-fiber: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='f'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23f)'/%3E%3C/svg%3E");

    /* Low-frequency blotching, on a deliberately large tile so the repeat is
       hard to spot. This is the layer that separates "a scan of real stock"
       from "a noise overlay": even grain reads as an effect, uneven pulp
       reads as a material. feComponentTransfer squashes the noise into
       [0.72, 1.02] so multiplying darkens by at most ~28% and never blows out. */
    --tex-mottle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='700' height='700'%3E%3Cfilter id='m'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.008' numOctaves='5' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='0.3' intercept='0.72'/%3E%3CfeFuncG type='linear' slope='0.3' intercept='0.72'/%3E%3CfeFuncB type='linear' slope='0.3' intercept='0.72'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23m)'/%3E%3C/svg%3E");

    /* Masks rather than background images, so the stroke picks up currentColor
       and needs no per-theme duplicate. */
    --squiggle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='9' viewBox='0 0 220 9' preserveAspectRatio='none'%3E%3Cpath d='M3 6.1 C 32 3, 55 7.5, 88 4.8 C 120 2.4, 148 7.7, 178 4.3 C 195 2.5, 208 5.3, 217 4' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round'/%3E%3C/svg%3E");

    /* Both barbs have to point back up the shaft. The shaft arrives at the tip
       travelling down-left, so both barbs run up-right, straddling that
       reverse direction by about 28 degrees either side. Getting the sign
       wrong on the second barb splays it downward and the head reads as a
       fork pointing somewhere else entirely. */
    --arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='44' height='26' viewBox='0 0 44 26'%3E%3Cg fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M41 7 C 30 4, 14 7, 5 14'/%3E%3Cpath d='M5 14 l 8.9 -1.5 M5 14 l 3.7 -8.2'/%3E%3C/g%3E%3C/svg%3E");

    /* Three separator strokes, cycled down the list. One path reused four
       times is worse than a straight border: the eye picks up the identical
       wobble instantly and it stops reading as drawn and starts reading as a
       repeated graphic. The variants differ in where they cross the centre
       line so no two neighbours rhyme. */
    --sep-a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='10' viewBox='0 0 300 10' preserveAspectRatio='none'%3E%3Cpath d='M2 5.5 C 45 3.2, 90 7.1, 135 4.6 C 180 2.4, 225 6.8, 268 4.2 C 282 3.4, 292 4.6, 298 5.1' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E");
    --sep-b: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='10' viewBox='0 0 300 10' preserveAspectRatio='none'%3E%3Cpath d='M3 4.4 C 40 6.9, 85 2.8, 128 5.4 C 172 8, 210 3.1, 252 5.6 C 270 6.6, 286 5, 297 4.3' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E");
    --sep-c: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='10' viewBox='0 0 300 10' preserveAspectRatio='none'%3E%3Cpath d='M2 6 C 38 4.1, 76 6.4, 118 4 C 158 1.7, 198 5.9, 238 4.4 C 262 3.5, 280 6, 298 4.8' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E");

    /* All-vertical on purpose. A bracket with arms needs preserveAspectRatio
       'none' to stretch to the row height, which scales the stem and the arms
       on different axes and renders them at different weights. A plain drawn
       line beside the entry is also the mark people actually make in margins. */
    --margin-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='100' viewBox='0 0 10 100' preserveAspectRatio='none'%3E%3Cpath d='M5.5 2 C 3.2 20, 6.4 42, 4.4 62 C 3.4 76, 5.6 88, 4.8 98' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E");

    /* Ragged along both long edges so the ends of the swipe don't line up.
       The blob fills ~89% of the viewBox height on purpose: an earlier version
       spanned only the middle 73%, which left the tops and tails of letters
       sitting on bare stock while their middles sat on ink. Inconsistent
       backing under one word is harder to read than a weaker wash under all
       of it, so coverage matters more here than opacity does. */
    --marker: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='30' viewBox='0 0 200 30' preserveAspectRatio='none'%3E%3Cpath d='M3 4.5 C 30 1.8, 70 3.8, 110 2.6 C 145 1.6, 175 3.6, 197 4.2 C 198 11, 197 20, 196 26.2 C 160 28.4, 120 26.4, 80 27.4 C 50 28.1, 20 26.4, 4 25.4 C 2.6 19, 2 11, 3 4.5 Z' fill='%23000'/%3E%3C/svg%3E");

    /* Four separate strokes, each overshooting its corner, because a pen drawn
       round a photo never closes the box cleanly. A single closed rect here
       would put the drawing straight back into looking like a CSS border. */
    --drawn-box: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='120' viewBox='0 0 100 120' preserveAspectRatio='none'%3E%3Cg fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M3 6 C 28 3.5, 66 4.5, 97 5.5'/%3E%3Cpath d='M95.5 2.5 C 97.5 42, 96.5 80, 95 116'/%3E%3Cpath d='M97 113.5 C 68 116.5, 30 115.5, 4 113'/%3E%3Cpath d='M5.5 117 C 3 80, 4.5 40, 4 4'/%3E%3C/g%3E%3C/svg%3E");
}

@media (prefers-color-scheme: light) {
    :root {
        --desk: #cdc3a3;
        --stock: #f4efde;
        --stock-shade: #ece4c9;
        --rule: #cfc29a;

        --margin-line: rgba(176, 94, 44, 0.45);
        --ledger-line: rgba(43, 58, 40, 0.05);

        --text-body: #2b3a28;
        --text-muted: #55624c;
        /* Chroma-matched to the dark theme rather than eyeballed. Measured in
           CIELAB, the first pass at these two came out at C 34 and C 11
           against the dark theme's C 49 and C 19, so the headings lost 29% of
           their colourfulness and the handwriting lost 41% - enough to read as
           grey-green on a warm cream ground. Dark inks hold chroma poorly, so
           matching by eye across a light and a dark palette systematically
           under-saturates the light one. #095d2b is C 42 at 6.99:1 contrast,
           #5b6d51 is C 19 at 4.86:1. */
        --text-heading: #095d2b;
        --accent: #c9861b;
        --link: #8a5410;
        --focus-ring: #8a5410;
        --pencil: #5b6d51;

        --sheet-edge: rgba(255, 255, 255, 0.7);
        --sheet-shadow: 0 1px 2px rgba(60, 46, 20, 0.3), 0 20px 44px -16px rgba(60, 46, 20, 0.5);

        --fiber-blend: multiply;
        --fiber-opacity: 0.045;
        --mottle-opacity: 0.75;
    }
}

* {
    box-sizing: border-box;
}

html,
body {
    margin: 0;
    padding: 0;
}

body {
    background: var(--desk);
    color: var(--text-body);
    font-family: "DM Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
    font-size: 1rem;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}

a {
    color: var(--link);
}

a:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 3px;
}

b,
strong {
    color: var(--text-heading);
}

.page {
    min-height: 100vh;
    padding: 2.5rem 2rem 5rem;
}

/* ---------------------------------------------------------------- the sheet */

.sheet {
    position: relative;
    /* isolate keeps the texture layers' blend modes inside the sheet. Without
       it they would blend straight through to the desk and the paper would
       stop looking like a separate object sitting on top of it. */
    isolation: isolate;
    max-width: 65rem;
    min-height: calc(100vh - 7.5rem);
    margin: 0 auto;
    padding: 4.5rem 2.5rem 3.5rem calc(var(--margin-x) + 1.75rem);
    background-color: var(--stock);
    background-image:
        /* the printed margin rule */
        linear-gradient(to right,
            transparent calc(var(--margin-x) - 1px),
            var(--margin-line) calc(var(--margin-x) - 1px),
            var(--margin-line) var(--margin-x),
            transparent var(--margin-x)),
        /* Ledger ruling, deliberately decorative: the text does not sit on
           these and is not meant to. Landing it on them means putting the
           whole page on a 1.75rem baseline grid that every future margin and
           every added paragraph would have to honour, which is a permanent
           tax for a line at 4.5% alpha.

           One 1px line at the top of a tile, repeated by background-size. The
           obvious spelling is a repeating-linear-gradient with the line
           defined between two rem stops, but that makes the line a fraction
           of a pixel tall and the browser rounds each repeat independently,
           so whole runs of lines vanish and reappear down the page. Sizing a
           tile keeps it exactly 1px in every repeat. */
        linear-gradient(to bottom, var(--ledger-line) 0 1px, transparent 1px);
    background-size: 100% 100%, 100% var(--ledger-pitch);
    background-repeat: no-repeat, repeat;
    border-top: 1px solid var(--sheet-edge);
    box-shadow: var(--sheet-shadow);
}

/* Content column is pinned to the margin rule instead of centred in the sheet,
   which leaves the whole right-hand gutter free for the notes. */
.container {
    position: relative;
    z-index: 2;
    max-width: 46rem;
}

/* Reserve the gutter only once there is room for it. Below this the notes
   fall back into the flow and the sheet uses even padding. */
@media (min-width: 68rem) {
    .sheet {
        padding-right: 12rem;
    }
}

.sheet::before,
.sheet::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
}

.sheet::before {
    background-image: var(--tex-fiber);
    mix-blend-mode: var(--fiber-blend);
    opacity: var(--fiber-opacity);
}

.sheet::after {
    background-image: var(--tex-mottle);
    mix-blend-mode: multiply;
    opacity: var(--mottle-opacity);
}

/* ----------------------------------------------------------------- the hero */

header.hero {
    margin-bottom: 3rem;
}

h1.name {
    font-family: "Windsor", Georgia, serif;
    font-weight: 700;
    /* Max is capped by the container, not taste: .container tops out at 46rem.
       The name measures ~13.8x its font-size in Windsor, so anything above
       ~3.1rem is guaranteed to wrap on wide screens. */
    font-size: clamp(2.5rem, 6vw, 3rem);
    line-height: 1.05;
    color: var(--text-heading);
    margin: 0 0 0.5rem;
    text-wrap: balance;
    /* A sub-pixel spread of the ink's own colour thickens the stems the way
       ink bleeds into absorbent stock. Any larger and it reads as a blur. */
    text-shadow: 0 0 0.4px currentColor;
}

/* Keep the surname intact so the name breaks after "Fernando" rather than
   mid-surname. Released below 26rem, where "Legarda Waismann" at the clamp
   floor is wider than a 320px viewport. */
.surname {
    white-space: nowrap;
}

@media (max-width: 26rem) {
    .surname {
        white-space: normal;
    }
}

p.tagline {
    font-family: "Windsor", Georgia, serif;
    font-size: 1.35rem;
    color: var(--text-muted);
    margin: 0 0 1.75rem;
}

/* Wordmarks rather than a row of bordered circles: the circle-icon-button row
   is one of the most template-looking components on the modern web, and the
   names read faster than the glyphs do anyway. */
.link-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 1.5rem;
    margin-bottom: 2rem;
}

.link-row a {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    color: var(--text-body);
    font-size: 0.95rem;
    text-decoration: none;
}

.link-row a:hover {
    color: var(--accent);
}

.link-row .label {
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.25em;
    text-decoration-color: var(--rule);
}

.link-row a:hover .label {
    text-decoration-color: currentColor;
}

.link-row svg {
    width: 1.05rem;
    height: 1.05rem;
    flex-shrink: 0;
}

.intro {
    display: flex;
    align-items: flex-start;
    gap: 2.25rem;
    margin-top: 2.5rem;
}

/* ------------------------------------------------------------- the portrait */

/* A rectangle on its own just looks like an unstyled <img>, so the shape has
   to be paid off: the photo is toned down into the page's ink range, given
   the stock's own tooth over the top, and then boxed in by hand. The drawn
   box is what makes the square read as a decision. */
.portrait-frame {
    position: relative;
    isolation: isolate;
    flex-shrink: 0;
    width: 7.5rem;
    line-height: 0;
}

.portrait {
    display: block;
    width: 100%;
    height: 9rem;
    object-fit: cover;
    /* Pulled toward the palette rather than all the way to monochrome: enough
       that it belongs to the sheet, not so much that it stops being a photo
       of a person. Push grayscale toward 1 for a fully printed look. */
    filter: grayscale(0.55) sepia(0.18) contrast(1.04);
}

/* The stock's tooth, laid over the photo at more than twice the strength it
   has on the bare sheet, so the image reads as printed onto the paper rather
   than pasted on top of it. */
.portrait-frame::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background-image: var(--tex-fiber);
    mix-blend-mode: var(--fiber-blend);
    opacity: 0.13;
    pointer-events: none;
}

.portrait-frame::after {
    content: "";
    position: absolute;
    inset: -0.45rem;
    z-index: 2;
    background-color: var(--accent);
    opacity: 0.75;
    -webkit-mask: var(--drawn-box) no-repeat center / 100% 100%;
    mask: var(--drawn-box) no-repeat center / 100% 100%;
    pointer-events: none;
}

p.bio {
    flex: 1;
    min-width: 0;
    font-size: 1.0625rem;
    margin: 0;
    text-wrap: pretty;
}

@media (max-width: 30rem) {
    .intro {
        flex-direction: column;
        gap: 1.75rem;
    }

    .portrait-frame {
        width: 6rem;
    }

    .portrait {
        height: 7.25rem;
    }
}

/* -------------------------------------------------------------- the drawing */

.heading-text {
    position: relative;
}

/* Drawn once and left there, rather than sliding in on hover. An underline
   that animates is a web effect; an underline that is simply present is an
   annotation, which is the whole point of the page. */
.heading-text::after {
    content: "";
    position: absolute;
    left: -0.12em;
    right: -0.12em;
    bottom: -0.34em;
    height: 0.4rem;
    background-color: var(--accent);
    opacity: 0.85;
    -webkit-mask: var(--squiggle) no-repeat center / 100% 100%;
    mask: var(--squiggle) no-repeat center / 100% 100%;
}

.section-heading {
    font-family: "Windsor", Georgia, serif;
    font-weight: 700;
    font-size: 1.75rem;
    color: var(--text-heading);
    margin: 3.5rem 0 1.75rem;
    text-shadow: 0 0 0.4px currentColor;
}

.drawn-rule {
    height: 0.5rem;
    margin: 3.5rem 0 2.5rem;
    background-color: var(--rule);
    -webkit-mask: var(--squiggle) no-repeat center / 100% 100%;
    mask: var(--squiggle) no-repeat center / 100% 100%;
}

/* The highlighter swipe, shared by the Contact Me link and by the optional
   hover style on the project titles. Each consumer only sets its own opacity.
   Translucent in both cases so the stock's mottle reads through the ink,
   which is what keeps it from going flat the way a solid fill would. */
.hover-highlight .title-text::before,
a.contact-me .heading-text::before {
    content: "";
    position: absolute;
    z-index: -1;
    left: -0.45rem;
    right: -0.45rem;
    /* Negative, so the swipe bleeds past the box and takes the ascenders and
       descenders with it. Kept in em rather than rem so it tracks the two
       different type sizes this rule serves. */
    top: -0.06em;
    bottom: -0.04em;
    /* Each consumer picks its own ink: the swipe has to contrast with whatever
       text is sitting on top of it, and that differs per use. */
    background-color: var(--swipe-ink, var(--accent));
    opacity: 0.3;
    /* Revealed by clipping in from the left rather than by fading. A marker
       cannot fade into existence, it gets drawn, in a direction. Clipping
       also keeps the mask's ragged ends intact, where animating the width
       would stretch them across the sweep. */
    -webkit-clip-path: inset(0 100% 0 0);
    clip-path: inset(0 100% 0 0);
    transition: clip-path 0.26s ease-out, -webkit-clip-path 0.26s ease-out;
    /* A marker held at a slight angle. Skew rather than rotate, so the swipe
       stays on the baseline instead of drifting off it on long titles. */
    transform: skewX(-2.5deg);
    -webkit-mask: var(--marker) no-repeat center / 100% 100%;
    mask: var(--marker) no-repeat center / 100% 100%;
}

/* -------------------------------------------------------------- the entries */

.project-row {
    position: relative;
}

/* Drawn rather than a border. The old 1px --rule line was neither printed by
   the press nor drawn by a hand, which is what made it read as a UI divider
   sitting on a page that has neither. Muted ink, not the rule grey, so it
   belongs to the same layer as the underlines and the photo frame. */
.project-row::before {
    content: "";
    position: absolute;
    top: -0.25rem;
    left: 0;
    right: 0;
    height: 0.55rem;
    background-color: var(--text-muted);
    opacity: 0.5;
    pointer-events: none;
    -webkit-mask: var(--sep-a) no-repeat center / 100% 100%;
    mask: var(--sep-a) no-repeat center / 100% 100%;
}

.project-row:nth-child(3n+2)::before {
    -webkit-mask-image: var(--sep-b);
    mask-image: var(--sep-b);
}

.project-row:nth-child(3n)::before {
    -webkit-mask-image: var(--sep-c);
    mask-image: var(--sep-c);
}

a.project {
    display: block;
    padding: 2rem 0.75rem;
    color: inherit;
    text-decoration: none;
}

/* Nothing on paper changes colour when you point at it, and an opaque row
   tint painted above .sheet::after made the hovered row the one row with no
   mottle on it, so it went visibly flat. Instead the entry gets marked in the
   margin, the way you'd mark a paragraph you meant to come back to. */
.project-row::after {
    content: "";
    position: absolute;
    left: -1.35rem;
    top: 1.5rem;
    bottom: 1.5rem;
    width: 0.6rem;
    background-color: var(--pencil);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
    -webkit-mask: var(--margin-mark) no-repeat center / 100% 100%;
    mask: var(--margin-mark) no-repeat center / 100% 100%;
}

/* Hover is on the row, not the anchor, so pointing at an entry's margin note
   lights up the entry it belongs to. */
.project-row:hover::after {
    opacity: 0.9;
}

/* Scoped away from the highlighter variant on purpose: a marker doesn't
   change the ink it passes over, and amber text on an amber swipe was too
   close in value to read. Under .hover-highlight the title stays its normal
   colour and the swipe alone carries the state. */
.projects:not(.hover-highlight) .project-row:hover .project-name {
    color: var(--accent);
}

.project-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 0.75rem;
    row-gap: 0.2rem;
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    margin: 0 0 0.4rem;
}

.project-role {
    color: var(--accent);
}

.meta-sep {
    margin: 0 0.5em;
    font-weight: 400;
    color: var(--text-muted);
    opacity: 0.7;
}

/* Deliberately quieter than the role: these are supporting metadata, and
   uppercase amber at 700 out-shouts the serif project title next to it. */
.project-context,
.project-year {
    font-weight: 400;
    color: var(--text-muted);
}

.project-year {
    margin-left: auto;
    /* Trailing letter-spacing on the last glyph otherwise pushes the years a
       hair off the card's right edge, breaking the column they form. */
    margin-right: -0.1em;
}

h3.project-name {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    font-family: "Windsor", Georgia, serif;
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--text-heading);
    margin: 0 0 0.5rem;
    line-height: 1.2;
    text-shadow: 0 0 0.4px currentColor;
    transition: color 0.2s ease;
}

/* No plate behind it any more: the logos sit beside the titles as marks, at
   roughly cap height so they read as part of the line rather than as an
   avatar chip parked next to it. */
.project-mark {
    width: 1.9rem;
    height: 1.9rem;
    object-fit: contain;
    flex-shrink: 0;
}

/* Losing the plate loses the guaranteed dark backing it provided, and the
   logos are not all drawn for the same ground. Two of them are white line
   art, and SCP: Made in Heaven is a near-black badge, so each needs help in
   the one theme that works against it. Set logo-ink in _data/projects.yml:
   `light` for pale art, `dark` for dark art. Leave it off if the art already
   holds up on both grounds, as Mouseburg's does. */
@media (prefers-color-scheme: light) {
    .logo-ink-light {
        /* invert flips the luminance, hue-rotate puts the colour back where
           it started, so pale art darkens without turning into its opposite. */
        filter: invert(1) hue-rotate(180deg) saturate(1.15);
    }
}

@media (prefers-color-scheme: dark) {
    .logo-ink-dark {
        /* Inverting the Made in Heaven badge would turn a red-and-black seal
           mint green, so this lifts it instead of flipping it. */
        filter: brightness(1.5) saturate(1.1);
    }
}

/* --- Alternative hover: highlighter swipe -------------------------------
   Add `hover-highlight` to the .projects div in test.html to try this instead
   of the pencil mark; remove it to go back. Nothing else needs editing.
   Translucent on purpose so the stock's mottle still reads through the ink,
   which is the whole reason the old opaque row tint felt wrong. */

.hover-highlight .project-row::after {
    content: none;
}

.hover-highlight .project-name {
    /* Contains the z-index:-1 swipe so it sits behind the title's letters but
       still in front of the sheet, rather than falling through to the page. */
    isolation: isolate;
}

.hover-highlight .title-text {
    position: relative;
}

/* The swipe itself is defined once, up with the other drawn marks. */
.hover-highlight .project-row:hover .title-text::before {
    -webkit-clip-path: inset(0 0 0 0);
    clip-path: inset(0 0 0 0);
}

/* ----------------------------------------------------------------------- */

p.project-desc {
    font-size: 1rem;
    margin: 0;
    /* Not `balance`: browsers skip balancing past ~6 lines, so the longer
       descriptions would silently opt out while the short ones didn't. */
    text-wrap: pretty;
}

@media (max-width: 30rem) {
    .project-year {
        width: 100%;
        margin-left: 0;
        margin-right: 0;
    }

    .project-mark {
        width: 1.6rem;
        height: 1.6rem;
    }
}

/* The gap between the margin rule and the text narrows on small screens, so
   the mark has to tuck in with it or it lands on top of the printed rule. */
@media (max-width: 48rem) {
    .project-row::after {
        left: -1rem;
        width: 0.5rem;
    }
}

/* ------------------------------------------------------------ margin notes */

.note {
    font-family: "Caveat", "Segoe Script", cursive;
    /* Caveat's x-height is small, so it needs roughly 1.3x the size of the
       body text to sit at the same optical weight. */
    font-size: 1.3rem;
    line-height: 1.2;
    color: var(--pencil);
}

@media (min-width: 68rem) {
    .note {
        position: absolute;
        top: 2.6rem;
        left: calc(100% + 2.4rem);
        width: 9.25rem;
        /* Varied rather than a single uniform tilt: every note landing at the
           same angle is the thing that makes rotated text look like a widget
           instead of like handwriting. */
        transform: rotate(-2.2deg);
    }

    .project-row:nth-child(even) .note {
        transform: rotate(1.6deg);
    }

    .note::before {
        content: "";
        position: absolute;
        left: -2.15rem;
        top: 0.4rem;
        width: 1.8rem;
        height: 1.05rem;
        background-color: currentColor;
        -webkit-mask: var(--arrow) no-repeat center / contain;
        mask: var(--arrow) no-repeat center / contain;
        opacity: 0.75;
    }

    .note-hero {
        top: 0.5rem;
    }
}

/* Below the gutter breakpoint the notes rejoin the flow. They stay in the
   handwriting so they still read as something added in the margin. */
@media (max-width: 67.99rem) {
    .note {
        display: block;
        max-width: 28rem;
        margin: 0 0 1.75rem 0.75rem;
        padding-left: 0.9rem;
        border-left: 2px solid var(--margin-line);
    }

    .project-row .note {
        margin-top: -1.25rem;
        margin-bottom: 2rem;
    }
}

/* --------------------------------------------------------------- the signoff */

.sign-off {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 2rem;
}

a.contact-me {
    font-family: "Windsor", Georgia, serif;
    font-weight: 700;
    font-size: clamp(1.5rem, 4vw, 2rem);
    color: var(--link);
    /* Inverted against the project titles: amber is the brighter of the two,
       so it works as the mark and fights the text when it sits behind it.
       Amber ink over a green wash instead of the reverse. */
    --swipe-ink: var(--text-heading);
    text-decoration: none;
    text-shadow: 0 0 0.4px currentColor;
    /* Contains the swipe's z-index:-1 so it sits behind the letters rather
       than falling through to the sheet. */
    isolation: isolate;
}

/* Pushed past the titles' 0.3: this is the one action on the page, and the
   filled-pill hover it replaces was a louder event. Also the ceiling: the
   green wash darkens fast, and past 0.35 the amber stops clearing 3:1
   against it at large-text sizes. */
a.contact-me .heading-text::before {
    opacity: 0.35;
}

a.contact-me:hover .heading-text::before,
a.contact-me:focus-visible .heading-text::before {
    -webkit-clip-path: inset(0 0 0 0);
    clip-path: inset(0 0 0 0);
}

.doodle {
    width: 5.5rem;
    height: auto;
    flex-shrink: 0;
    color: var(--pencil);
    transform: rotate(4deg);
}

@media (max-width: 30rem) {
    .doodle {
        width: 4.25rem;
    }
}

/* ------------------------------------------------------------------ narrow */

@media (max-width: 48rem) {
    :root {
        --margin-x: 2.25rem;
    }

    .page {
        padding: 1rem 0.75rem 2rem;
    }

    .sheet {
        min-height: calc(100vh - 3rem);
        padding: 3rem 1.5rem 2.5rem calc(var(--margin-x) + 1.25rem);
    }
}

@media (prefers-reduced-motion: reduce) {

    *,
    *::before,
    *::after {
        transition: none !important;
        animation: none !important;
    }
}
