/* Value-menu mock, eighth pass (2026-10-28).
   Grant's instruction: take every note as a master web designer and marketer would. Think through what the note is for, what a literal change would break,
   and ship the better form. Pass 7 did what he said. Pass 8 is what I would ship. What changed, and why:
   - One grid for the whole page (pass 8w): a label column as wide as the longest label (100 px), a label gap of 1.75 gutters (--gap-col, the gap between the cards), then the content, which runs to the right margin as two columns.
     Everything below the header starts on the content line: the credo and the cards, the claim and the Expertise names, the blog image, the quotes and the contact text. His portrait stands on the page margin beside the labels.
     The second column's line is shared by the second work card and the Expertise lines; the hero copy is placed from his portrait instead, so it stays close to him. Labels sit on the baseline of their content's first line (on the top of the picture in the Blog row).
   - The label "About" is gone from the first row: a photo, a name and a title do not need a label, and the space is worth more to the photo.
   - The availability line sits at the end of the intro, where the reader finishes learning who he is, in plain 14 px type, with its full wording restored.
   - A work card is a picture, a name and one line, and every line fits on one line. Ectomatter's card says it is in development; Ectoprompt and Contentingency are not shipped either, and labeling them waits on Grant's word (docs/deferred.md, Pass 21).
   - "What I make" is "Expertise" (the live site's own word), with a line per row written for this page.
   - The blog is back (Journal), the contact block shows the address and the location, and the phone keeps a way to the resume.
   - Four type sizes: 64, 28, 18, 14. Mono is for metadata only (a date, a state, a role) and code.
   Radius rule (pass 5): surface 12 px = the product panels and his photo; press 8 px = the buttons; screen 6 px = a window inside a panel; tag 2 px = a small box;
   circle = the heading dots only (pass 8e); square = hairlines and the angled beam. */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  --bg: #fff; --line: #e4e4e7; --line-soft: #eeeef1; --line-strong: #d4d4d8;   /* --line-soft: the rules inside a section; --line: the ones between sections */
  --fg: #18181b; --fg-2: #3f3f46; --muted: #52525b; --subtle: #71717a;
  --orange: #FF6000;                 /* the live site's orange */
  --d1: #FFB020; --d2: #FF9A1F; --d3: #FF7A12; --d4: #FF6000; --d5: #D8480B;   /* the heading dots: five steps of one orange, amber to burnt, so the page ends on the color of the button */
  --g1: #FF8A1C; --g2: #F97316; --g3: #EA580C; --g4: #C2410C;   /* the gradient: one orange at 105 degrees, from #FF8A1C (2.4 to 1 against white, about the lightest the word can start) to burnt orange #C2410C (5.2 to 1) */
  --gradient: linear-gradient(105deg, var(--g1) 0%, var(--g2) 28%, var(--g3) 62%, var(--g4) 100%);
  --paper: linear-gradient(155deg, #faf5ef, #f7ebe0);   /* the one tint behind every product panel, reference tile and the blog picture: warm paper, so the pictures carry the color (chosen 2026-10-31 from nine treatments; colors/ keeps them) */
  --font-display: "Schibsted Grotesk", system-ui, sans-serif;
  --font-sans: "Geist", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "Geist Mono", ui-monospace, "SFMono-Regular", monospace;
  --size-display: clamp(2.6rem, 5.6vw, 4rem);        /* 64px: the title */
  --size-statement: clamp(1.35rem, 2.3vw, 1.75rem);  /* 28px: the sentence, card names, expertise names */
  --size-text: 1.125rem;                             /* 18px: body text and card lines */
  --size-small: .875rem;                             /* 14px: nav, labels, links, metadata, the button */
  --r-surface: 12px; --r-press: 8px; --r-screen: 6px; --r-tag: 2px;
  --label-col: 6.25rem;                              /* the label column is as wide as its longest label (References, 95 px) and a few pixels more: 100 px */
  --label-gap: calc(var(--gap-col) * 1.75);          /* label to content is one and three quarter gutters (56 px at 1440): a title gets room before its content, and the longest title (References) is no longer jammed against it */
  --photo-w: clamp(15rem, 34vw, 27.5rem);           /* his portrait: 440 px from 1294 px wide up (549 tall), smaller below, tall enough to hold the title, story and availability line */
  --hero-gap: clamp(6.25rem, 7.8vw, 7rem);              /* portrait to copy: 112 px at 1440, the figure he asked for; it follows the portrait, not the work grid */
  --gap-col: clamp(1.25rem, 2.2vw, 2rem);             /* the page's one gutter: label to content, and between the two work cards */
  --cap-gap: .6rem;                                  /* the space above the capital C of the title inside its line box */
  --pad: clamp(1.25rem, 4vw, 3rem);
  --ease: cubic-bezier(.2, .7, .2, 1);
}
html { color-scheme: light; -webkit-text-size-adjust: 100%; }
body { position: relative; background: var(--bg); color: var(--fg); font: 400 var(--size-text)/1.6 var(--font-sans); -webkit-font-smoothing: antialiased; overflow-x: hidden; }
a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 4px; text-decoration-color: var(--line-strong); }
a:hover { text-decoration-color: var(--orange); }
:focus-visible { outline: 2px solid var(--fg); outline-offset: 3px; }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }   /* following a jump link ("Let's talk" goes down to Contact) glides instead of snapping; anyone who has asked their system for less motion keeps the snap. The browser's own wheel and touch scrolling is left alone */
img { display: block; max-width: 100%; }
.wrap { position: relative; max-width: 1344px; margin-inline: auto; padding-inline: var(--pad); }

