/* MultiMichel — homepage styles. All visual values come from tokens.css. */

*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;background:var(--rail)}
/* The ground is white. The paper canvas is an inner column inset by
   --rail-width on both sides, so white shows down each edge for the full
   height of the page. The contrast break is deliberate. */
body{
  background:var(--paper);
  border-left:var(--rail-width) solid var(--rail);
  border-right:var(--rail-width) solid var(--rail);
  color:var(--slate);font-family:var(--font-grotesk);font-variation-settings:normal;font-weight:400;line-height:1.5;-webkit-font-smoothing:antialiased;
  /* Declared ONCE, here, because font-variation-settings inherits: it reaches
     every serif surface on every page without a per-component repeat, and the
     faces that have no such axes (Agrandir, Hanken Grotesk) ignore it. The
     EDITORIAL recipe is the default — most of the serif on this site is row
     titles — and the two display surfaces override it. See tokens.css for the
     three recipes and why opsz is pinned per role rather than left on `auto`. */
  /* No axes here. <body> is set in Hanken, so declaring Fraunces axes on it
     only pushed them onto every element on the page through inheritance —
     which is what made Agrandir report "WONK 0" in devtools. Each serif rule
     now carries its own recipe instead. */
  font-variation-settings:normal;
}
/* height:auto is not optional once images carry intrinsic width/height
   attributes: without it the HTML height attribute dictates layout height and
   a 2000×1333 photo renders 1333px tall. With it, the attributes do only the
   job they are there for — reserving the right box so nothing shifts on load —
   while CSS keeps control of the rendered size. */
img{max-width:100%;height:auto;display:block}
a{color:inherit;text-decoration:none}
/* Emphasis inside running copy. --weight-emphasis, not a bare 700: see
   tokens.css for why Hanken needs more than 700 to match what Founders Bold
   did against its regular. Headings and labels set their own weight and are
   deliberately not routed through this. */
b,strong{font-weight:var(--weight-emphasis)}

/* Left-biased canvas: anchored to the page edge, not centered with auto margins.
   Inside the paper column, text is inset a further 16px so it lands at
   --content-start (72 rail + 16 = 88). The orange blocks pull back that 16px
   to sit flush on the rail/paper seam and break the text alignment on purpose. */
.wrap,header.site{max-width:var(--canvas-reference);padding-left:var(--content-inset);padding-right:var(--content-inset)}

/* Skip link — first focusable element on every page. Off-screen until focused,
   then lands on the marker rail like everything else. */
.skiplink{
  position:absolute;left:-9999px;top:0;z-index:100;
  font-family:var(--font-label);font-variation-settings:normal;font-weight:900;font-size:var(--type-label-size);
  letter-spacing:var(--type-label-tracking);text-transform:uppercase;
  background:var(--slate);color:var(--paper);padding:var(--space-16);
}
.skiplink:focus{left:0}
/* Kept in the document for structure and search, taken out of the picture.
   Not display:none — that would remove it from the accessibility tree too. */
.visually-hidden{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;
}

/* ---------- masthead & nav ----------
   The brand square is a real <button> (mobile menu control) and the
   wordmark a separate link, so the square can own its own tap target
   without nesting interactive elements. */
header.site{display:flex;align-items:flex-end;justify-content:space-between;padding-bottom:var(--space-32)}
.brand{display:flex;align-items:center}
.brand-link{text-box:trim-both cap alphabetic;font-family:var(--font-label);font-variation-settings:normal;font-weight:900;font-size:var(--type-masthead-size);line-height:var(--type-masthead-leading);letter-spacing:var(--type-masthead-tracking);color:var(--masthead);text-transform:uppercase}
/* The header square rides the SAME marker rail as every .sechead .tab:
   both resolve to --block-inset from the page edge, so the square hangs
   out of the content column exactly as the section markers do. Margin is
   declared once here — an earlier `margin:0` in this block was silently
   cancelling the rail offset and pulling the square inside the column. */
.brand-menu{
  position:relative;
  width:var(--block-header-w);height:var(--block-header-h);background:var(--orange);
  border:0;padding:0;flex:none;cursor:default;
  display:inline-flex;align-items:center;justify-content:center;
  margin:0 var(--space-16) 0 calc(var(--block-inset) - var(--content-start));
}
.brand-menu:disabled{opacity:1}
.menu-icon{display:none}
nav.main{display:flex;gap:var(--nav-gap);padding-bottom:var(--space-8)}
nav.main a{position:relative;font-family:var(--font-label);font-variation-settings:normal;font-weight:900;font-size:var(--type-label-size);line-height:var(--type-label-leading);letter-spacing:var(--type-label-tracking);color:var(--dim)}

nav.main a[aria-current="page"]{color:var(--ink)}
nav.main a[aria-current="page"]::before{
  content:"";position:absolute;left:50%;transform:translateX(-50%);
  /* Sits on the top edge of the page, level with the brand square, rather than
     floating just above the word. Derived from tokens rather than measured: the
     link sits (header square − its own height − the nav's bottom padding) below
     the header top, so undoing exactly that lands the marker at 0. */
  top:calc(100% + var(--space-8) - var(--block-header-h));
  width:var(--nav-current-marker);height:var(--nav-current-marker);
  background:var(--orange);
}

