/* kristinakaganer.com · Proof Edition (BRAND.md)
   Serif to read, sans to click, mono for proof only.
   Oxblood marks evidence and nothing else. */

/* Self-hosted type (BRAND.md). Downloaded from Google Fonts, which serves
   these three as variable fonts: one file carries every weight the site uses,
   so Libre Franklin's four weights are one file and Newsreader's two are one.
   All three are SIL Open Font License 1.1; see fonts/OFL.txt.
   Self-hosted rather than linked, so there is no third-party request on first
   paint, no swap shift, and no dependency on a service the site does not own.
   Only latin and latin-ext are held: the site is en-GB, and the unicode-range
   means a browser fetches latin-ext only if a page needs it. */
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("fonts/newsreader-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("fonts/newsreader-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Newsreader";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/newsreader-italic-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/newsreader-italic-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Libre Franklin";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/libre-franklin-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Libre Franklin";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/libre-franklin-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --paper: #F7F5F0;
  --ink: #1A1A18;
  --rule: #D8D3C8;
  --muted: #6B665E;
  --evidence: #7A1F1F;

  --serif: "Newsreader", Georgia, "Times New Roman", serif;
  --sans: "Libre Franklin", "Helvetica Neue", Arial, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, Menlo, Consolas, monospace;

  --reading: 680px;
  --measure: 30em;
  --margin-col: 312px;
  --proof-col: 312px;
  --gutter: 16px;
}

*, *::before, *::after { box-sizing: border-box; }

/* The paper goes on html as well as body, so the overscroll area does not
   flash white. color-scheme: light is the site saying it is light only
   (D-021), which also stops browser force-dark inverting the palette. */
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  color-scheme: light;
  background: var(--paper);
  color: var(--ink);
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 1.125rem;
  line-height: 1.6;
  font-optical-sizing: auto;
  overflow-wrap: break-word;
}

h1, h2, h3 {
  font-family: var(--serif);
  font-weight: 400;
  line-height: 1.2;
  margin: 0;
  text-wrap: balance;
}

p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

ul { margin: 0; padding: 0; list-style: none; }

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

:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
}

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link */
.skip {
  /* Fixed, not absolute: absolute puts it above the viewport once the
     page is scrolled, so it is off screen at the moment it takes focus. */
  position: fixed;
  left: var(--gutter);
  top: 8px;
  z-index: 10;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 16px;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
  transform: translateY(-200%);
}
.skip:focus { transform: none; outline-color: var(--ink); }

.wrap {
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 var(--gutter);
}

@media (min-width: 600px) {
  :root { --gutter: 32px; }
}

/* Anchors and keyboard focus land below the sticky stack. The offset sits
   on the targets as scroll-margin, not on the page as scroll-padding: with
   scroll-padding, tabbing onto a link inside a stuck bar made the browser
   scroll the page to "reveal" it. Below 720px the stuck part is the 50px nav
   row; from 720px the 70px masthead; from 960px on Work and Speaking the
   masthead plus the 46px "On this page" bar. Each offset adds 22px to 26px
   of breathing room. Controls inside the stuck bars need no offset. */
:root { --stack-offset: 72px; }
@media (min-width: 720px) {
  :root { --stack-offset: 96px; }
}
@media (min-width: 960px) {
  :root.has-on-page { --stack-offset: 140px; }
}
[id], a[href], button, [tabindex] { scroll-margin-top: var(--stack-offset); }
.masthead a, .back-to-top { scroll-margin-top: 0; }
@media (min-width: 960px) {
  .on-page a { scroll-margin-top: 0; }
}

/* Masthead: sticky, so the nav stays in reach (D-011). Below 720px the
   wordmark takes its own row and scrolls away; the negative top leaves only
   the nav row stuck to the top of the screen. */
.masthead {
  position: sticky;
  top: -48px;
  z-index: 5;
  background: var(--paper);
  border-bottom: 2px solid var(--ink);
}
.masthead .wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0 24px;
  padding-top: 4px;
  padding-bottom: 4px;
}
.site-name {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-family: var(--sans);
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  text-decoration: none;
}
.site-name:hover { text-decoration: underline; text-underline-offset: 6px; }