/* the wash: a whisper of warm paper with an angled beam, gone before the work starts */
.wash { position: absolute; z-index: 0; left: 0; right: 0; top: 0; height: 900px; pointer-events: none; overflow: hidden;
  background:
    radial-gradient(58% 70% at 10% 0%, #f4f0ed 0%, transparent 72%),
    radial-gradient(52% 64% at 92% 6%, #f3f1ed 0%, transparent 74%),
    radial-gradient(46% 58% at 4% 88%, #f5f0ee 0%, transparent 72%),
    linear-gradient(180deg, #faf8f7 0%, #fff 100%);
  -webkit-mask-image: linear-gradient(180deg, #000 50%, transparent 100%); mask-image: linear-gradient(180deg, #000 50%, transparent 100%); }
.wash::after { content: ""; position: absolute; inset: 0; background: #fff; opacity: .6; clip-path: polygon(80% 0, 100% 0, 100% 100%, 90% 100%); }

/* header: the name, four plain words, one action */
.top { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding-block: 28px; }
.brand { font-family: var(--font-display); font-size: var(--size-text); font-weight: 700; letter-spacing: -.02em; text-decoration: none; white-space: nowrap; }   /* his name as the page's mark: the headline's own font ("Creative Technologist" is set in it), bold like the title, one step larger (18 px) than the 14 px links beside it so it reads as a mark and not as a fifth link; the tracking is eased from the title's -.04em to -.02em because at this size the title's tight setting would close the letters up */
.top nav { display: flex; align-items: center; gap: clamp(1.25rem, 3vw, 2.25rem); font-size: var(--size-small); }
.top nav a { color: var(--muted); text-decoration: none; white-space: nowrap; }
.top nav a:hover { color: var(--fg); }
.top nav .talk { color: var(--fg); background: rgba(255, 255, 255, .7); border: 1px solid var(--line-strong); padding: 9px 16px; border-radius: var(--r-press); font-weight: 500; transition: border-color .2s, background-color .2s; }
.top nav .talk:hover { border-color: var(--fg); background: #fff; }

/* the hero: his photo, then the title, the sentence, a short story, and whether he is available */
.hero { position: relative; display: grid; grid-template-columns: minmax(0, var(--label-col)) minmax(0, 1fr); column-gap: var(--label-gap); align-items: start; padding-block: clamp(1rem, 3vw, 2.5rem) clamp(3.25rem, 5vw, 3.75rem); }
.intro { grid-column: 2; grid-row: 1; align-self: stretch; margin-top: var(--cap-gap); padding-left: calc(var(--photo-w) + var(--hero-gap) - var(--label-col) - var(--label-gap)); display: flex; flex-direction: column; justify-content: center; }   /* the same box as the photo, with the title, story and availability line centered in its height; the copy starts one hero gutter after the portrait's right edge, so the two read as one unit */
.photo { position: relative; grid-column: 1 / -1; grid-row: 1; width: var(--photo-w); aspect-ratio: 4 / 5; margin-top: var(--cap-gap); border-radius: var(--r-surface); overflow: hidden; background: #ececea; }   /* a portrait on the page margin, level with the name and the labels; the copy sits over the second work card */
.photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 34% 0%; }   /* a 4:5 portrait window on the 4:3 original: the full height, centered on his face, both elbows kept */
.hero h1 { max-width: 8em; font: 700 var(--size-display)/1.02 var(--font-display); letter-spacing: -.04em; }   /* two lines, Creative over Technologist */
.hero h1 .g { background: var(--gradient); -webkit-background-clip: text; background-clip: text; color: transparent; padding-right: .04em; }
.statement { max-width: 44ch; font-size: var(--size-statement); line-height: 1.3; letter-spacing: -.015em; color: var(--fg-2); }   /* a section's one sentence at the claim's size: it leads whatever the section shows */
.statement.credo { max-width: 21.8em; text-wrap: wrap; }   /* his credo, Latest work's subhead, in quote marks: the first line runs to "“Good design ... of people than" (measured 20.97 em with the opening mark; one more word is 22.67 em, so 21.8 em sits in the middle of the window), so the second line is "the moment demands.”" and the break does not split "the moment". The opening mark sits on the content line, the way the lead reference's does: no hanging mark (see .quote.lead blockquote) */
.statement.claim { max-width: 24.5em; text-wrap: wrap; }   /* what he does, Expertise's subhead: the first line is the problem and runs to "...hard to explain" (measured 23.6 em; one more word is 25.5 em), so the second line is the result, "into systems people can actually use." The old 44ch measure ended the first line on "into", which cut the verb from its object. */
.bio { max-width: 38rem; text-wrap: balance; margin-top: 3rem; color: var(--muted); line-height: 1.7; }
.status { margin-top: 2.5rem; color: var(--fg); font-size: var(--size-small); font-weight: 500; line-height: 1.5; }

/* every other section: a label on the left, the content on the right, a hairline above */
.row { position: relative; display: grid; grid-template-columns: minmax(0, var(--label-col)) minmax(0, 1fr); column-gap: var(--label-gap); align-items: baseline; padding-block: clamp(3.5rem, 7.5vw, 6rem); border-top: 1px solid var(--line); }   /* a label's baseline sits on the baseline of the first line of its content */
#work { padding-block: clamp(4.5rem, 10vw, 8rem); }   /* the work is the heaviest section and gets the most air */
.row-tight { padding-block: clamp(3rem, 6.5vw, 5rem); }   /* a lone post and three short quotes need less */
.row > h1, .row > h2 { font-size: var(--size-small); font-weight: 600; line-height: 1; }   /* the label sits at the left margin; its content starts one gutter after the longest label, so a title and its content read as one */
.row:has(.post-img, .cases:first-child, .jobs) > :is(h1, h2) { align-self: start; margin-top: -.14em; }   /* a row that opens on a picture (the blog post, a row of work cards) has no text baseline to share, so the top of its label's capitals lines up with the top of the picture (measured: 2 px of line box above the capital) */
@supports (text-box: trim-start cap alphabetic) {   /* where the browser can trim a line box to its capitals, the label's top IS the top of its capitals, so it meets the picture's top whatever font the visitor has (the -.14em above was measured for Geist; Grant, 2026-11-04: the top of the Ectonomic tile should start on the line where the E of Experience starts) */
  .row:has(.make.jobs) > :is(h1, h2) { margin-top: 0; text-box: trim-start cap alphabetic; }   /* the Resume page's Experience only: the home page is committed and its labels keep the offset they were approved with */
}
.dot { display: inline-block; width: 7px; height: 7px; margin-right: 12px; border-radius: 50%; vertical-align: .08em; background: var(--d1); }   /* a round dot before each section heading, one color each, five steps of the one orange */
.d1 { background: var(--d1); } .d2 { background: var(--d2); } .d3 { background: var(--d3); } .d4 { background: var(--d4); } .d5 { background: var(--d5); }

/* work: the product on a panel of warm paper, then a name and one line */
.cases { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(4rem, 8vw, 6.5rem) var(--gap-col); margin-top: clamp(2.5rem, 4vw, 3.5rem); }   /* the credo, then four cards that start on the labels' line and fill the content column, two across */
.case { display: block; color: inherit; text-decoration: none; }
.art { position: relative; aspect-ratio: 4 / 3; border-radius: var(--r-surface); overflow: hidden; isolation: isolate; display: grid; place-items: center; background: var(--paper); box-shadow: inset 0 0 0 1px rgba(24, 24, 27, .05); }   /* every panel is the same paper: the .art-ecto, .art-prompt, .art-cg and .art-tcr classes on a card say which project it is and no longer color it */
.art > * { transition: translate .7s var(--ease); }
.case:hover .art > *, .case:focus-visible .art > * { translate: 0 -6px; }
.art-prompt img { width: 90%; height: auto; }
.win { width: 84%; height: auto; border-radius: var(--r-screen); box-shadow: 0 0 0 1px rgba(24, 24, 27, .1), 0 26px 50px -28px rgba(24, 24, 27, .5), 0 10px 20px -14px rgba(24, 24, 27, .25); }
.covers { position: relative; width: 74%; aspect-ratio: 1.6; }
.covers img { position: absolute; top: 50%; left: 50%; width: 54%; height: auto; border-radius: 2px; box-shadow: 0 0 0 1px rgba(24, 24, 27, .1), 0 26px 44px -26px rgba(24, 24, 27, .55); }
.covers img:nth-child(1) { translate: -98% -50%; rotate: -7deg; }
.covers img:nth-child(2) { translate: -2% -50%; rotate: 7deg; }
.covers img:nth-child(3) { translate: -50% -54%; z-index: 1; }
.case:hover .covers img:nth-child(1) { translate: -98% -53%; }
.case:hover .covers img:nth-child(2) { translate: -2% -53%; }
.case:hover .covers img:nth-child(3) { translate: -50% -57%; }
.cap { margin-top: 28px; }
.case h3 { font: 700 var(--size-statement)/1.15 var(--font-display); letter-spacing: -.03em; }
.case:hover h3 { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 5px; text-decoration-color: var(--orange); }
.case p { margin-top: .6rem; color: var(--muted); }
.meta { font: 400 var(--size-small)/1.4 var(--font-mono); letter-spacing: 0; color: var(--subtle); }
.case h3 .meta { margin-left: .85rem; vertical-align: .3em; text-decoration: none; }

/* expertise: a name at 28 px and its line at 18 px muted, on one ruled row */
.make { list-style: none; margin-top: clamp(2.5rem, 4vw, 3.5rem); }   /* under the claim, the way the cards sit under the credo */
.make li { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--gap-col); row-gap: 0; align-items: baseline; padding-block: 2rem; border-top: 1px solid var(--line-soft); }
.make li:first-child { border-top: 0; padding-top: 0; }
.make b { font: 600 var(--size-statement)/1.2 var(--font-display); letter-spacing: -.025em; }
.make li > span { color: var(--muted); text-wrap: balance; }   /* the name sits over the first work card's column, the line starts where the second work card starts */

.make .head b { display: block; }   /* the resume's jobs and school are Expertise rows with the role and the dates under the name (the name and its lines share one cell, so they stay together) */
.make .head .role { display: block; margin-top: .35rem; color: var(--fg-2); text-wrap: balance; }
.make.jobs li > span { text-wrap: pretty; }   /* a job's paragraph runs past the six lines that `balance` can even out, so it keeps its last line from being a single word another way */
.make .head .meta { display: block; margin-top: .5rem; text-wrap: balance; }   /* beside a tile the line is narrow: "Tallahassee, FL (Remote)" breaks into two even lines, never leaves "(Remote)" alone */
/* the resume's places (2026-11-03): each place is shown the way LinkedIn lists a job (Grant: "square marks like on LinkedIn"), by the company's own square picture, backdrop and all (Grant: "real backdrops for these brand marks"),
   edge to edge in a 72 px tile beside its name; the work related to the place sits under its paragraph. The tile has the reference headshot's size and 12 px corners, and a hairline over the picture so a white backdrop keeps its edge on the page.
   A picture is never cropped, stretched, recolored or multiplied onto the paper: it keeps the company's own colors (the build refuses a picture that is not square or not opaque). A place with two names has two tiles side by side. */
.make .head { display: flex; flex-direction: column; align-items: flex-start; gap: 2.5rem; }   /* the tile above the name, on its left edge (Grant, 2026-11-03: "stack the logo images above the names"), 40 px clear of it on a desktop (Grant, 2026-11-04: "more gutter between the profile image and the name of the company"); beside the name, in one column, the gap is 16 px, and 12 px on a phone */
.make .place { min-width: 0; }
.marks { flex: none; display: flex; gap: .5rem; }
.mark { flex: none; position: relative; overflow: hidden; width: 4.5rem; aspect-ratio: 1; border-radius: var(--r-surface); background: var(--paper); }   /* the paper shows only while the picture loads */
.mark img { display: block; width: 100%; height: 100%; }
.mark::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: inset 0 0 0 1px rgba(24, 24, 27, .08); }   /* the hairline is drawn over the picture, where an inset shadow on the tile itself would sit under it */
.make.jobs { margin-top: 0; }   /* the list starts at the top of its row, so the section label can sit on the top of the first tile, the way a post's label sits on the top of its picture */
.make.jobs .place b, .make.edu .place b { font: 700 var(--size-text)/1.3 var(--font-display); letter-spacing: -.015em; }   /* the names of the places he worked and studied, at the text size and bold, one step under the Expertise names they are drawn from (Grant, 2026-11-03: "the headings for vocation need to be reduced"); only the Resume page's lists, so the home page's Expertise names keep the statement size */
.make.jobs .place b { text-wrap: balance; }   /* a long name ("Wicked Armor / True Geek Media") breaks into two even lines, never a lone word */
.make.jobs li { grid-template-rows: auto 1fr; align-items: start; }   /* a job starts at the top of both columns: the tile and the paragraph begin on one line, and the related work sits right under the paragraph */
.make.jobs .head { grid-row: 1 / span 2; }
.make.jobs li > span { grid-column: 2; grid-row: 1; }
.related { grid-column: 2; grid-row: 2; align-self: start; margin-top: 1.5rem; font-size: var(--size-small); line-height: 1.5; }   /* the label on its own line, the links in one row under it (two rows when they do not fit), never a link left alone beside the label */
.rel-label { display: block; color: var(--subtle); }
.rel-links { display: flex; flex-wrap: wrap; gap: .25rem 1.5rem; margin-top: .4rem; }
.related a { font-weight: 500; }
/* the Profile section (2026-11-03). Grant: "a collage or something", "in the profile section", "crop some photos so its more focused on me, or be editorial", "I want the profile copy to sit well against the collage", the brief for the photos
   ("my character and facility to work with others", "that I have fun and enjoy my work", "a snapshot of my career"), "I don't like how the copy sits in the photos", and "DESIGN IT. Don't just do it."
   The design: an editorial spread, in the page's own grid. The copy is the resume's words split at the end of its first sentence: that sentence as the statement (28 px at the full width, cut into phrases so that a line ends only between two of them), the rest as text (18 px).
   Where the column is 1088 px wide or more (a viewport of about 1340 px and up) the statement is the top of the left column (4 of 12), beside the photos, and under it the rest of the copy and the photos a contact sheet on the right (8 of 12), the columns a whole gutter apart; the copy's top is level with the sheet's top, and the key at the bottom of the copy column
   (each place and its years, from the resume, in the metadata face) is level with the sheet's bottom, so copy and photos share both edges. The sheet is three strips of three, each strip one height and the strips within a few pixels
   of each other: a strip is built from its photos' own proportions (flex-grow = width over height), so nothing is cropped by the page and every strip fills the sheet's width. The photos are in the order he set
   (collage.json), and the key lists the places they come from, oldest first. Narrower, the copy sits above the sheet (the statement across the top, the rest of the copy and the key side by side under it while there is room); at a viewport of 700 px and narrower the photos are two plain columns. Gaps between photos are half the page's gutter. */
.profile { container-type: inline-size; }
.pf { --g: calc(var(--gap-col) / 2); }
.pf .prose { max-width: none; }
.pf-lede { font-size: var(--size-statement); line-height: 1.3; letter-spacing: -.015em; color: var(--fg); max-width: 27em; text-wrap: balance; }   /* 27 em, not the credo's 21: wide enough for his three lines (collage.json), the longest being 21 em; a line may end only where a phrase ends */
.pf-lede .ln, .pf-lede .ph { display: inline-block; vertical-align: top; }   /* the statement is cut into phrases (collage.json) and a line may end only between two of them, so "across", "and" or "strategy," never hang at the end of a line (Grant, 2026-11-03: "fix the line dropoffs"). An inline-block is as wide as its text unless the column is narrower than the phrase, so nothing overflows on a small screen. vertical-align: top keeps the paragraph's first baseline on its first line (an inline-block's own baseline is its last line, which put the section label a line too low). */
.pf .pf-body { margin-top: 1.5rem; max-width: 40rem; text-wrap: pretty; }
.pf-key { list-style: none; margin-top: 2rem; display: grid; gap: .1rem; }
.pf-key li { display: grid; grid-template-columns: 15ch minmax(0, 1fr); column-gap: 1rem; }
.pf-sheet { margin-top: 2.5rem; }
.pf-strip { display: flex; gap: var(--g); }
.pf-strip + .pf-strip { margin-top: var(--g); }
.pf figure { flex: var(--ar) 1 0; min-width: 0; margin: 0; }
.pf img { display: block; width: 100%; height: auto; border-radius: var(--r-surface); background: var(--paper); }
@media (hover: hover) and (prefers-reduced-motion: no-preference) {   /* a little bump when the pointer is on a photo (Grant, 2026-11-03): it grows a twentieth and rises over its neighbors; no motion for anyone who has asked for less, and none on a touch screen */
  .pf figure { position: relative; transition: transform .25s var(--ease); }
  .pf figure:hover { transform: scale(1.05); z-index: 1; }
}
@container (min-width: 700px) {   /* beside the photos from 700 px of column up (Grant, 2026-11-04: the statement belongs beside the collage at every width, not over it): the statement is the top of the left column, level with the sheet's top, in his phrases one to a line; under it the rest of the copy and the key. From 700 to 1087 px the copy takes 5 of 12 and the photos 7, so the longest phrase still fits its line; the copy may run longer than the sheet there. */
  .pf { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); grid-template-rows: auto auto 1fr; column-gap: var(--gap-col); align-items: start;
    grid-template-areas: "lede sheet" "body sheet" "key sheet"; }
  .pf-copy, .pf-copy .prose { display: contents; }
  .pf-lede { grid-area: lede; margin-bottom: 1.75rem; }
  .pf .pf-body { grid-area: body; margin-top: 0; }
  .pf-key { grid-area: key; margin-top: 1.5rem; }
  .pf-sheet { grid-area: sheet; margin-top: 0; }
}
@container (min-width: 1088px) {   /* the spread: the copy takes 4 of 12 and is no taller than the sheet, so the key stands level with the sheet's bottom (a viewport of about 1340 px and up) */
  .pf { grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); }
  .pf-key { align-self: end; }
}
@media (max-width: 700px) {   /* a phone: the photos as two plain columns, each whole and never bigger than half its pixel width */
  .pf-sheet { columns: 2; column-gap: var(--g); margin-top: 2rem; }
  .pf-strip { display: contents; }
  .pf figure { break-inside: avoid; margin: 0 0 var(--g); max-width: calc(var(--nw) * .5px); }
  .pf-key li { grid-template-columns: 14ch minmax(0, 1fr); }
}
.skills { display: grid; color: var(--fg-2); }   /* the resume's skills in three groups, each a row on the same two column lines as the jobs: the group's name on the left, its skills on the right (2026-11-04, from the writing advice Grant approved) */
.skill-group { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--gap-col); padding-block: 1.25rem; border-top: 1px solid var(--line-soft); }
.skill-group:first-child { padding-top: 0; border-top: 0; }
.skill-group h3 { font: 600 var(--size-text)/1.4 var(--font-display); letter-spacing: -.01em; color: var(--fg); }
.skill-group ul { list-style: none; display: grid; gap: .5rem; }
.act-row .meta { white-space: nowrap; }

/* journal: titles and dates on ruled rows, then one link */
.post { display: grid; grid-template-columns: clamp(14rem, 24vw, 21rem) minmax(0, 1fr); column-gap: var(--gap-col); align-items: start; color: inherit; text-decoration: none; }   /* a post is one unit: the image, then its date and title beside it, the pair centered on each other. Its left edge is the work grid's left edge and its right edge the grid's right edge. */
.post-text { align-self: center; }   /* centered on the picture when it is shorter, and the row grows with it when it is taller, so the picture's top stays the row's top */
.post-img { aspect-ratio: 3 / 2; border-radius: var(--r-surface); overflow: hidden; display: grid; place-items: center; background: var(--paper); box-shadow: inset 0 0 0 1px rgba(24, 24, 27, .05); }   /* on the same paper as the work panels */   /* the post's featured image: always 3:2, up to 336 px wide, a little over half a work card */
.post-img img { width: 84%; height: auto; border-radius: var(--r-screen); box-shadow: 0 0 0 1px rgba(24, 24, 27, .1), 0 18px 34px -22px rgba(24, 24, 27, .5); transition: transform .7s var(--ease); }
.post:hover .post-img img, .post:focus-visible .post-img img { transform: scale(1.03); }
.post-text :is(h2, h3) { margin-top: .6rem; font: 700 var(--size-statement)/1.15 var(--font-display); letter-spacing: -.03em; text-wrap: balance; }   /* no lone word on the last line */
.post-text p { margin-top: .75rem; max-width: 62ch; color: var(--muted); text-wrap: pretty; }   /* the lead-in: one or two lines, so the pair stays no taller than its image */
.post:hover :is(h2, h3) { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 5px; text-decoration-color: var(--orange); }
.more { display: inline-block; margin-top: 2.5rem; font-size: var(--size-small); font-weight: 600; }

/* references: one lead at the claim's size, then two across on the work cards' two columns at body size; each person sits directly under their words, small */
.quotes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--gap-col); row-gap: 4rem; }
.quote.lead { grid-column: 1 / -1; padding-bottom: 4rem; border-bottom: 1px solid var(--line-soft); }   /* the lead gets a hairline under it, like the rows of Expertise */
.quote blockquote { max-width: min(38ch, 80%); color: var(--fg-2); text-wrap: balance; }   /* each supporting quote stops well short of the next column, so there is air between the two */
.quote.lead blockquote { max-width: 40ch; font-size: var(--size-statement); line-height: 1.3; letter-spacing: -.015em; color: var(--fg); text-wrap: balance; }   /* the lead at the claim's size, at most about 40 characters across, lines evened out so none ends short; no hanging mark, so it starts on the content line like everything else */
.quote figcaption { display: flex; align-items: center; gap: 1.25rem; margin-top: 1.5rem; }   /* the headshot directly under the quote, and the name and title centered on it: level with its middle whether the caption runs to one line or three */
.quote.lead figcaption { margin-top: 1.75rem; }
.ref-pic { flex: none; display: grid; place-items: center; width: 4.5rem; aspect-ratio: 1; border-radius: var(--r-surface); overflow: hidden; object-fit: cover; box-shadow: inset 0 0 0 1px rgba(24, 24, 27, .05); font: 700 var(--size-small)/1 var(--font-display); letter-spacing: 0; color: var(--fg-2); background: var(--paper); }   /* their headshot, a 72 px square with the corners of his own portrait and the work panels (12 px); a reference without a photo would get a tile with their initials on the same paper */
.ref-pic img { width: 100%; height: 100%; object-fit: cover; mix-blend-mode: multiply; }   /* the photo fills the tile and is multiplied onto the paper, so a white background turns to paper and the tile keeps a soft edge on the page (the tile's own 5 percent hairline shows through the light parts), with no border drawn over the face; dark photos are unchanged to the eye */
.who { display: grid; }
.who b { font-size: var(--size-small); font-weight: 600; line-height: 1.5; }
.who span { color: var(--subtle); font-size: var(--size-small); line-height: 1.5; text-wrap: balance; }
.refs-link { margin-top: 3.5rem; }

/* contact: the invitation, then the one action with the plain facts beside it on one line, then the place; nothing hangs off by itself */
.ask { max-width: 34ch; font-size: var(--size-statement); line-height: 1.32; letter-spacing: -.015em; }
.avail { margin-top: 1.5rem; color: var(--fg-2); text-wrap: balance; }   /* the Resume page's line on whether he takes projects, under the statement and over the button (Grant, 2026-11-04: "Open to projects") */
.act-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 1rem 2.5rem; margin-top: 2.5rem; }   /* the button and the links share one baseline */
.act { display: inline-block; padding: 15px 28px; border-radius: var(--r-press); background: var(--gradient); color: #fff; font-size: var(--size-small); font-weight: 600; letter-spacing: .01em; text-decoration: none; white-space: nowrap; transition: translate .3s var(--ease), filter .3s; }
.act:hover { filter: brightness(1.07); translate: 0 -2px; }
.details { display: flex; flex-wrap: wrap; gap: .5rem 2rem; color: var(--muted); font-size: var(--size-small); }

footer { position: relative; display: flex; flex-wrap: wrap; gap: .5rem 2rem; border-top: 1px solid var(--line); padding-block: 2.5rem 4rem; color: var(--subtle); font-size: var(--size-small); }
footer .where { margin-left: auto; text-align: right; }   /* where he is, at the right end of the footer line; on a phone, where the line wraps, it stays on the right */

/* ---- inner pages (2026-10-30): the Work index, a case study, the Blog index and a post. Drawn from the home page's own parts: no new type size, no new corner, no new color. ---- */
.top nav a[aria-current] { color: var(--fg); }   /* the page you are on */

/* the Work page and a case study's "Next" row use the home page's own work cards (.cases, .case, .art): the same picture, name and line, nothing drawn twice */
.cases:first-child { margin-top: 0; }   /* a row that opens on its cards has no sentence above them to make room for */
.cases.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); row-gap: clamp(3rem, 5vw, 4.5rem); }   /* the Work page lays the complete list out three across, so it can grow by the row; below 1200 px a card would be too narrow for its own name, so it goes back to two */
@media (max-width: 1199px) { .cases.cols-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* a project's or a post's opening: its name at the title size, one line under it, and the plain facts, all on the content line */
.lede { display: grid; grid-template-columns: minmax(0, var(--label-col)) minmax(0, 1fr); column-gap: var(--label-gap); padding-block: clamp(1rem, 3vw, 2.5rem) clamp(3.5rem, 6vw, 5rem); }
.lede > div { grid-column: 2; margin-top: var(--cap-gap); }
.lede h1 { max-width: 14em; font: 700 var(--size-display)/1.02 var(--font-display); letter-spacing: -.04em; text-wrap: balance; }
.lede .statement { margin-top: 1.5rem; text-wrap: balance; }   /* the sentence style, directly under the name, its lines evened out */
.stage { display: grid; grid-template-columns: minmax(0, var(--label-col)) minmax(0, 1fr); column-gap: var(--label-gap); padding-block: clamp(3.5rem, 6vw, 5rem) clamp(3.5rem, 7.5vw, 6rem); border-top: 1px solid var(--line); }   /* what a page leads with after its hero (the cards, the post): under the same hairline that separates the sections, on the content line, with no label of its own */
.stage > div { grid-column: 2; }
@media (min-width: 641px) { .lede .statement.credo { text-wrap: wrap; } }   /* the credo keeps the break chosen for it ("...of people than / the moment demands."), here as on the home page; on a phone its lines are evened out, as there */
.lede > div > .meta { display: block; margin-top: 1.5rem; }   /* a post's date line under its title (a card's state label, inside the cards, is not this) */
.facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.75rem var(--gap-col); margin-top: clamp(2.5rem, 4vw, 3.5rem); }   /* two columns on the page's two column lines: role and status down the first, stack and year down the second */
.facts dt { font-size: var(--size-small); line-height: 1.5; color: var(--subtle); }
.facts dd { color: var(--fg); text-wrap: balance; }
.lede > div > .art { margin-top: clamp(2.5rem, 4vw, 3.5rem); }   /* the project's own picture under its facts; not the panels of cards that sit inside an opening */
.art.wide { aspect-ratio: 16 / 10; }   /* the project's own picture, across the whole content column */
.art.wide.band { aspect-ratio: 11 / 5; }   /* a wide, short picture (the keyboard's screen) gets a panel to match */
.art.wide .win, .art.wide > img { width: 72%; }
.art.wide.art-prompt > img { width: 72%; }