/* The mobile nav panel exists at every width but only ever paints inside
   the ≤900px block below — this base rule just keeps it out of layout
   above that breakpoint. */
.mobile-nav{display:none}

/* ---------- hero — large intentional opening whitespace ---------- */
.hero{padding-top:var(--space-192)}
/* The display role is the page's single statement voice, keyed to the JOB and
   not to the tag: an <h2> carrying the same weight in the page (about-v2's story
   headline) must be the same style. Every existing use is an <h1>, so the widened
   selector changes nothing that already renders. */
.display{
  font-family:var(--font-serif);font-variation-settings:var(--serif-row);
  font-weight:var(--serif-display-weight);
  font-variation-settings:var(--serif-display);
  /* Clamp floor and slope move with --type-hero-size on the same serif scale,
     so the headline holds its optical size at EVERY width — not only at the
     1496px reference. 52/10.2vw/148 → 49/9.6vw/140. */
  font-size:clamp(49px,9.6vw,var(--type-hero-size));
  letter-spacing:var(--type-hero-tracking);
  /* .96 → 1.02 sets the SAME absolute leading as before: the ratio grew by
     exactly as much as the font-size shrank, so a three-line headline still
     occupies the block it always did. */
  line-height:1.02;
  color:var(--slate);
}
@media(max-width:700px){.display{letter-spacing:-.03em}}

/* ---------- running copy ----------
   ONE reading style, shared. The homepage intro and the About prose are the
   same typography and differ only in measure — the intro runs wider because it
   opens the page. There is deliberately no .lede type declaration here: a
   second set of values is what made it a tier, and the tier is gone.
   Emphasis is the shared --weight-emphasis; the 700 that used to sit on .lede b
   was a local override that quietly undercut it. */
.lede,.prose{
  font-family:var(--font-grotesk);font-variation-settings:normal;
  font-weight:400;
  font-size:var(--type-body-size);
  line-height:var(--type-body-leading-ratio);
  color:var(--slate);
}
.lede{max-width:var(--intro-max-width);margin-top:var(--space-32)}
.lede p+p{margin-top:var(--gap-lede-paragraph)}


/* ---------- photo — 1400×788 reference (16:9) ---------- */
figure.full{margin:var(--space-72) 0 0}
figure.full img,figure.full video{width:100%;height:auto;aspect-ratio:1400/788;object-fit:cover}
/* Opt-in, per photo, at EVERY width. The 16:9 reference frame is a crop, and on
   a 3:2 source it takes ~8% off the top and bottom — enough to cut the Figma
   logo and the "SYDNEY" lockup out of the Maker Collective shot. .natural means
   this photograph's whole frame carries meaning, so it runs at its own ratio.
   It used to say that only below 900px, which left the crop in place on the
   desktop composition the class exists to protect. Still opt-in: the
   case-study covers are portrait and are meant to be cropped. */
figure.full.natural img,figure.full.natural video{aspect-ratio:auto;height:auto}
figcaption,.cap{font-family:var(--font-grotesk);font-variation-settings:normal;font-weight:500;font-size:var(--type-caption-size);letter-spacing:.4px;color:var(--muted);text-transform:uppercase;margin-top:var(--space-16)}

/* ---------- quote ----------
   The block owns the vertical rhythm; the quote itself no longer carries the
   margin, so the source line sits with it rather than floating off. */
.quote-block{margin:var(--space-144) 0;text-align:center}
.quote{
  text-align:center;
  font-family:var(--font-serif);font-variation-settings:var(--serif-row);
  font-weight:var(--serif-display-weight);
  font-variation-settings:var(--serif-display);
  /* Ceiling holds cap-height parity with the original 48px Family setting, so
     the desktop quote keeps its presence. Only the floor moved: the statement
     recipe sets wider than the old one, and at 28px the phone lost the two-line
     composition — "of" orphaned onto its own line and the highlight came apart. */
  font-size:clamp(24px,4.3vw,var(--type-quote-size));
  line-height:1.104;
  letter-spacing:var(--type-quote-tracking);
  color:var(--ink-quote);
  max-width:15em;
  margin:0 auto;
  padding:0 var(--space-16);
}
/* Attribution stays deliberately small — it is a footnote to the quote, not a
   second headline competing with it. */
.quote-source{
  margin:var(--space-32) auto 0;
  max-width:var(--measure-form);
  padding:0 var(--space-16);
  font-family:var(--font-grotesk);font-variation-settings:normal;
  font-size:var(--type-row-meta-size);
  line-height:1.5;
  color:var(--muted);
  /* this is a sentence, not a caption — opt out of the uppercase figcaption rule */
  text-transform:none;
  letter-spacing:normal;
}
.quote-source cite{font-style:normal;font-weight:500;color:var(--oak)}
.quote-source a{white-space:nowrap;color:var(--oak)}

/* z-index:0 gives .hl its own stacking context, so the brush at z-index:-1
   sits behind the words but still in front of the paper fill on <body>.
   Without it the brush paints underneath the body background and vanishes. */
/* ---------- yellow marker (highlighter) ----------
   Keep the original painted SVG behind the complete phrase. The image uses
   its natural aspect ratio, with a modest overshoot beyond both text edges. */