/* LinkedIn: the profile mark, beside the name and not in the nav (D-030).
   Measured at 320px the nav row has 8.3px of slack, so a sixth 44px item wraps
   it to two rows and the masthead goes 98px to 142px, which is the cost D-026
   priced and refused. Row one has 125px free at the same width, so the mark
   costs nothing at any width and the masthead height is unchanged, which is
   what the --stack-offset maths above depends on. The trade is that below
   720px row one scrolls away with the name and only the nav row stays stuck,
   so the mark is at the top of the page rather than always in reach. Contact
   is the route that has to be in reach, and it still is.
   LinkedIn's own blue, #0A66C2, and not one of the tokens above (D-032).
   Muted grey read as a dimmer shade of the name beside it rather than as a
   separate thing. It is hard coded rather than given a token because it is
   not this site's colour and must not become one: BRAND rule 4 still says
   oxblood is the one accent and it still means evidence. 5.22:1 on paper at
   rest and 9.27:1 on hover, both above the 3:1 a non-text mark needs and
   above 4.5:1 as well. */
.masthead-left { display: flex; align-items: center; gap: 0 4px; }
.nav-linkedin {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  color: #0A66C2;
}
.nav-linkedin svg { display: block; fill: currentColor; }
.nav-linkedin:hover { color: #004182; }
.nav-linkedin:focus-visible { outline-offset: -3px; }

.site-nav { flex: 1 1 100%; }
.site-nav ul {
  display: flex;
  /* The five items fit one row at normal size. They cannot at 200% text, and
     without this the row is what pushes the page sideways (WCAG 1.4.4). */
  flex-wrap: wrap;
  justify-content: space-between;
  margin: 0 -6px;
}
.site-nav a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  min-width: 44px;
  padding: 0 6px;
  font-family: var(--sans);
  font-size: 0.9375rem;
  font-weight: 500;
  white-space: nowrap;
  text-decoration: none;
}
/* The narrowest phones: the five items still fit on one row */
@media (max-width: 379px) {
  .site-nav ul { margin: 0 -4px; }
  .site-nav a { padding: 0 4px; }
}
@media (max-width: 339px) {
  .site-nav ul { margin: 0 -2px; }
  .site-nav a { padding: 0 2px; }
}
@media (min-width: 720px) {
  .masthead { top: 0; }
  .masthead .wrap { flex-wrap: nowrap; padding-top: 12px; padding-bottom: 12px; }
  .site-nav { flex: 0 1 auto; }
  .site-nav ul { justify-content: flex-end; gap: 0 4px; margin: 0 -12px 0 0; }
  .site-nav a { padding: 0 12px; font-size: 1rem; }
}
.site-nav a:focus-visible { outline-offset: -3px; }
.site-nav a:hover { text-decoration: underline; text-underline-offset: 6px; }
.site-nav a[aria-current="page"] {
  font-weight: 700;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
}

/* Contact: the one button in the masthead, ink like every button
   (BRAND rule 4). The link keeps the 44px row height, so the masthead and
   its below-720px sticky offset are unchanged. On phones the ink fill is
   36px tall inside that 44px target, so it never touches the screen edge
   when the nav row is stuck; from 720px it fills the target. The right
   margin cancels the list's negative margin so the fill meets the gutter. */
.site-nav .nav-contact {
  padding: 0 12px;
  background: linear-gradient(var(--ink), var(--ink)) center / 100% 36px no-repeat;
  color: var(--paper);
  font-weight: 600;
}
.site-nav .nav-cta { margin-right: 6px; }
.site-nav .nav-contact:hover {
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 5px;
}
/* Focus inverts the button and draws the outline inside it, so the ring
   is never clipped by the top of the screen when the row is stuck */
.site-nav .nav-contact:focus-visible { background-image: none; color: var(--ink); outline-offset: -3px; }
@media (max-width: 379px) {
  .site-nav .nav-contact { padding: 0 8px; }
  .site-nav .nav-cta { margin-right: 4px; }
}
@media (min-width: 720px) {
  .site-nav .nav-contact { padding: 0 16px; background-size: 100% 100%; border-radius: 2px; }
  .site-nav .nav-cta { margin: 0 12px 0 8px; }
}