/* the body of a case study: one part per row, in plain 18 px text */
.prose { max-width: 40rem; color: var(--fg-2); line-height: 1.7; text-wrap: pretty; }
.prose p + p { margin-top: 1.25rem; }
.shots { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2.5rem var(--gap-col); margin-top: 3rem; }   /* the screens: two across on the work cards' two columns, each with its caption */
.prose + .shots { margin-top: 3rem; }
.shot figcaption { margin-top: 1rem; max-width: 34ch; font-size: var(--size-small); line-height: 1.5; color: var(--muted); text-wrap: pretty; }
.ruled { list-style: none; max-width: 40rem; }
.ruled li { padding-block: 1.25rem; border-top: 1px solid var(--line-soft); color: var(--fg-2); line-height: 1.7; text-wrap: pretty; }
.ruled li:first-child { border-top: 0; padding-top: 0; }
.ruled li:last-child { padding-bottom: 0; }
.fine { max-width: 40rem; margin-top: 2rem; font-size: var(--size-small); line-height: 1.6; color: var(--subtle); text-wrap: pretty; }

/* a post: the same column, the text at a reading measure, the code in mono */
.article { display: grid; grid-template-columns: minmax(0, var(--label-col)) minmax(0, 1fr); column-gap: var(--label-gap); padding-block: clamp(3.5rem, 6vw, 5rem) clamp(3.5rem, 7.5vw, 6rem); border-top: 1px solid var(--line); }   /* the post starts under the hairline that closes its hero */
.article > div { grid-column: 2; }
.article .art { margin-bottom: 3.5rem; }
.article .prose + .prose { margin-top: 1.25rem; }   /* one paragraph per block, so the spacing between paragraphs is set here */
.article h2 { max-width: 40rem; margin: 3rem 0 1rem; font: 600 var(--size-statement)/1.2 var(--font-display); letter-spacing: -.025em; text-wrap: balance; }
.article pre { max-width: 40rem; margin-block: 2rem; padding: 1.25rem 1.5rem; overflow-x: auto; background: color-mix(in srgb, var(--fg) 3%, var(--bg)); box-shadow: inset 0 0 0 1px var(--line); border-radius: var(--r-screen); font: 400 var(--size-small)/1.7 var(--font-mono); color: var(--fg-2); tab-size: 4; }
.article code { font-family: var(--font-mono); font-size: inherit; background: color-mix(in srgb, var(--fg) 5%, var(--bg)); padding: .05em .3em; border-radius: var(--r-tag); }
.article pre code { padding: 0; background: none; font-size: inherit; }
.article pre .c { color: var(--subtle); }
.article pre mark { background: color-mix(in srgb, var(--orange) 16%, var(--bg)); color: inherit; border-radius: var(--r-tag); padding: .05em .15em; }
.article .more { margin-top: 3.5rem; }