.hl{position:relative;z-index:0;white-space:nowrap;display:inline-block}
/* The marks sit lighter than the words they enclose — punctuation should not
   weigh as much as the sentence. Opening and closing take the same treatment;
   they are one role, and splitting them would be exactly the kind of local
   difference this system does not keep. */
.quote-mark{position:relative;z-index:1;font-weight:var(--serif-quote-mark-weight);font-variation-settings:var(--serif-quote-mark)}
/* The closing ” belongs to the highlighted phrase — on a wide viewport it was
   breaking onto a line of its own. Marker styling is untouched. */
.quote-tail{white-space:nowrap}
.hl-mark{
  position:absolute;
  left:-5%;
  top:-8%;
  width:110%;
  /* Height is keyed to the LINE BOX, not to the brush's own width. Left on the
     SVG's intrinsic aspect it scaled with the phrase, so it only covered the
     words at roughly the homepage phrase's length — a short highlight ("hard.")
     collapsed to a thin stripe floating above the text. 155% is the ratio the
     homepage brush already had at every width, so that quote is unmoved. */
  height:155%;
  max-width:none;
  /* fill, not cover: stretching keeps the tapered hand-drawn ends, which is the
     whole point of the mark. cover would crop it into a hard rectangle. */
  object-fit:fill;
  z-index:-1;
  pointer-events:none;
}

/* ---------- section markers ----------
   The marker is taken OUT of flow so it never pushes the label. That keeps
   every piece of text — section labels and the row titles beneath them — on
   one column at --content-start, while the square sits out in the white rail
   (8 → 72), flush against the rail/paper seam. */
/* The row is as tall as the marker, so the square and the label are centred
   against the SAME box rather than the label's much shorter line box. The
   square centres by the box model (top/bottom 0 + auto margins), not by a
   transform, and neither element carries a positional nudge. */
.sechead{
  position:relative;display:flex;align-items:center;
  min-height:var(--block-marker);
  margin-bottom:var(--gap-label-to-rows);
}
.sechead .tab{
  position:absolute;
  left:calc(var(--block-inset) - var(--content-start));
  top:0;bottom:0;margin-block:auto;
  width:var(--block-marker);height:var(--block-marker);
  background:var(--orange);
}
/* Copy-permalink affordance, added by nav.js. Sits exactly inside the
   marker's own box (inset:0) so the square's size/position never changes —
   invisible until hover/focus, same square either way. */
.tab-copy{
  position:absolute;inset:0;
  display:flex;align-items:center;justify-content:center;
  background:transparent;border:0;padding:0;margin:0;cursor:pointer;
  color:#fff;opacity:0;transition:opacity .15s ease;
}
.sechead .tab:hover .tab-copy,
.tab-copy:focus-visible{opacity:1}
.tab-copy:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.tab-copy-status{
  position:absolute;left:50%;top:calc(100% + 8px);transform:translateX(-50%);
  white-space:nowrap;
  font-family:var(--font-grotesk);font-variation-settings:normal;font-size:11px;font-weight:600;
  background:var(--ink);color:var(--paper);
  padding:4px 8px;border-radius:3px;
  opacity:0;pointer-events:none;transition:opacity .15s ease;
}
.sechead .tab.is-copied .tab-copy-status{opacity:1}
/* Page-level eyebrow — the same label treatment as a section name
   (.sechead h2), used above an H1 rather than above a list. Tied to the
   heading it introduces, never a floating line of body text. */
.eyebrow{font-family:var(--font-label);font-variation-settings:normal;font-weight:900;font-size:var(--type-label-size);line-height:var(--type-label-leading);letter-spacing:var(--type-label-tracking);color:var(--muted);text-transform:uppercase;margin-bottom:var(--space-16)}
/* Agrandir's caps fill its ascent (cap 720 / asc 750), so the visible
   letterforms sit 0.11em above the centre of their own line box — geometric
   centring alone leaves the label reading high against the square. The padding
   is exactly twice that, because centring halves whatever is added. Metric
   compensation applied once in the shared component, not a per-section nudge. */
/* Label and action share ONE type definition — same face, size, leading,
   tracking and colour — so the pair sits on a single optical line. Only the
   action's placement differs.

   text-box trims the line box down to cap-height/baseline, so the box the
   browser centres IS the visible word. That removes the need for any
   cap-height offset: align-items:center now centres what you actually see.
   Browsers without text-box fall back to the untrimmed box, which is the
   behaviour this had before — never worse. */
.sechead h1,.sechead h2,.sechead .more{
  font-family:var(--font-label);font-variation-settings:normal;
  font-weight:900;
  font-size:var(--type-label-size);
  line-height:1;
  text-box:trim-both cap alphabetic;
  /* Symmetric: the underline needs room below the baseline (text-box trims the
     box bottom TO the baseline), and equal padding top and bottom keeps the
     caps dead-centre against the marker. */
  padding:0.5rem 0;
  letter-spacing:var(--type-label-tracking);
  color:var(--muted);
  text-transform:uppercase;
}
.sechead .more{margin-left:auto}