/* Sections */
/* Sections open on a 2px ink rule, the masthead weight, so a new section
   reads differently from the 1px rules between entries (UIX-10). */
.section {
  padding: 40px 0;
  border-top: 2px solid var(--ink);
}
main > .wrap > .section:first-child { border-top: 0; }
@media (min-width: 960px) {
  .section { padding: 56px 0; }
}

/* Page grammar rule 5: a section heading wraps at the reading measure on every
   page, whichever wrapper it sits in. Without this, an h2 placed directly in
   .section runs to the full 1120px wrap while the same h2 inside .col-main
   wraps at 680px, so the same element has two measures across the site. */
.section-title {
  font-size: 1.75rem;
  margin-bottom: 20px;
  max-width: var(--reading);
}
@media (min-width: 600px) {
  .section-title { font-size: 2rem; }
}

/* Reading column with a margin column for the aside */
.with-margin {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  row-gap: 32px;
}
.col-main { min-width: 0; max-width: var(--reading); }
@media (min-width: 960px) {
  .with-margin {
    grid-template-columns: minmax(0, var(--reading)) var(--margin-col);
    column-gap: 64px;
  }
  .col-margin { padding-top: 8px; }
}

@media (min-width: 600px) {
  .prose p, .lede { font-size: 1.1875rem; }
}

/* Claim headline with portrait. The portrait follows the headline in the
   source, so on phones it sits under the claim, never above it (UX-03). */
.claim {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  max-width: none;
}
/* 700px, not 600px. Measured on the home headline: at 620 the row leaves a
   364px column and the headline sets 5 lines, the shortest 199px; at 700 it
   leaves 444px and sets 3 even lines filling 87% of the column. Below 700 the
   portrait goes under the headline instead. */
@media (min-width: 700px) {
  .claim { flex-direction: row; align-items: center; gap: 64px; }
  .claim .page-title { flex: 0 1 var(--reading); }
}
.page-title {
  font-size: 2.125rem;
  line-height: 1.15;
  letter-spacing: -0.01em;
  max-width: var(--reading);
}
@media (min-width: 600px) {
  .page-title { font-size: 2.75rem; }
}
@media (min-width: 960px) {
  .page-title { font-size: 3.25rem; }
}

/* A real portrait, small and round (BRAND rule 5) */
.portrait { display: block; flex: none; }
.portrait img {
  display: block;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  object-fit: cover;
}
@media (min-width: 600px) {
  .portrait img { width: 128px; height: 128px; }
}

/* Proof note: mono, oxblood, never in a heading */
.proof {
  font-family: var(--mono);
  font-size: 0.8125rem;
  line-height: 1.55;
  color: var(--evidence);
  border-left: 2px solid var(--evidence);
  padding: 2px 0 2px 12px;
  margin: 0;
  max-width: var(--reading);
}

/* Aside: italic, in the margin at desktop */
.aside {
  margin: 0;
  padding-top: 12px;
  border-top: 1px solid var(--ink);
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.1875rem;
  line-height: 1.45;
  max-width: var(--reading);
}

/* Contents lists: year, body, optional proof column */
.contents { border-top: 1px solid var(--ink); }
.contents > li {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 4px 24px;
  padding: 20px 0;
  border-bottom: 1px solid var(--rule);
}
.contents .year {
  margin: 0;
  font-family: var(--sans);
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.5;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  padding-top: 3px;
}
.contents .year:empty { display: none; }
.contents .body { min-width: 0; max-width: var(--reading); }
.contents .proof { margin-top: 10px; }

@media (min-width: 600px) {
  .contents > li { grid-template-columns: 8.5rem minmax(0, 1fr); }
  .contents .year:empty { display: block; }
  .contents .proof { grid-column: 2; }
}