@media (prefers-reduced-motion: reduce) { .art > *, .act, .covers img, .post-img img { transition: none; } .case:hover .art > *, .case:hover .covers img, .act:hover { translate: none; } .post:hover .post-img img { transform: none; } }
@media (max-width: 1099px) {
  .statement { text-wrap: balance; }
  .intro { padding-left: 0; grid-row: 1; }
  .photo { grid-column: 2; grid-row: 2; width: 100%; max-width: 340px; margin-top: 2.5rem; }
}
@media (max-width: 860px) {
  .quotes { grid-template-columns: minmax(0, 1fr); }
  .quote:not(.lead) blockquote { max-width: none; }
  .row, .hero, .lede, .article, .stage { grid-template-columns: minmax(0, 1fr); row-gap: 1.5rem; }
  .intro, .photo, .lede > div, .article > div, .stage > div { grid-column: 1; }
  .photo { grid-row: 2; max-width: 340px; margin-top: 0; }
  .intro { margin-top: 0; }
  .make li { grid-template-columns: minmax(0, 1fr); gap: .4rem; }
  .make.jobs li { grid-template-rows: none; row-gap: 1rem; }
  .make.jobs .head, .make.jobs li > span, .related { grid-column: 1; grid-row: auto; }
  .make .head { flex-direction: row; align-items: flex-start; gap: 1rem; }   /* in one column the tile goes to the left of the name and the role and dates (Grant, 2026-11-04: "in mobile mode the company logo needs to be on the left of the company name and my title"); the stacking above the name stays where the name has a column of its own, from 861 px of screen up */
  .related { margin-top: 0; }
  .wash::after { clip-path: polygon(70% 0, 100% 0, 100% 100%, 88% 100%); }
}
@media (max-width: 640px) {
  .statement.credo, .statement.claim { text-wrap: balance; }   /* on a phone the line is narrower than its measure, so even the lines out */
  .cases, .cases.cols-3, .shots, .facts, .skill-group { grid-template-columns: minmax(0, 1fr); }
  .skill-group h3 { margin-bottom: .6rem; }
  .top nav a:not(.talk) { display: none; }
  .post { grid-template-columns: minmax(0, 1fr); row-gap: 1.5rem; align-items: start; }
}
@media (max-width: 480px) {   /* a phone: the tile is 56 px and the gap 12 px, so the role beside it keeps 237 px at 360 px of screen: "Brand Consulting, Marketing" needs 233 on one line, and "Design & Communications Specialist" would otherwise leave "Specialist" alone */
  .make .head { gap: .75rem; }
  .make .mark { width: 3.5rem; border-radius: var(--r-press); }
}