/* ---------- editorial rows — ONE grammar, everywhere ----------
   Speaking, Writing, Archive, Around-the-internet and Media all render the
   same four-part row, stacked at every width:

       KICKER      optional — status (UPCOMING) or category (WRITING, TALK…)
       Title ↗     the click target; the arrow belongs to it
       metadata    where / when / source
       description optional

   There is no second arrow column and no side-by-side variant. Anything that
   belongs to the row belongs in this stack. */
a.row,div.row{
  display:grid;
  grid-template-columns:minmax(0, 1fr) auto;
  column-gap:var(--space-16);
  /* Title, metadata and arrow share ONE baseline at every width. Without this
     the arrow floated to the top of the row while the publication sat on the
     baseline beside it — the two never lined up. */
  align-items:baseline;
  padding:var(--space-16);margin:0 calc(-1 * var(--space-16));
  border-bottom:1px solid var(--rule-light);transition:background .15s;
}
/* Kicker, metadata and description run the full width; only the title shares
   its line with the action arrow. */
.row .kicker-line,.row .m,.row .sub{grid-column:1 / -1}
.row .t{grid-column:1}
.row .go{grid-column:2}
a.row{cursor:pointer}
/* Hover + keyboard focus share ONE treatment: the row inverts to the slate
   ground so #FF6C00 has a dark backdrop to sit on. Orange on paper is 2.6:1;
   orange on slate clears AA comfortably, so the brand colour is untouched and
   the contrast is solved by what sits behind it. */
a.row:hover,a.row:focus-visible{background:var(--slate);outline:none}
a.row:focus-visible{outline:2px solid var(--orange);outline-offset:-2px}
.rowlist .row:first-child{border-top:1px solid var(--rule-light)}

.row .t{font-family:var(--font-serif);font-variation-settings:var(--serif-row);font-weight:var(--serif-row-weight);font-size:var(--type-row-title-size);line-height:var(--type-row-title-leading);color:var(--ink)}
a.row:hover .t,a.row:focus-visible .t{color:var(--orange)}
.row .m{font-family:var(--font-grotesk);font-variation-settings:normal;font-weight:var(--type-row-meta-weight);font-size:var(--type-row-meta-size);line-height:var(--type-row-meta-leading);letter-spacing:var(--type-row-meta-tracking);color:var(--oak);margin-top:var(--gap-row-title-to-meta)}
.row .sub{font-family:var(--font-grotesk);font-variation-settings:normal;font-weight:400;font-size:var(--type-sub-size);line-height:1.4;color:var(--muted);margin-top:var(--space-8);max-width:640px}
/* Metadata and description flip to paper so they stay readable on the slate. */
a.row:hover .m,a.row:focus-visible .m,
a.row:hover .sub,a.row:focus-visible .sub{color:var(--paper)}
a.row:hover .cat,a.row:focus-visible .cat{background:transparent;color:var(--paper);box-shadow:inset 0 0 0 1px currentColor}

/* ---------- action arrow — ONE mark, ONE treatment, every context ----------
   Position: .go takes the right edge of the title line, in its own grid cell.
   Because it is a sibling of the title rather than part of its text, a long
   title wraps on its own measure and never reflows around the arrow — and the
   arrow can never be stranded alone on a line. ↗ leaves the site, → stays.

   Compact navigation labels (ALL WRITING →, Next: … →, ORIGINAL PUBLICATION ↗)
   keep their arrow INLINE — they are labels, not editorial rows — but they are
   no longer bare characters loose in the sentence. They carry .arrow: the same
   rule, the same size, the same text presentation, differing only in taking the
   label's own colour, because they sit inside the words.

   ONE DESIGNED MARK, AT ONE SIZE, EVERYWHERE. Agrandir draws it — see
   --font-arrow in tokens.css. That is what keeps ↗ off the emoji font: the
   first family in the stack owns the glyph, so nothing ever falls through to
   the platform. font-variant-emoji and the U+FE0E in the markup stay as belt
   and braces for the case where Agrandir has not loaded yet.

   Sizing this mark by its font-size is a trap: Agrandir draws a small arrow on
   a large em, so the SAME font-size that suits a label produces a mark half the
   size of the one the original design showed. --type-arrow-size is set from the
   rendered glyph height instead, and no context may override it — an inline
   label arrow and a row's action mark are the same mark. */
/* Agrandir draws it, at --type-arrow-size, in both variants — see tokens.css.
   The two variants differ only in how they meet the line they sit on:
     .go    a grid/flex item with its own cell, baseline-aligned by the parent.
            Keeps line-height 1; at 18.8px it is shorter than the title's line
            box, so it cannot change a row's height.
     .arrow inline INSIDE a label, where an 18.8px inline box would grow a 13px
            label's line box and push the section marker out of alignment.
            line-height 0 takes it out of the line-box calculation entirely, so
            the mark paints at full size and the label's rhythm is untouched. */
.arrow,.go{
  font-family:var(--font-arrow);font-variation-settings:normal;
  font-weight:900;
  font-size:var(--type-arrow-size);
  font-variant-emoji:text;
  /* The mark belongs to the word it follows: never break between them, and
     never let the mark start a line on its own. */
  white-space:nowrap;
}
/* The .065em in both variants is ONE number for ONE reason: Agrandir draws ↗
   sitting 0.065em clear of the baseline, where the original design's arrow sat
   on it. Without this the mark floats a pixel high against the metadata beside
   it. .go is a grid/flex item so vertical-align does not reach it and it takes
   the offset positionally; .arrow is inline and takes it as vertical-align.
   Same compensation, expressed the only way each context allows. */