/* Roles and awards: three short fields per row */
.fields > li { font-size: 1.0625rem; }
.fields .cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0 24px;
}
.fields .cols span:first-child { font-weight: 500; }
.fields .cols span + span { color: var(--ink); }
@media (min-width: 600px) {
  .fields .cols { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

/* Roles grouped by company: the company once, its roles stacked newest
   first, and a thin rule joining the roles so the progression reads at a
   glance. Same year, company and title styles as the awards rows. */
.orgs { --role-gap: 12px; --marker: 13px; }
.orgs > li.org { display: block; }
.org .org-name { margin: 0; font-weight: 500; }
.org .roles {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  row-gap: var(--role-gap);
  margin: 6px 0 0;
  padding: 0;
  list-style: none;
}
.org .roles > li {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  padding-left: 20px;
}
.org .role-title { margin: 0; color: var(--ink); }
/* A square marker on each role, level with its years */
.org .roles > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc(var(--marker) - 3.5px);
  width: 7px;
  height: 7px;
  background: var(--muted);
}
/* The rule runs from this role's marker down to the next one */
.org .roles > li:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 3px;
  top: var(--marker);
  bottom: calc(-1 * (var(--role-gap) + var(--marker)));
  border-left: 1px solid var(--muted);
}
/* A single role needs no marker; its years stay aligned with the others */
.org .roles > li:first-child:last-child::before { content: none; }
@media (min-width: 600px) {
  .orgs { --half: calc((var(--reading) - 24px) / 2); }
  .orgs > li.org {
    display: grid;
    grid-template-columns: 8.5rem minmax(0, var(--half)) minmax(0, var(--half));
    column-gap: 24px;
  }
  .org .org-name { grid-column: 2; grid-row: 1; }
  .org .roles {
    grid-column: 1 / -1;
    grid-row: 1;
    margin-top: 0;
    grid-template-columns: subgrid;
  }
  .org .roles > li {
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
    padding-left: 0;
  }
  .org .roles > li > .year { grid-column: 1; padding-left: 20px; }
  .org .roles > li > .role-title { grid-column: 3; }
}

/* Builds and proofed blocks: proof beside at desktop */
.proofed {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
}
.proofed .proofed-main { min-width: 0; max-width: var(--reading); }
@media (min-width: 960px) {
  .proofed {
    grid-template-columns: minmax(0, var(--reading)) var(--proof-col);
    column-gap: 64px;
  }
  .proofed .proofed-main { grid-column: 1; }
  .proofed .proof { grid-column: 2; grid-row: 1; align-self: start; margin-top: 6px; }
  .build.proofed .proof { grid-row: 1 / span 2; }
}

/* Page grammar rule 4: a margin note never lands inside the thing it annotates.
   Below 960px there is no margin column, and source order would drop the mono
   proof line between a build's claim and the paragraph that explains it. Order
   moves it after both, so it reads as a note on the build rather than an
   interruption mid thought. Only auto-placed items are reordered, so the
   explicit placement above 960px is untouched. */
@media (max-width: 959.98px) {
  .proofed .proofed-main { order: 0; }
  .proofed .proof { order: 1; }
}

.builds { border-top: 1px solid var(--ink); }
.build {
  padding: 32px 0;
  border-bottom: 1px solid var(--rule);
}
.build:last-child { border-bottom: 0; padding-bottom: 0; }
.build h3 { font-size: 1.625rem; margin-bottom: 12px; }
.build .claim-line { font-size: 1.3125rem; line-height: 1.45; margin-bottom: 0; }
.build .mechanism { font-size: 1.0625rem; }

/* Topics: a bulleted list, an ink square marker level with each title */
.topics {
  border-top: 1px solid var(--ink);
  max-width: var(--reading);
  margin: 0;
  padding: 0;
  list-style: none;
}
.topics > li {
  position: relative;
  padding: 24px 0 24px 22px;
  border-bottom: 1px solid var(--rule);
}
.topics > li:last-child { border-bottom: 0; padding-bottom: 0; }
.topics > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc(24px + 0.72em);
  width: 7px;
  height: 7px;
  background: var(--ink);
}
.topics .topic-title {
  font-size: 1.5rem;
  line-height: 1.25;
  font-weight: 500;
  margin: 0 0 8px;
}
.topics .lead { font-size: 1.1875rem; margin: 0; }