.go{color:var(--oak);line-height:1;position:relative;top:.065em}
/* Inline inside a label, so it takes that label's colour and its hover with it. */
.arrow{color:inherit;line-height:0;vertical-align:-.065em;margin-left:.3em}
/* A mark that LEADS its label ("← Back to Home") rather than trailing it. Not a
   second treatment: same face, same size, same nudge — the gap just moves to
   the other side of the glyph. */
.arrow.is-before{margin-left:0;margin-right:.3em}
a.row:hover .go,a.row:focus-visible .go{color:var(--orange)}

/* ---------- kickers: status and category ----------
   Both sit on their own line above the title. They are deliberately NOT the
   same weight: UPCOMING is live, actionable status; a category is a filing
   label that exists to help scanning and must stay behind the title. */
.row .kicker-line{display:block;margin-bottom:var(--gap-label-to-title)}
.row .up,.row .cat{
  display:inline-block;
  font-family:var(--font-label);font-variation-settings:normal;font-weight:900;font-size:var(--type-chip-size);letter-spacing:var(--type-chip-tracking);
  text-transform:uppercase;
}
/* Slate on orange, not white on orange — white was 2.84:1. */
.row .up{background:var(--orange);color:var(--slate);padding:2px var(--space-8)}
.row .cat{background:var(--rule-light);color:var(--oak);padding:2px var(--space-8);border-radius:2px}

/* ---------- desktop composition ----------
   Same markup, same source order, different arrangement: above the mobile
   breakpoint the row becomes an editorial two-column rhythm — title on the
   left, source/date in the secondary right-hand column, sharing a baseline.
   Kicker and description still span the full width, so the reading order is
   unchanged; only the middle band splits.
   This is one grammar composed two ways, NOT two markups — nothing here
   reintroduces the old has-badge / has-ext / rowbody variants. */
@media(min-width:901px){
  a.row,div.row{
    display:grid;
    /* minmax(0,1fr) not 1fr: a bare 1fr floors at the title's min-content
       width, so a long unbroken title would refuse to wrap and shove the
       metadata column off the row. */
    grid-template-columns:minmax(0, 1fr) auto auto;
    /* The markup order is title -> arrow -> metadata (columns 1, 3, 2) because
       that order is what mobile needs. Default sparse auto-placement never
       moves backwards, so metadata could not reclaim column 2 and dropped onto
       a row of its own. Dense lets it fill the hole, so the row is one line. */
    grid-auto-flow:dense;
    column-gap:var(--space-32);
    align-items:baseline;
  }
  .row .kicker-line{grid-column:1 / -1}
  .row .t{grid-column:1}
  /* Back into its own column: no stacking margin, right-aligned, and kept on
     one line so it reads as a column rather than a second sentence. */
  .row .m{grid-column:2;margin-top:0;text-align:right;white-space:nowrap}
  .row .go{grid-column:3}
  .row .sub{grid-column:1 / -1}
}


/* ---------- hand-drawn link underline (pen) ----------
   ONE stroke, reused by every underlined link on the site. This is the PEN
   gesture: thin, slightly unsteady, drawn once. It is deliberately not the
   highlighter — the yellow marker behind "understanding people" is a separate,
   heavier gesture (.hl-mark).
   Painted as a background rather than a border so it never changes the line
   box, and so hover can thicken it without shifting a single glyph. */
.penline,
.lede a,
.prose a,
.aside-note-link,
.quote-source a{
  background-image:url("img/pen-underline.svg");
  background-repeat:no-repeat;
  background-position:0 100%;
  background-size:100% var(--pen-thickness);
  padding-bottom:.14em;
  transition:background-size .15s ease;
}
/* On by default rather than hover-only: on touch there is no hover, and the
   underline is the only thing marking these as links. Hover/focus thickens
   the same stroke instead of introducing a different affordance. */
.penline:hover,.penline:focus-visible,
.lede a:hover,.lede a:focus-visible,
.prose a:hover,.prose a:focus-visible,
.aside-note-link:hover,.aside-note-link:focus-visible,
.quote-source a:hover,.quote-source a:focus-visible{background-size:100% var(--pen-thickness-hover)}
.penline:focus-visible,
.lede a:focus-visible,
.prose a:focus-visible,
.aside-note-link:focus-visible,
.quote-source a:focus-visible{outline:2px solid var(--ink);outline-offset:3px}

/* ---------- hover / focus on a paper ground ----------
   Orange never becomes the text colour here: #FF6C00 on #F8F5F2 is 2.6:1, and
   a hover that REDUCES legibility is the wrong trade. The text stays dark and
   the orange arrives as the pen stroke underneath — the accent moves from the
   glyphs to the rule. Links that already show the stroke simply thicken it
   (see .penline); these three reveal it from zero. */