/* Pull line */
.pull {
  font-size: 1.75rem;
  line-height: 1.25;
  margin-top: 1.25em;
  max-width: var(--reading);
  text-wrap: balance;
}
@media (min-width: 600px) {
  .prose p.pull { font-size: 2.125rem; }
}

.after-title { margin-top: 24px; }
.after-contact { margin-top: 32px; }

/* Controls: sans, sentence case, ink.
   Inline by default, because every use but one sits inside a sentence. An
   inline-flex box 44px tall inside a 22.5px line box doubles the line and
   drops the baseline, which is what the "Or write to ..." line was doing on
   all four pages. The padding gives the same hit height without moving a
   glyph, and WCAG 2.5.8 exempts targets in a line of text anyway.
   box-decoration-break keeps that padding on both halves of a wrapped link;
   overflow-wrap: anywhere lets a long address break, which is what stops the
   page scrolling sideways at 200% text (body's break-word does not reduce
   min-content width, so it cannot). */
.text-link {
  font-family: var(--sans);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.35;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 5px;
  padding: 13px 0;
  overflow-wrap: anywhere;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.text-link:hover { text-decoration-thickness: 2px; }

/* A link inside reading prose (site grammar rule 4, D-027). It takes the face,
   the size and the colour of the sentence it sits in and is marked by its
   underline alone. Deliberately not .text-link, which is sans 1rem weight 600
   with 13px of vertical padding: a 44px box inside a line box of 29px at 390
   takes the clicks meant for the lines above and below it, and WCAG 2.5.8
   exempts a target in a line of text for exactly that reason. */
.prose-link {
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--muted);
  text-underline-offset: 4px;
}
.prose-link:hover {
  text-decoration-thickness: 2px;
  text-decoration-color: currentColor;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 24px 0 12px;
}
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 22px;
  border: 1px solid var(--ink);
  border-radius: 2px;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
}
.button:hover { background: var(--paper); color: var(--ink); }
/* .write-to is the quiet line under a close-block button. It had a twin,
   .hosted-note, for a line that is not a close block: Webinars used it and
   left this repo at D-031, then Speaking borrowed it for the note under the
   PDF download at D-035. D-037 cut that note, so .hosted-note has no caller
   and is gone. The class existed at all because the checks count p.write-to to
   prove the close block carries exactly one, and a second p.write-to anywhere
   on the page would fail that. Anything quiet and outside a close block needs
   its own class again for the same reason. */
.write-to {
  font-family: var(--sans);
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--muted);
  margin: 0;
}
.write-to .text-link { font-size: inherit; color: var(--ink); }

/* No footer (D-014). Room under the last section, so at the end of the page
   the floating back-to-top button sits clear of the last line */
main { padding-bottom: 96px; }

/* Floating back to top (site.js). Hidden in the markup, so it never shows
   or takes focus without JS; revealed after one screen of scrolling. It
   follows main in the source, so Tab reaches it after the page content.
   Ink with a paper ring, so it reads on paper and on the video posters. */
.back-to-top {
  position: fixed;
  right: max(16px, env(safe-area-inset-right));
  bottom: max(16px, env(safe-area-inset-bottom));
  z-index: 6;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border: 1px solid var(--ink);
  border-radius: 2px;
  background: var(--ink);
  color: var(--paper);
  box-shadow: 0 0 0 2px var(--paper);
  font-family: var(--sans);
  font-size: 1.375rem;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
}
.back-to-top[hidden] { display: none; }
.back-to-top::before { content: "\2191"; content: "\2191" / ""; }
.back-to-top:hover,
.back-to-top:focus-visible { background: var(--paper); color: var(--ink); }
.back-to-top:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; }
@media (min-width: 720px) {
  .back-to-top { right: max(24px, env(safe-area-inset-right)); bottom: max(24px, env(safe-area-inset-bottom)); }
}
/* Smooth jumps only for people who have not asked for less motion */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}


/* Home doors: Speaking, Work and About, straight under the claim (D-017) */
.doors-section { padding-top: 0; }
.doors {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}
.door {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 16px 0;
  border-bottom: 1px solid var(--rule);
}
.door:last-child { border-bottom: 0; padding-bottom: 0; }
.door-title { font-family: var(--sans); font-size: 1.125rem; font-weight: 700; line-height: 1.2; }
.door-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 44px;
  min-height: 44px;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
}
.door-link::after { content: "\2192"; content: "\2192" / ""; text-decoration: none; display: inline-block; }
.door-link:hover { text-decoration-thickness: 3px; }
/* The whole door is the link: the title link's ::before covers the card,
   and the mailto buttons sit above it so they stay separate targets. */
.door { position: relative; }
.door-link::before { content: ""; position: absolute; inset: 0; z-index: 0; }
/* Short about under the claim on home (D-015) */
.home-about { margin-top: 24px; max-width: var(--reading); }
.home-about p { margin: 0 0 8px; }
.door:hover .door-link,
.door-link:focus-visible { text-decoration-thickness: 3px; }
/* The arrow nudge is motion, so only without a reduced-motion preference */
@media (prefers-reduced-motion: no-preference) {
  .door-link::after { transition: transform 0.15s ease-out; }
  .door:hover .door-link::after,
  .door-link:focus-visible::after { transform: translateX(4px); }
}
.door-line { margin: 0; font-size: 1.0625rem; line-height: 1.45; max-width: var(--measure); }
@media (min-width: 720px) {
  .doors { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 32px; }
  .door { padding: 12px 0 0; border-bottom: 0; }
  .door + .door { border-left: 1px solid var(--rule); padding-left: 32px; margin-left: -1px; }
}

/* On this page: a row of section links under the lede */
.on-page {
  margin-top: 24px;
  padding-top: 8px;
  border-top: 1px solid var(--rule);
  font-family: var(--sans);
}
.on-page-label {
  margin: 0;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--muted);
}
.on-page ul { display: flex; flex-wrap: wrap; gap: 0 20px; }
.on-page a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  min-width: 44px;
  font-size: 1rem;
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 5px;
}
.on-page a:hover { text-decoration-thickness: 2px; }
/* The nav sits between the intro and the first section, so the intro gives
   up its bottom padding and the nav carries it as margin. */
.section.before-on-page { padding-bottom: 0; }
.on-page { margin-bottom: 40px; }
/* The section in view (site.js): a 3px ink bar under the link and no thin
   underline, so it differs by shape as well as weight. */
.on-page a[aria-current="true"] {
  text-decoration: none;
  box-shadow: inset 0 -3px 0 var(--ink);
}
/* Desktop: the row pins under the masthead (D-013). On phones it stays in
   the page flow, because a second stuck bar costs too much screen height. */
@media (min-width: 960px) {
  .on-page {
    position: sticky;
    top: 68px;
    z-index: 4;
    display: flex;
    align-items: center;
    gap: 0 28px;
    margin-bottom: 56px;
    padding-top: 0;
    background: var(--paper);
    /* No rule under the bar (D-029). It used to carry a 2px ink rule so the
       section boundary sat on the bar rather than 56px below it, which read as
       a box around the row: a 1px rule above, the links, a heavy rule below.
       The section takes its own rule back, the one every other section opens
       on, so the row is a line of links under the lede and nothing more. The
       "one rule, not two" it was written for still holds, because the two are
       56px apart and only one of them is at the boundary. */
  }
  .on-page ul { gap: 0 24px; }
}

/* Keep each proof item whole; wrap only at the separators */
.proof .nb { display: inline-block; max-width: 100%; }

/* Reading measure: no more than 80 characters per line */
.prose p, .lede, .lead, .mechanism p, p.mechanism { max-width: var(--measure); }


/* Click-to-load video (assets/video.js). A 16:9 poster that links to
   YouTube without JS, and a button that swaps in the player with it. */
.video { margin: 0; min-width: 0; }
.video-poster,
.video-frame {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  margin: 0;
  padding: 0;
  border: 0;
  border-top: 2px solid var(--ink);
  background: var(--ink);
  overflow: hidden;
}
.video-poster { cursor: pointer; color: var(--paper); font: inherit; }
.video-poster img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.video-play {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 64px;
  height: 48px;
  transform: translate(-50%, -50%);
  background: var(--ink);
  border: 1px solid var(--paper);
  border-radius: 2px;
}
.video-play::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-35%, -50%);
  border-style: solid;
  border-width: 9px 0 9px 15px;
  border-color: transparent transparent transparent var(--paper);
}
.video-poster:hover .video-play { background: var(--paper); border-color: var(--ink); }
.video-poster:hover .video-play::after { border-left-color: var(--ink); }
.video-poster:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }

/* Past talks: year, event, format and role, title, and a compact poster.
   The poster sits under the row text on phones and in its own right-hand
   column at desktop, so five videos keep the list short. */
.talks { margin: 0; padding: 0; list-style: none; }
.talks > li { row-gap: 12px; }
.talk-event {
  margin: 0;
  font-size: 1.25rem;
  line-height: 1.3;
  font-weight: 500;
}
.talk-format {
  margin: 2px 0 6px;
  font-family: var(--sans);
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--muted);
}
.talk-title { margin: 0; font-size: 1.0625rem; line-height: 1.5; }
.talk-video { margin-top: 4px; }
@media (min-width: 600px) {
  .talks .talk-video { grid-column: 2; max-width: 360px; }
}
@media (min-width: 960px) {
  .talks > li { grid-template-columns: 8.5rem minmax(0, 1fr) 280px; }
  .talks .talk-video {
    grid-column: 3;
    grid-row: 1;
    align-self: start;
    max-width: none;
    margin-top: 4px;
  }
}

/* Home fits one laptop screen (D-016): no bottom room for the floating
   button, which never shows on a page this short, and tighter section
   padding than the inner pages.
   D-034 tightened every figure here. D-016 was measured against an 800px
   viewport and home ended at 686px, which is one screen only if the window
   is a tall one. D-033's qualification sentence then took it to 747px, so a
   laptop with a bookmarks bar scrolled. The room came back out of spacing
   rather than out of the sentence, because the spacing was the part nobody
   had measured. Home now ends at 660px and holds one screen at 700. */
.home { padding-bottom: 0; }
.home .section { padding: 24px 0; }
.home .doors-section { padding-top: 0; padding-bottom: 20px; }
.home .home-about { margin-top: 12px; }
.home .home-about p { margin-bottom: 0; }
@media (min-width: 960px) {
  .home .section { padding: 12px 0 20px; }
  /* Repeated inside the query, not a duplicate: .home .section above has the
     same specificity and comes later, so it would otherwise win. */
  .home .doors-section { padding-top: 0; padding-bottom: 20px; }
  /* The headline takes the room beside the portrait, so it sets in two lines */
  .home .claim { gap: 40px; }
  .home .claim .page-title { flex: 1 1 auto; max-width: none; }
}

/* Home contact (D-017): one wide button under all three doors, with the
   address and LinkedIn written out in case the mail link does nothing */
/* D-034 took the rule's breathing room from 24px to 16px and the button from
   64px to 56px. 56px still clears the 44px hit target with room to spare. */
.home-contact { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--rule); }
.button-wide { display: flex; width: 100%; min-height: 56px; font-size: 1.125rem; }
.home-contact .write-to { margin-top: 8px; }
/* Email and LinkedIn share one line only where there is room for it (D-016).
   Below 1100px the phrase has to be allowed to wrap, or it alone overflows
   the viewport at 200% text. */
@media (min-width: 1100px) {
  .home-contact .nb { white-space: nowrap; }
}

/* Speaking: the list stops at 2023 and says so (D-019).
   Webinars: the same line, used while there is no session to list yet. */
.talks-more,
.sessions-empty { margin: 16px 0 0; font-family: var(--sans); font-size: 1rem; font-weight: 600; }
.sessions-empty { margin-top: 0; }

/* Speaking bio: the host intro. A third-person block inside a first-person
   page, so it is set apart rather than styled up. It reuses .aside's italic
   serif and ink rule, and adds only the label that says who it is for, because
   an unlabelled third-person paragraph in her own bio reads as a slip. */
.host-intro { margin-top: 32px; max-width: var(--reading); }
.host-intro-label {
  font-family: var(--sans);
  font-style: normal;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 8px;
}
.host-intro-text { margin: 0; }