nav.main a,.sechead .more{
  background-image:url("img/pen-underline.svg");
  background-repeat:no-repeat;
  background-position:0 100%;
  background-size:100% 0;
  transition:background-size .15s ease;
}
/* Clearance is per-context and must NOT be set here: .sechead .more already
   gets symmetric padding from the label rule above, and re-declaring
   padding-bottom in this later rule silently overrode it — which is what put
   the stroke back through the word. nav links are untrimmed, so they get their
   own. */
nav.main a{padding-bottom:.5rem}
nav.main a:hover,nav.main a:focus-visible,
.sechead .more:hover,.sechead .more:focus-visible{background-size:100% var(--pen-thickness)}
/* Outline is :focus-visible ONLY — never on hover, never at rest. */
nav.main a:focus-visible,.sechead .more:focus-visible{outline:2px solid var(--ink);outline-offset:3px}

.aside-note{margin-top:var(--space-32);font-family:var(--font-grotesk);font-variation-settings:normal;font-size:var(--type-aside-size);color:var(--dim)}

/* ---------- portrait figure ----------
   A vertical portrait must not be forced into the 16:9 landscape frame that
   figure.full uses. Constrained width, left-aligned with the text column —
   the canvas is left-oriented, so a centred portrait would fight it. */
/* A vertical portrait must not be forced into the 16:9 landscape frame that
   figure.full uses — on the 1066×1600 About photograph that crop showed 37% of
   the image. Constrained width, left-aligned with the text column, whole frame.
   This rule was written for that photograph and sat unused while the figure
   carried .full; the class is the fix, not a new crop. */
figure.portrait{margin:var(--space-72) 0 0;max-width:var(--portrait-w)}
figure.portrait img{width:100%;height:auto;aspect-ratio:auto;object-fit:contain}

/* ---------- prose (about) ---------- */
.prose{max-width:var(--measure)}
.prose p+p{margin-top:var(--space-16)}


/* ---------- form (contact) ----------
   Rules, not boxes: an input is a line you write on, matching the editorial
   rows rather than importing a SaaS form kit. */
.form{max-width:var(--measure-form)}
.field{margin-bottom:var(--space-32)}
.field label{
  display:block;margin-bottom:var(--space-8);
  font-family:var(--font-label);font-variation-settings:normal;font-weight:900;font-size:var(--type-label-size);
  letter-spacing:var(--type-label-tracking);text-transform:uppercase;color:var(--muted);
}
.field input,.field textarea,.field select{
  width:100%;display:block;
  background:transparent;border:0;border-bottom:1.5px solid var(--rule-strong);
  padding:var(--space-8) 0;
  font-family:var(--font-grotesk);font-variation-settings:normal;font-size:var(--type-field-size);color:var(--ink);
  border-radius:0;-webkit-appearance:none;appearance:none;
}
.field textarea{resize:vertical;line-height:1.5}
.field select{cursor:pointer;background-image:none}
.field input:focus,.field textarea:focus,.field select:focus{outline:0;border-bottom-color:var(--orange)}
.field input[aria-invalid="true"],.field textarea[aria-invalid="true"]{border-bottom-color:var(--orange)}
.err{margin-top:var(--space-8);font-family:var(--font-grotesk);font-variation-settings:normal;font-size:var(--type-row-meta-size);color:var(--orange)}

.submit{
  display:inline-block;border:0;cursor:pointer;
  /* slate on orange: white was 2.84:1 */
  background:var(--orange);color:var(--slate);
  font-family:var(--font-label);font-variation-settings:normal;font-weight:900;font-size:var(--type-label-size);
  letter-spacing:var(--type-label-tracking);text-transform:uppercase;
  padding:var(--space-16) var(--space-32);border-radius:0;
  transition:opacity .15s;
}
.submit:hover{opacity:.85}
.submit:disabled{opacity:.4;cursor:default}
.form-status{margin-top:var(--space-16);font-family:var(--font-grotesk);font-variation-settings:normal;font-size:var(--type-row-meta-size);color:var(--dim);min-height:1.2em}
.form-status.is-error{color:var(--orange)}
.form-status.is-ok{color:var(--oak)}

/* about / contact section rhythm */
/* About's major narrative sections all sit on the same boundary gap. The
   section-heading component itself is untouched — this is the space BETWEEN
   sections, not inside them. */
#how-i-think,#at-figma,#route,#besides,#about-links{padding-top:var(--gap-section-major)}
#besides,#about-links{padding-bottom:var(--space-8)}
#contact-form{padding-top:var(--space-72)}
#elsewhere-contact{padding-top:var(--space-96);padding-bottom:var(--space-8)}

/* ---------- per-section rhythm — every value from the 8pt scale.
   Deliberately uneven: WORK gets the biggest run-up, MEDIA the tightest,
   so the cadence modulates rather than repeating. ---------- */
#writing{padding-top:var(--space-72)}
#speaking{padding-top:var(--gap-writing-to-speaking)}
#work{padding-top:var(--space-144)}
#media{padding-top:var(--space-72)}
#elsewhere{padding-top:var(--space-72);padding-bottom:var(--space-8)}

/* ---------- fixed-frame image zoom hover ---------- */
.zoomframe{overflow:hidden;display:block;background:var(--white)}
.zoomframe img{transition:transform var(--zoom-duration) var(--zoom-ease);transform:scale(1);will-change:transform}
.zoomframe:hover img{transform:scale(var(--zoom-scale))}