/* Speaking topics: the mechanism paragraph under each lead. One step down from
   the lead in size and set in the muted ink, because a reader who stops at the
   lead has the topic and a reader who goes on gets how it works. Held back for
   length until D-035. */
.topics .mechanism {
  font-size: 1rem;
  line-height: 1.6;
  color: var(--muted);
  margin: 10px 0 0;
}

/* Booking: a two-column spec list at desktop, stacked on a phone. It holds one
   row, Formats, and the list survives the count because the layout is per row.
   D-036 cut the other four and with them .slot, the muted italic treatment for
   a row with no fact behind it: she would rather the page carry one row that is
   true than five where three say "to confirm". If a practical is ever answered
   it is a new .spec and nothing else has to change. */
.specs {
  border-top: 1px solid var(--ink);
  max-width: var(--reading);
  margin: 0;
}
.spec {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 4px 24px;
  padding: 16px 0;
  border-bottom: 1px solid var(--rule);
}
.spec dt {
  font-family: var(--sans);
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.5;
}
.spec dd { margin: 0; font-size: 1.0625rem; line-height: 1.55; }
@media (min-width: 600px) {
  .spec { grid-template-columns: 200px minmax(0, 1fr); }
  .spec dt { padding-top: 2px; }
}

/* Print. Last in the file on purpose: these rules are single-class, so the
   later .on-page and .section rules would otherwise win on source order
   and the sticky sub-navigation would print. */
@media print {
  /* A4 with a 14mm margin. Chrome is told preferCSSPageSize, so this rule and
     not the print dialog decides the sheet (D-035). */
  @page { size: A4; margin: 14mm; }
  html, body { background: #fff; color: #000; }
  .skip, .on-page, .back-to-top, .video { display: none; }
  /* The masthead keeps her name and loses its navigation. A nav bar on paper
     is five words a reader cannot click and the LinkedIn mark is a sixth, and
     together they cost the first sheet a fifth of its height. The address a
     booker actually needs is in the close block, which prints. */
  .site-nav, .nav-linkedin { display: none; }
  .masthead { position: static; background: none; border-bottom: 1px solid #000; }
  .masthead .wrap { padding-top: 0; padding-bottom: 8px; }
  /* break-inside on the section was written when the tallest section on the
     site was four short rows. Bio and Topics are now taller than an A4 sheet,
     and a block that cannot break and cannot fit starts a new page, which is
     how the first profile came out at six pages for four pages of content.
     The break rules moved down to the rows, which are the things that must
     not be split. */
  .section { border-top-width: 1px; break-inside: auto; }
  h1, h2, h3 { break-after: avoid; }
  .talks > li, .proofed, .contents > li, .topics > li, .spec, .host-intro { break-inside: avoid; }
  .aside, .proof { color: #000; }
  /* Controls are not printable. The button and the line under it say "click"
     and "download" to a reader holding a sheet of paper, and the sheet is the
     download. The address stays, because it is the one thing a booker needs
     off the page. */
  .actions { display: none; }
  /* Print is paper, not a screen at arm's length, so the whole page steps down
     one size. At screen sizes the profile runs to six A4 sheets for four sheets
     of content, and a booker printing it pays for the difference. */
  .page-title { font-size: 1.875rem; }
  .section-title { font-size: 1.375rem; }
  .prose p, .lede, .lead, .aside, .topics .lead { font-size: 0.9375rem; line-height: 1.5; }
  .topics .topic-title { font-size: 1.125rem; }
  .topics .mechanism { font-size: 0.875rem; line-height: 1.45; }
  .spec dd, .talk-title, .talk-event { font-size: 0.9375rem; }
  .portrait img { width: 88px; height: 88px; }
  .section { padding: 14px 0; }
  /* On screen the claim runs straight into the on-page bar, so it carries no
     bottom padding. In print the bar is gone and the next section's rule lands
     on the claim's last line without it. */
  .section.before-on-page { padding-bottom: 14px; }
  .topics > li { padding-top: 14px; padding-bottom: 14px; }
  .contents > li, .spec { padding: 10px 0; }
  main { padding-bottom: 0; }
  /* A link in print is ink, not blue, and its target is on the same sheet or
     on a site the reader can type in. */
  a { color: #000; }
}