/* ---------- work — two compact visual case studies, side by side.
   680×425 reference ratio (8:5), work-gap is a Figma value (24px). ---------- */
/* Two across wherever two fit — same content-width logic as .media-grid.
   Below 560px a single case-study cover would otherwise run full-bleed huge. */
.work-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--work-gap);margin-top:0}
@media(max-width:560px){.work-grid{grid-template-columns:1fr;gap:var(--space-48)}}
.work-card{display:block}
.work-card .zoomframe{aspect-ratio:680/425}
.work-card .zoomframe img{width:100%;height:100%;object-fit:cover}
/* CASE STUDY label uses the label face, all-caps — matching node 13:688. */
/* Work and Media are ONE anatomy — cover, label, title — so both of the stack's
   relationships are declared here once, for both cards, and neither card owns a
   spacing value of its own. Only the title SIZE differs, because Work and Media
   are genuinely different hierarchy. */
.work-label,.media-label{
  font-family:var(--font-label);font-variation-settings:normal;font-weight:900;font-size:var(--type-label-size);
  letter-spacing:var(--type-label-tracking);color:var(--orange);text-transform:uppercase;
  margin-top:var(--gap-cover-to-label);
}
.work-card-title,.media-card-title{
  font-family:var(--font-serif);font-variation-settings:var(--serif-row);font-weight:var(--serif-card-weight);
  font-variation-settings:var(--serif-card);
  color:var(--ink);
  margin-top:var(--gap-label-to-title);
}
/* block + fit-content, never inline-block: an inline-block sits on the parent's
   text baseline, so its line box inherits the parent's strut and leaves a few
   stray pixels under the title that Media — a flex title — never had. That
   asymmetry is the whole reason the same 8px measured differently on the two
   cards. width:fit-content keeps the shrink-to-fit box the brush needs. */
.work-card-title{position:relative;display:block;width:fit-content;font-size:var(--type-work-title-size);line-height:var(--type-work-title-leading)}
/* Hover: the same hand-drawn brush as the quote, in white, swiped behind the
   title. The title colour does not change — the brush is the whole gesture. */
.work-card-title .brush{
  position:absolute;
  left:-3%;top:40%;
  width:106%;height:85%;
  /* Same escape hatch .hl-mark already carries, for the same reason: the global
     img{max-width:100%} was capping this at the title's width, so the declared
     106%/-3% overhang only ever painted on the left and the stroke stopped 3%
     short on the right. Two brush marks, one treatment — they have to repeat. */
  max-width:none;
  object-fit:fill;
  z-index:-1;
  pointer-events:none;
  opacity:0;
  transition:opacity var(--zoom-duration) var(--zoom-ease);
}
.work-card:hover .work-card-title .brush{opacity:1}

/* ---------- media — visual, real imagery, no embeds (outside Figma-node scope) ---------- */
/* Three across wherever three fit. Below ~560px the content column can no
   longer give each card a usable thumbnail, so it drops to one. Keyed to the
   width the cards actually need, not to a device class. */
.media-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-32)}
@media(max-width:560px){.media-grid{grid-template-columns:1fr}}
.media-card{display:block}
.media-card .zoomframe{aspect-ratio:1/1}
.media-card .zoomframe img{width:100%;height:100%;object-fit:cover}

/* Same anatomy as an editorial row: title left, action mark at the far right,
   in its own element so a wrapping title never reflows around it. */
.media-card-title{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--space-16);
  font-size:var(--type-media-title-size);line-height:var(--type-media-title-leading);
}
.media-card-title .go{flex:none}
.media-card:hover .go{color:var(--orange)}
.media-card:hover .media-card-title{color:var(--orange)}

/* ---------- footer (outside Figma-node scope) ---------- */
/* ---------- footer social row ----------
   Icon-only to keep the row compact. Bootstrap Icons (MIT) — one system, so
   every mark shares the same 16px grid and solid weight; no brand colours, the
   icons take the footer's own tan via currentColor. Each is a real link with
   an aria-label, and the 44px tap target is padding, not visible size, so the
   optical rhythm stays tight. */
footer.site .social{display:flex;align-items:center;gap:var(--space-8);flex-wrap:wrap}
.social-icon{
  display:inline-flex;align-items:center;justify-content:center;
  width:var(--space-32);height:var(--space-32);
  color:var(--muted);
  border-radius:2px;
  transition:color .15s ease,background-color .15s ease;
}
.social-icon svg{display:block}
.social-icon:hover,.social-icon:focus-visible{color:var(--orange);background:var(--rule-light)}
.social-icon:focus-visible{outline:2px solid var(--ink);outline-offset:2px}

footer.site{border-top:1px solid var(--rule-strong);margin-top:var(--space-96);padding:var(--space-32) var(--content-inset) var(--space-48) var(--content-inset);display:flex;flex-wrap:wrap;gap:var(--space-16);justify-content:space-between;align-items:center}
.fineprint{
  font-family:var(--font-grotesk);font-variation-settings:normal;font-weight:var(--type-fineprint-weight);
  font-size:var(--type-fineprint-size);line-height:var(--type-fineprint-leading);
  letter-spacing:var(--type-fineprint-tracking);color:var(--muted);
}


@media(prefers-reduced-motion:reduce){
  .zoomframe img{transition:none}
  html{scroll-behavior:auto}
}

/* ---------- mobile — authored, not shrunk ----------
   The desktop redlines are desktop-only. On a 390px screen a 72px rail plus a
   96px text column eats 28% of the width before a word is set, so the whole
   left-edge system is re-proportioned on the same 8pt grid:

       0  → 8    white
       8  → 40   orange block (32×32) — still straddles the seam
       40 → 48   8px gap
       48        text column
       24        rail/paper seam

   That returns ~326px of content on a 390px screen instead of 206px.
   The hero stays deliberately enormous; only the frame gets tighter. */
@media(max-width:900px){
  :root{
    --rail-width:var(--space-24-m);
    --content-start:var(--space-48);
    --block-inset:var(--space-8);
    --block-marker:var(--space-32);
    --block-header-w:var(--space-32);
    --block-header-h:var(--space-32);
    --nav-gap:var(--space-16);
    /* Masthead steps down, and its leading steps with it so the line box
       stays on the 8pt grid: 24px, not 19 × 1.5 = 28.5px (off-grid AND
       sub-pixel). */
    --type-masthead-size:19px;
    --type-masthead-leading:24px;
    --intro-max-width:100%;
  }
  header.site{flex-direction:row;align-items:center;justify-content:flex-start;gap:var(--space-16);padding-bottom:var(--space-24-m);position:relative;z-index:50}
  .brand{position:relative;z-index:41}

  /* Nav row is replaced by the hamburger + full-panel menu below — this is
     the actual fix for ABOUT wrapping onto its own line: there's no longer
     a horizontal row of five labels fighting for a ~326px column. */
  nav.main{display:none}
  .brand-menu{cursor:pointer}
  .menu-icon{position:relative;width:14px;height:10px;display:block}
  .menu-icon span{position:absolute;left:0;right:0;height:2px;background:#fff;transition:transform .2s ease}
  .menu-icon span:first-child{top:0}
  .menu-icon span:last-child{bottom:0}
  .brand-menu[aria-expanded="true"] .menu-icon span:first-child{transform:translateY(4px) rotate(45deg)}
  .brand-menu[aria-expanded="true"] .menu-icon span:last-child{transform:translateY(-4px) rotate(-45deg)}
  /* Touch target ≥44px without enlarging the visible square: an invisible
     ::before extends the hit area, the square itself stays 32×32. */
  .brand-menu::before{
    content:"";position:absolute;top:50%;left:50%;
    width:44px;height:44px;transform:translate(-50%,-50%);
  }

  /* Same technique for the section-marker copy-link control — visible
     square stays whatever size --block-marker is at this breakpoint. */
  .tab-copy::before{
    content:"";position:absolute;top:50%;left:50%;
    width:44px;height:44px;transform:translate(-50%,-50%);
  }

  .mobile-nav.is-open{
    display:flex;flex-direction:column;
    position:fixed;inset:0;z-index:40;
    background:var(--paper);
    padding:calc(var(--space-72) + var(--space-24-m)) var(--space-48) var(--space-48);
    gap:var(--space-24-m);
    overflow-y:auto;
  }
  .mobile-nav a{
    position:relative;
    font-family:var(--font-label);font-variation-settings:normal;font-weight:900;
    font-size:clamp(28px,9vw,40px);
    letter-spacing:var(--type-label-tracking);
    color:var(--dim);text-transform:uppercase;
  }
  .mobile-nav a:active{color:var(--orange)}
  /* Same current-page marker as the desktop nav. A stacked list cannot take it
     above without colliding with the item before, so it sits in the left margin
     — the rail the section markers already use. */
  .mobile-nav a[aria-current="page"]{color:var(--ink)}
  .mobile-nav a[aria-current="page"]::before{
    content:"";position:absolute;top:50%;transform:translateY(-50%);
    left:calc(-1 * var(--space-24-m));
    width:var(--nav-current-marker-mobile);height:var(--nav-current-marker-mobile);
    background:var(--orange);
  }

  /* vertical rhythm compresses; the hero keeps its scale */
  .hero{padding-top:var(--space-72)}
  figure.full{margin-top:var(--space-48)}

  .quote{margin:var(--space-72) auto;max-width:none}
  #writing{padding-top:var(--space-48)}
  #speaking{padding-top:var(--space-48)}
  #work{padding-top:var(--space-72)}
  #media{padding-top:var(--space-48)}
  #elsewhere{padding-top:var(--space-48)}
  #contact-form{padding-top:var(--space-48)}
  #elsewhere-contact{padding-top:var(--space-72)}
  .form,.prose{max-width:100%}
  /* Same single token, stepped down for the phone. */
  :root{--gap-section-major:var(--space-128)}

  /* Same stack as desktop — only the frame tightens. */
  a.row,div.row{padding:var(--space-16) var(--space-8);margin:0 calc(-1 * var(--space-8))}
  /* A wrapped serif title must read as one unit, not two stacked lines.
     Size is unchanged — only the leading tightens. */
  .row .t{line-height:var(--type-row-title-leading-mobile)}

  .media-grid{gap:var(--space-32)}
}
