/* ==========================================================================
   evanexanes.com — Final Prototype (2026-09-28)
   USERDES (IT-252) · Front-end built from IA chart v2 (peer-tested 2026-09-14)

   Palette tokens are the WCAG-AA verified set from the USERDES Mastery
   Reference. Contrast ratios below are against Bone (#EFECE3).
   The dark theme (2026-09-28) remaps the SAME tokens under
   :root[data-theme="dark"] — the brand denim #4A70A9 is unchanged in both.
   ========================================================================== */

/* Fonts are served from this site (assets/fonts), so a visit never contacts Google. Both files are
   variable fonts: one file per script subset covers every weight the pages use. */
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 400 600; font-display: swap;
  src: url(assets/fonts/inter-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: 'Inter'; font-style: normal; font-weight: 400 600; font-display: swap;
  src: url(assets/fonts/inter-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: 'Outfit'; font-style: normal; font-weight: 400 700; font-display: swap;
  src: url(assets/fonts/outfit-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: 'Outfit'; font-style: normal; font-weight: 400 700; font-display: swap;
  src: url(assets/fonts/outfit-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;
}


:root {
  /* --- Surfaces --- */
  --bone:        #EFECE3;   /* page canvas                                   */
  --card:        #F7F5F0;   /* raised surface                                */
  --rule:        #D8D3C5;   /* decorative hairlines between sections.        */
                            /* Exempt from WCAG 1.4.11 — they carry no        */
                            /* information and identify no control.           */
  --rule-strong: #B9B2A0;   /* emphasised decorative borders                 */
  --border-ui:   #827D70;   /* BOUNDARIES OF CONTROLS — inputs, secondary    */
                            /* buttons, the menu toggle. WCAG 1.4.11 requires */
                            /* 3:1 here: 3.47:1 on bone, 3.77:1 on card.     */

  /* --- Ink --- */
  --black:       #000000;   /* display headings, wordmark   17.78:1  AAA     */
  --ink:         #13161C;   /* body text                    15.34:1  AAA     */
  --slate:       #4A5260;   /* captions, metadata            6.67:1  AAA     */

  /* --- Accents --- */
  --denim-brand: #4A70A9;   /* THE BRAND DENIM. 4.25:1 on bone — legal for  */
                            /* large/display type, fills, borders and focus  */
                            /* rings (all need 3:1). NEVER body text.        */
  --denim:       #3B5F99;   /* the same denim darkened one step, so it can   */
                            /* carry text. Links, small copy.  5.42:1  AA    */
  --denim-dark:  #2F4C7C;   /* link hover                                    */
  --clay:        #9C4526;   /* trust signals, evidence       5.40:1  AA      */

  /* The five-colour Brand Identity System declared by every shipped USERDES
     artifact is #EFECE3 / #8FABD4 / #4A70A9 / #000000 / #FFFFFF. All five are
     live here: bone is the canvas, black the display ink, soft-blue the chip
     fill, denim-brand the structural blue, white the print ground.
     --emerald and --crimson are the affirmative and negative halves of the
     status pair, both in use on the contact form. */
  --emerald:     #2F6B4F;   /* affirmative states            5.33:1  AA      */
  --crimson:     #A32E2E;   /* negative states, invalid input 5.95:1 AA      */
  --soft-blue:   #8FABD4;   /* FILLS ONLY — 1.99:1, never text               */
  --paper:       #FFFFFF;   /* print surface — the brand white               */

  /* --- Theme-dependent surfaces (added with the dark theme) ---
     These were raw values or borrowed tokens before; they need their own
     names because they do NOT simply invert. The code window stays dark in
     both themes. */
  --header-bg:    rgba(239, 236, 227, 0.92);
  --code-bg:      #13161C;
  --code-fg:      #EFECE3;  /* 15.34:1 on code-bg                          */
  --code-key:     #8FABD4;  /*  7.72:1                                     */
  --code-comment: #B9B2A0;  /*  8.57:1                                     */
  --code-chrome:  #4A5260;  /* window-bar dots and divider (decorative)   */

  /* --- Type --- */
  --font-display: 'Outfit', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-body:    'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono:    ui-monospace, 'SF Mono', 'Cascadia Mono', 'Roboto Mono', Menlo, monospace;

  /* --- Rhythm --- */
  --gutter: 24px;
  --maxw: 1200px;
  --tap: 44px;              /* WCAG 2.1 minimum touch target                 */
  --radius: 12px;           /* buttons, inputs, icon buttons (was 2px)      */
  --radius-sm: 8px;         /* tags, chips, tooltips                        */
  --radius-lg: 16px;        /* cards, header, map, footer card              */

  --t-fast: 0.15s ease;

  /* --- Brand & interaction pass (2026-09-29) ---
     Denim depth: navy -> brand denim. White text passes AA on every stop
     (#243B61 11.21, #3B5F99 6.40, #4A70A9 5.02). Elements using a gradient also
     set background-color to the LIGHTEST stop, which is what capture.py audits. */
  --grad-brand:  linear-gradient(120deg, #243B61 0%, #3B5F99 55%, #4A70A9 100%);
  --grad-footer: linear-gradient(135deg, #13161C 0%, #1C2C48 55%, #243B61 100%);
  --shadow-xs:    0 1px 2px rgba(19, 22, 28, 0.05);
  --shadow-sm:    0 1px 3px rgba(19, 22, 28, 0.08), 0 1px 2px rgba(19, 22, 28, 0.04);
  --shadow-float: 0 6px 18px rgba(19, 22, 28, 0.06);
  --shadow-lift:  0 10px 24px rgba(19, 22, 28, 0.12);
  /* Motion: exit ~65% of enter, so hovering off feels responsive. */
  --ease-out: cubic-bezier(0.2, 0.7, 0.2, 1);
  --t-enter: 200ms var(--ease-out);
  --t-exit:  130ms ease;
}

/* ------------------------------------------------------------- dark theme */
/* Same token names, new values. Ratios are against the dark canvas #12151B;
   every text pair also clears its bar on the dark card #1A1F28.
   The attribute is set before first paint by the inline script in <head>
   (stored choice → ?theme= → system preference), so there is no flash.
   Screen only: print always uses the light tokens. */
@media screen {
  :root[data-theme="dark"] {
    color-scheme: dark;       /* native scrollbars and form controls follow */

    --bone:        #12151B;   /* page canvas                                */
    --card:        #1A1F28;   /* raised surface                             */
    --rule:        #2A303B;   /* decorative hairlines (exempt, 1.4.11)      */
    --rule-strong: #3D4555;   /* emphasised decorative borders              */
    --border-ui:   #7A8496;   /* control boundaries      4.85:1 (card 4.38) */

    --black:       #FFFFFF;   /* display headings        18.28:1  AAA       */
    --ink:         #EFECE3;   /* body text — the brand bone  15.47:1  AAA   */
    --slate:       #A9B2C0;   /* captions, metadata       8.54:1  AAA       */

    /* --denim-brand stays #4A70A9: 3.64:1 here, 3.29:1 on card — still
       legal for fills, borders and focus rings (3:1). Still never text. */
    --denim:       #8FABD4;   /* links — the brand soft-blue  7.79:1  AAA   */
    --denim-dark:  #B4C8E6;   /* link hover              10.75:1  AAA       */
    --clay:        #E0906B;   /* trust signals, evidence  7.29:1  AAA       */
    --emerald:     #5FBF92;   /* affirmative states       8.14:1  AAA       */
    --crimson:     #F08A8A;   /* negative states          7.58:1  AAA       */

    --header-bg:    rgba(18, 21, 27, 0.92);
    --shadow-xs:    0 1px 2px rgba(0, 0, 0, 0.35);
    --shadow-sm:    0 1px 3px rgba(0, 0, 0, 0.40), 0 1px 2px rgba(0, 0, 0, 0.30);
    --shadow-float: 0 6px 18px rgba(0, 0, 0, 0.30);
    --shadow-lift:  0 10px 24px rgba(0, 0, 0, 0.35);
    --code-bg:      #0B0D11;
    --code-comment: #9AA3B2;  /* 7.64:1 on #0B0D11                          */
  }
}

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

/* scroll-padding-top: in-page anchors (#work, #faq) land below the floating
   header instead of under it (12 px float + 68 px bar + 20 px air). */
html { font-size: 16px; scroll-behavior: smooth; scroll-padding-top: 100px; }

body {
  background: var(--bone);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--denim); text-decoration: none; }
a:hover { color: var(--denim-dark); text-decoration: underline; text-underline-offset: 3px; }

/* Focus is never removed — only restyled. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
summary:focus-visible {
  outline: 3px solid var(--denim-brand);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

img { max-width: 100%; display: block; }

/* --------------------------------------------------------------- utilities */
.container { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--black); color: var(--bone);
  padding: 14px 20px; font-weight: 600;
}
.skip-link:focus { left: 0; text-decoration: none; }

/* Off-screen text for assistive tech only. Used to announce that a link opens
   in a new tab (WCAG 3.2.5) — the ' \2197' marker in .ext is visual only. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* ------------------------------------------------------------------ header */
/* Pass 2 (2026-09-29): the header CTA is Schedule a call; Download CV lives in the Home hero, the drawer, the footer and About. Three menu items only —
   "Proof" was removed by the peer test, and the old Contact header link
   was dropped because Contact is already a menu item. */
/* Floating header (2026-09-29). The sticky element is a transparent strip whose
   12 px top padding is the "float"; the visible bar is .header-inner, inset from
   the container edges with a 16 px radius. Scrolled (JS adds .is-scrolled via an
   IntersectionObserver in script.js), the bar takes the denim gradient: a
   ::before layer fades in over a solid #4A70A9 (the lightest stop, which is what
   the contrast audit measures). */
.site-header {
  position: sticky; top: 0; z-index: 100;
  padding-top: 12px;
  pointer-events: none;       /* the transparent strip and the sides must not swallow clicks... */
}
.header-inner {
  position: relative; isolation: isolate;
  pointer-events: auto;       /* ...only the bar (and its open menu) takes them */
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px; min-height: 68px;
  width: calc(100% - 2 * var(--gutter));
  max-width: calc(var(--maxw) - 2 * var(--gutter));
  padding-inline: 18px 12px;
  background-color: var(--header-bg);
  backdrop-filter: blur(10px);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float);
  transition: background-color var(--t-enter), border-color var(--t-enter), box-shadow var(--t-enter);
}
.header-inner::before {
  content: ''; position: absolute; inset: -1px; z-index: -1;
  border-radius: inherit;
  background-color: #4A70A9; background-image: var(--grad-brand);
  opacity: 0; transition: opacity var(--t-enter);
}

/* Logo: the <E mark + name + subline. The link's accessible name starts with
   the visible name (WCAG 2.5.3); the SVG is decorative. */
.wordmark {
  display: inline-flex; align-items: center; gap: 10px;
  min-height: var(--tap); flex: 0 0 auto;
  color: var(--black);
}
.wordmark:hover { color: var(--black); text-decoration: none; }
.logo-mark {
  width: 30px; height: 30px; flex: none;
  fill: none; stroke: url(#logo-grad-light); stroke-width: 4.5;
  stroke-linecap: round; stroke-linejoin: round;
  transition: transform var(--t-exit);
}
:root[data-theme="dark"] .logo-mark { stroke: url(#logo-grad-dark); }
.wordmark-text { display: flex; flex-direction: column; line-height: 1; }
.wordmark-name {
  font-family: var(--font-display);
  font-size: 1.125rem; font-weight: 600; letter-spacing: -0.01em;
  white-space: nowrap;        /* the name is one unit — never break it */
}
.wordmark-sub {
  margin-top: 4px; padding-top: 3px; border-top: 1px solid currentColor;
  font-family: var(--font-body); font-size: 0.75rem; font-weight: 500;
  letter-spacing: 0; white-space: nowrap;
  color: var(--slate);        /* 6.67:1 light, 8.54:1 dark */
}

.site-nav { display: flex; align-items: center; gap: 4px; }
.site-nav a {
  color: var(--ink);
  font-size: 0.9375rem; font-weight: 500;
  padding: 0 16px;
  min-height: var(--tap);
  display: inline-flex; align-items: center;
  border-bottom: 2px solid transparent;
  transition: border-color var(--t-fast), color var(--t-fast);
}
.site-nav a:hover { color: var(--black); text-decoration: none; border-bottom-color: var(--rule-strong); }
.site-nav a[aria-current] { color: var(--black); font-weight: 600; border-bottom-color: var(--denim); }

.header-cta { display: flex; align-items: center; gap: 12px; }

/* Theme switch. A utility control, not an IA item — the menu is still three
   links. aria-pressed carries the state; the icon shows the theme a press
   will switch TO (moon in light, sun in dark), the same convention as the
   reference theme. 44 x 44 px target. */
.theme-toggle {
  width: var(--tap); height: var(--tap); flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; color: var(--ink);
  border: 1px solid var(--border-ui); border-radius: var(--radius);
  cursor: pointer;
  transition: background-color var(--t-exit), border-color var(--t-exit), color var(--t-exit);
}
.theme-toggle svg { width: 20px; height: 20px; transition: transform var(--t-exit); }
.theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }

.nav-toggle {
  display: none; flex: none;              /* never squashed below the 44 px tap size */
  width: var(--tap); height: var(--tap);
  background: none; border: 1px solid var(--border-ui); border-radius: var(--radius);
  cursor: pointer; align-items: center; justify-content: center; flex-direction: column; gap: 5px;
}
.nav-toggle .bar { display: block; width: 20px; height: 2px; background: var(--ink); transition: transform var(--t-fast), opacity var(--t-fast); }
.nav-toggle[aria-expanded="true"] .bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .bar:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Scrolled state — everything on the bar turns white on the gradient. */
.site-header.is-scrolled .header-inner {
  background-color: #4A70A9; border-color: transparent;
  box-shadow: 0 12px 28px rgba(19, 22, 28, 0.25);
}
.site-header.is-scrolled .header-inner::before { opacity: 1; }
.site-header.is-scrolled .wordmark,
.site-header.is-scrolled .wordmark:hover,
.site-header.is-scrolled .wordmark-sub,
.site-header.is-scrolled .site-nav a,
.site-header.is-scrolled .site-nav a:hover,
.site-header.is-scrolled .site-nav a[aria-current],
.site-header.is-scrolled .theme-toggle { color: #FFFFFF; }
.site-header.is-scrolled .logo-mark { stroke: #FFFFFF; }
.site-header.is-scrolled .site-nav a:hover { border-bottom-color: rgba(255, 255, 255, 0.6); }
.site-header.is-scrolled .site-nav a[aria-current] { border-bottom-color: #FFFFFF; }
.site-header.is-scrolled .theme-toggle,
.site-header.is-scrolled .nav-toggle { border-color: rgba(255, 255, 255, 0.7); }
.site-header.is-scrolled .nav-toggle .bar { background: #FFFFFF; }
.site-header.is-scrolled .header-cta .btn-primary { background: #FFFFFF; color: #243B61; box-shadow: none; }
/* Keyboard focus on the gradient: the global ring is #4A70A9, the gradient's own lightest
   stop (1.0:1), so it goes white here (5.02:1), as it does on the footer card. */
.site-header.is-scrolled :focus-visible { outline-color: #FFFFFF; }

/* Pass 2 — at 1024 px and below the bar is the wordmark and the hamburger only; the links, the
   theme switch and Schedule a call move into the drawer. The wordmark text is never hidden:
   with two items in the bar it fits from 320 px (26 px mark + 10 + ~105 px name + 44 px toggle). */
@media (max-width: 1024px) {
  .site-nav, .header-cta { display: none; }
  .nav-toggle { display: flex; margin-left: auto; }
  .header-inner { gap: 12px; }
}

/* Drawer (D12). Fixed panel from the right over a shade. background-color is --grad-footer's
   lightest stop (#243B61), what capture.py audits: white 11.2:1, #8FABD4 4.8:1, #DCE6F3 8.9:1. */
html.drawer-open { overflow: hidden; scrollbar-gutter: stable; }   /* no layout shift with classic scrollbars */
.drawer-shade {
  position: fixed; inset: 0; z-index: 190;
  background: rgba(19, 22, 28, 0.5);
  opacity: 0; transition: opacity var(--t-enter);
}
.drawer-shade.is-open { opacity: 1; }
.site-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 200;
  width: min(84vw, 360px);
  display: flex; flex-direction: column; gap: 8px;
  padding: 14px 18px 20px;
  background-color: #243B61; background-image: var(--grad-footer);
  color: #FFFFFF; overflow-y: auto;
  transform: translateX(100%); transition: transform var(--t-enter);
}
.site-drawer.is-open { transform: none; }
.site-drawer[hidden], .drawer-shade[hidden] { display: none; }
.site-drawer :focus-visible { outline-color: #FFFFFF; }
.drawer-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.site-drawer .wordmark, .site-drawer .wordmark:hover, .site-drawer .wordmark-sub { color: #FFFFFF; }
.site-drawer .logo-mark, :root[data-theme="dark"] .site-drawer .logo-mark { stroke: #FFFFFF; }
.drawer-close {
  width: var(--tap); height: var(--tap); flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; color: #FFFFFF; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.55); border-radius: var(--radius);
}
.drawer-close svg { width: 20px; height: 20px; }
.drawer-nav { display: flex; flex-direction: column; }
.drawer-nav a {
  display: flex; align-items: center; min-height: 52px;
  font-family: var(--font-display); font-size: 1.125rem; font-weight: 600; color: #FFFFFF;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  border-left: 2px solid transparent;
}
.drawer-nav a:hover { color: #FFFFFF; text-decoration: none; }
.drawer-nav a[aria-current] { color: #8FABD4; border-left-color: #8FABD4; padding-left: 12px; }
.drawer-actions { display: grid; gap: 10px; margin-top: 20px; }
.drawer-actions .btn { justify-content: center; width: 100%; }
.drawer-actions .btn svg { width: 16px; height: 16px; }
.drawer-theme {
  margin-top: auto; min-height: var(--tap);
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 0 0; background: none; border: 0; border-top: 1px solid rgba(255, 255, 255, 0.12);
  color: #DCE6F3; font: inherit; font-size: 0.9375rem; cursor: pointer;
}
.drawer-switch {
  position: relative; width: 46px; height: 26px; border-radius: 13px;
  background: rgba(255, 255, 255, 0.22); transition: background-color var(--t-exit);
}
.drawer-switch::after {
  content: ''; position: absolute; left: 3px; top: 3px; width: 20px; height: 20px;
  border-radius: 50%; background: #FFFFFF; transition: transform var(--t-exit);
}
.drawer-theme[aria-pressed="true"] .drawer-switch { background: #4A70A9; }
.drawer-theme[aria-pressed="true"] .drawer-switch::after { transform: translateX(20px); }

@media (hover: hover) {
  .wordmark:hover .logo-mark { transform: translateX(-2px); transition: transform var(--t-enter); }
  .theme-toggle:hover {
    background-color: color-mix(in srgb, var(--soft-blue) 22%, transparent);
    border-color: var(--denim-brand); color: var(--black);
    transition-duration: 200ms; transition-timing-function: var(--ease-out);
  }
  .theme-toggle:hover .icon-moon { transform: rotate(-20deg); transition: transform var(--t-enter); }
  .theme-toggle:hover .icon-sun  { transform: rotate(45deg);  transition: transform var(--t-enter); }
  .site-header.is-scrolled .theme-toggle:hover { background-color: rgba(255, 255, 255, 0.14); border-color: #FFFFFF; color: #FFFFFF; }
}
.theme-toggle:focus-visible { background-color: color-mix(in srgb, var(--soft-blue) 22%, transparent); }

/* ------------------------------------------------------------------ button */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: var(--tap); padding: 0 22px;
  font-family: var(--font-body); font-size: 0.9375rem; font-weight: 600;
  border: 1px solid transparent; border-radius: var(--radius);
  cursor: pointer; white-space: nowrap;
  transition: background-color var(--t-exit), color var(--t-exit), border-color var(--t-exit),
              transform var(--t-exit), box-shadow var(--t-exit);
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); box-shadow: 0 1px 2px rgba(19, 22, 28, 0.1); }
/* Primary: the denim-depth gradient. background-color is the lightest stop
   (#4A70A9, 5.02:1 with white) — what the audit measures. */
.btn-primary {
  background-color: #4A70A9; background-image: var(--grad-brand); color: #FFFFFF;
  box-shadow: 0 2px 6px rgba(36, 59, 97, 0.20), 0 1px 2px rgba(36, 59, 97, 0.12);
}
.btn-primary:hover { color: #FFFFFF; }
.btn-secondary {
  background: transparent; color: var(--denim); border: 1.5px solid var(--denim-brand);
  box-shadow: var(--shadow-xs);
}
.btn-secondary:active { box-shadow: none; }
.btn-secondary:hover {
  background-color: color-mix(in srgb, var(--soft-blue) 18%, transparent);
  color: var(--denim-dark); border-color: var(--denim-dark);
}
.btn-ghost { background: transparent; color: var(--denim); padding: 0 4px; }
.btn-ghost:hover { color: var(--denim-dark); text-decoration: underline; text-underline-offset: 3px; }

/* -------------------------------------------------------------------- hero */
/* IA v2: "Opens with a one-line intro." Built against the persona constraint
   of a 30–60 second first review: who, what, proof and CV above the fold. */
.hero { padding: 88px 0 72px; border-bottom: 1px solid var(--rule); }
.hero-role {
  font-family: var(--font-mono); font-size: 0.8125rem;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--clay);
  margin-bottom: 20px;
}
.hero-intro {
  font-family: var(--font-display);
  font-size: clamp(2rem, 4.4vw, 3.25rem);
  line-height: 1.12; letter-spacing: -0.025em; font-weight: 600;
  color: var(--black);
  max-width: 20ch;
  text-wrap: balance;
}
.hero-support {
  margin-top: 24px; max-width: 58ch;
  font-size: 1.0625rem; color: var(--slate);
}
.hero-actions { margin-top: 36px; display: flex; flex-wrap: wrap; gap: 12px; }

/* Fact card (D8): one raised card divided into cells, used for the Home facts and each case study's
   Role / Build size / Stack. Icons are CSS masks keyed to cell order, so the <dl> markup is
   unchanged; the tile is the ::before, the glyph the ::after. */
.hero-meta, .case-facts {
  display: grid; grid-template-columns: repeat(4, 1fr);
  margin-top: 56px;
  background: var(--card); border: 1px solid var(--rule);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-float);
}
.case-facts { grid-template-columns: repeat(3, 1fr); margin-top: 32px; }
.hero-meta > div, .case-facts > div {
  position: relative;
  display: grid; grid-template-columns: 40px 1fr; column-gap: 14px; align-content: start;
  padding: 20px 22px; border-left: 1px solid var(--rule);
}
.hero-meta > div:first-child, .case-facts > div:first-child { border-left: 0; }
.hero-meta > div::before, .case-facts > div::before {
  content: ''; grid-row: 1 / span 2; width: 40px; height: 40px;
  border-radius: 10px; background: rgba(143, 171, 212, 0.28);
}
.hero-meta > div::after, .case-facts > div::after {
  content: ''; position: absolute; left: 32px; top: 30px; width: 20px; height: 20px;
  background: var(--denim-dark);
  -webkit-mask: var(--icon) center / contain no-repeat; mask: var(--icon) center / contain no-repeat;
}
.hero-meta dt, .case-facts dt { grid-column: 2; font-size: 0.8125rem; font-weight: 500; color: var(--slate); }
.hero-meta dd, .case-facts dd { grid-column: 2; margin-top: 2px; font-size: 0.98rem; line-height: 1.4; font-weight: 600; color: var(--ink); }
.hero-meta > div:nth-child(1) { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 21s-7-6.2-7-11a7 7 0 0 1 14 0c0 4.8-7 11-7 11z'/%3E%3Ccircle cx='12' cy='10' r='2.5'/%3E%3C/svg%3E"); }
.hero-meta > div:nth-child(2) { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 7l-5 5 5 5M16 7l5 5-5 5'/%3E%3C/svg%3E"); }
.hero-meta > div:nth-child(3) { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='6.5'/%3E%3Cpath d='M20 20l-4.2-4.2'/%3E%3C/svg%3E"); }
.hero-meta > div:nth-child(4) { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 9l10-5 10 5-10 5z'/%3E%3Cpath d='M6 11v5c0 1.5 2.7 3 6 3s6-1.5 6-3v-5'/%3E%3C/svg%3E"); }
.case-facts > div:nth-child(1) { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='8' r='4'/%3E%3Cpath d='M4 21c0-4 3.6-6.5 8-6.5s8 2.5 8 6.5'/%3E%3C/svg%3E"); }
.case-facts > div:nth-child(2) { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='7' height='7' rx='1.5'/%3E%3Crect x='14' y='3' width='7' height='7' rx='1.5'/%3E%3Crect x='3' y='14' width='7' height='7' rx='1.5'/%3E%3Crect x='14' y='14' width='7' height='7' rx='1.5'/%3E%3C/svg%3E"); }
.case-facts > div:nth-child(3) { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3l9 5-9 5-9-5z'/%3E%3Cpath d='M3 13l9 5 9-5'/%3E%3C/svg%3E"); }

@media (max-width: 1024px) {
  .hero-meta { grid-template-columns: 1fr 1fr; }
  .hero-meta > div:nth-child(3) { border-left: 0; }
  .hero-meta > div:nth-child(n+3) { border-top: 1px solid var(--rule); }
}
@media (max-width: 600px) {
  .hero-meta, .case-facts { grid-template-columns: 1fr; margin-top: 36px; }
  .hero-meta > div, .case-facts > div { border-left: 0; }
  .hero-meta > div + div, .case-facts > div + div { border-top: 1px solid var(--rule); }
}


/* Wednesday revision R1 — the hero's right half carries the proof of
   "and get found": an excerpt of the page's own Person JSON-LD.
   On ink: bone 15.34:1, soft-blue 7.72:1, rule-strong 8.57:1 — all AAA.
   Soft-blue is text here only because the ground is ink, not bone. */
.hero-grid { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 56px; align-items: center; }
.hero-proof { margin: 0; }
.code-window {
  background: var(--code-bg); border-radius: var(--radius);
  border: 1px solid var(--rule);          /* edges it on the dark canvas */
  border-top: 3px solid var(--denim-brand);
  overflow: hidden;
}
.code-bar { display: flex; gap: 6px; padding: 12px 16px; border-bottom: 1px solid var(--code-chrome); }
.code-bar span { width: 10px; height: 10px; border-radius: 50%; background: var(--code-chrome); }
/* macOS window controls — decorative (the bar is aria-hidden), same in both themes. */
.code-bar span:nth-child(1) { background: #FF5F57; }
.code-bar span:nth-child(2) { background: #FEBC2E; }
.code-bar span:nth-child(3) { background: #28C840; }
.code-window pre {
  margin: 0; padding: 18px 20px 22px;
  font-family: var(--font-mono); font-size: 0.8125rem; line-height: 1.7;
  color: var(--code-fg); white-space: pre-wrap; overflow-wrap: anywhere;
}
.code-window .k { color: var(--code-key); }
.code-window .v { color: var(--code-fg); }
.code-window .c { color: var(--code-comment); }
.hero-proof figcaption { margin-top: 12px; font-size: 0.875rem; color: var(--slate); max-width: 48ch; }

/* ----------------------------------------------------------------- section */
.section { padding: 72px 0; border-bottom: 1px solid var(--rule); }
.section:last-of-type { border-bottom: none; }
.section-title {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  line-height: 1.2; letter-spacing: -0.015em; font-weight: 600; color: var(--black);
}
.section-lede { margin-top: 14px; max-width: 62ch; color: var(--slate); }
.section-head { margin-bottom: 40px; }

/* ------------------------------------------------------------- work / cards */
/* IA v2, Work branch: three case studies, "strongest project first".
   Live-site and code links are NOT here — after revision 2 of the peer test
   they live only inside each case study. The card links to the case study. */
.work-list { display: grid; gap: 0; border-top: 1px solid var(--rule); }
/* Sub-block under the case studies (first-year code): same rows, smaller lead-in. */
.work-sub { margin-top: 72px; margin-bottom: 24px; }
.work-sub .section-title { font-size: 1.5rem; }

.work-item {
  display: grid; grid-template-columns: 1fr auto;
  gap: 32px; align-items: start;
  position: relative;
  padding: 32px 0;
  border-radius: var(--radius-lg);
  padding-inline: 16px; margin-inline: -16px;   /* 16 = smallest gutter: no overflow at 360 */
  transition: background-color var(--t-exit), box-shadow var(--t-exit), transform var(--t-exit);
}
/* The row is a rounded card on hover/focus, so its divider is drawn inset by that same 16 px:
   a border-bottom would curl up at both ends and run 16 px wider than the list's top rule. */
.work-item::after {
  content: ''; position: absolute; inset-inline: 16px; bottom: 0; height: 1px;
  background: var(--rule); transition: opacity var(--t-exit);
}
.work-item:focus-within { background: var(--card); box-shadow: var(--shadow-lift); }
.work-item:focus-within::after { opacity: 0; }
.work-title {
  font-family: var(--font-display); font-size: 1.375rem; font-weight: 600;
  letter-spacing: -0.01em; color: var(--black); margin-bottom: 2px;
}
/* Padded to a 44 px tap target; the heading margin above is reduced by the
   same amount so the visual rhythm is unchanged. */
.work-title a { color: inherit; display: inline-block; padding: 8px 0; }
.work-title a:hover { color: var(--denim-dark); text-decoration: none; }
.work-summary { color: var(--slate); max-width: 60ch; }
.work-action { display: flex; align-items: center; min-height: var(--tap); padding-top: 4px; }

/* Stack tags — monospace chips, soft-blue fill with ink text (fill-only rule) */
.stack { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; list-style: none; }
.stack li {
  font-family: var(--font-mono); font-size: 0.75rem; letter-spacing: 0.02em;
  color: var(--ink);
  background: rgba(143, 171, 212, 0.28);                          /* fallback */
  background: color-mix(in srgb, var(--soft-blue) 28%, transparent);
  border: 1px solid rgba(74, 112, 169, 0.34);                     /* fallback */
  border-color: color-mix(in srgb, var(--denim-brand) 34%, transparent);
  padding: 4px 10px; border-radius: var(--radius-sm);
  box-shadow: var(--shadow-xs);
  transition: background-color var(--t-exit), border-color var(--t-exit), transform var(--t-exit), box-shadow var(--t-exit);
}

/* ------------------------------------------------------------ testimonials */
/* A section of Home, not a menu item (IA v2 keeps three branches). Without script the
   slides stack as a list; script.js adds .is-enhanced and turns them into a carousel.
   No star ratings: one writer gave none, and a mixed set would break the identical format. */
.t-slide {
  margin: 0; padding: 32px 0; border-top: 1px solid var(--rule);
  display: grid; grid-template-columns: 240px minmax(0, 1fr);
  grid-template-areas: "photo mark" "photo quote" "photo who";
  align-content: center; column-gap: 64px; row-gap: 20px;
}
.t-slide:first-child { padding-top: 0; border-top: 0; }
.t-photo {
  grid-area: photo; display: block; align-self: center;
  width: 240px; height: 240px; border-radius: 50%; object-fit: cover; object-position: 50% 35%;
}
/* Decorative opening quote; the quote text itself carries no quotation marks on desktop. */
.t-mark {
  grid-area: mark; height: 56px;
  font-family: var(--font-display); font-weight: 600; font-size: 112px; line-height: 0.7;
  color: var(--denim-brand);
}
.t-quote { grid-area: quote; margin: 0; font-size: 1.375rem; line-height: 1.55; color: var(--ink); }
.t-quote p { margin: 0; }
.t-who { grid-area: who; display: flex; flex-direction: column; gap: 2px; }
.t-name { font-family: var(--font-display); font-weight: 600; font-size: 1.1875rem; line-height: 1.25; color: var(--black); }
.t-role { font-size: 0.9375rem; line-height: 1.4; color: var(--slate); }

.t-stage.is-enhanced {
  display: grid; align-items: center; column-gap: 20px; row-gap: 28px;
  grid-template-columns: 44px minmax(0, 1fr) 44px;
  grid-template-areas: "prev view next" ". count .";
}
.is-enhanced .t-prev { grid-area: prev; }
.is-enhanced .t-next { grid-area: next; }
.is-enhanced .t-viewport { grid-area: view; min-width: 0; overflow: hidden; touch-action: pan-y; }
.is-enhanced .t-track { display: flex; transition: transform 550ms var(--ease-out); }
.is-enhanced .t-slide {
  flex: 0 0 100%; padding: 16px 0; border-top: 0;
  opacity: 0; transition: opacity 400ms ease;
}
.is-enhanced .t-slide.is-active { opacity: 1; }
.is-enhanced .t-arrow {
  display: grid; place-items: center; width: 44px; height: 44px; padding: 0;
  border: 0; border-radius: var(--radius);
  background: var(--denim-brand); color: #FFFFFF; cursor: pointer;
  transition: filter var(--t-exit), transform var(--t-enter);
}
.is-enhanced .t-arrow:hover { filter: brightness(0.9); }
.is-enhanced .t-arrow:active { transform: scale(0.96); }
.is-enhanced .t-count {
  grid-area: count; justify-self: start; margin: 0;
  font-size: 0.9375rem; line-height: 1; color: var(--slate); font-variant-numeric: tabular-nums;
}

@media (max-width: 700px) {
  .t-slide {
    grid-template-columns: minmax(0, 1fr); grid-template-areas: "photo" "quote" "who";
    justify-items: start; column-gap: 0;
  }
  .t-photo { width: 96px; height: 96px; }
  .t-mark { display: none; }
  .t-quote { font-size: 1.125rem; }
  .t-quote p::before { content: '\201C'; }
  .t-quote p::after  { content: '\201D'; }
  .t-stage.is-enhanced {
    column-gap: 12px; row-gap: 20px;
    grid-template-areas: "view view view" "prev count next";
  }
  .is-enhanced .t-count { justify-self: center; }
}
@media (prefers-reduced-motion: reduce) {
  .is-enhanced .t-track, .is-enhanced .t-slide, .is-enhanced .t-arrow { transition: none; }
}

/* ------------------------------------------------------------- case study */
.breadcrumb {
  font-family: var(--font-mono); font-size: 0.8125rem; color: var(--slate);
  padding: 8px 0 0; display: flex; flex-wrap: wrap; gap: 0; align-items: center;
}
/* Each crumb is its own target and gets the full 44 x 44 px. The padding
   supplies the spacing the flex gap used to, and the first crumb pulls left
   by its own padding so the row still starts flush with the container. */
.breadcrumb a {
  color: var(--denim);
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--tap); min-width: var(--tap);
  padding: 0 9px;
}
.breadcrumb a:first-child { margin-left: -9px; }
.breadcrumb [aria-current="page"] { padding-left: 9px; }
.breadcrumb [aria-current="page"] { color: var(--ink); }

.case-header { padding: 32px 0 56px; border-bottom: 1px solid var(--rule); }
.case-title {
  font-family: var(--font-display);
  font-size: clamp(1.875rem, 3.6vw, 2.75rem);
  line-height: 1.15; letter-spacing: -0.022em; font-weight: 600; color: var(--black);
  max-width: 22ch; text-wrap: balance;
}
.case-lede { margin-top: 20px; max-width: 60ch; font-size: 1.0625rem; color: var(--slate); }

/* D9: the live site and the code sit right under the lede, as a plain button row. */
.case-links { margin-top: 28px; display: flex; flex-wrap: wrap; gap: 12px; }
.case-links .btn svg { width: 16px; height: 16px; flex: none; }


.case-visual { margin: 0 0 48px; }
/* Relationship diagram (D10): nodes -> labelled dashed connector -> hub. Every number in it is one the
   page text already states. The connector label is real text (it names the relationship). */
.case-diagram .cd-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) 132px minmax(0, 1fr); align-items: center;
  background: var(--card); border: 1px solid var(--rule); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float); padding: clamp(18px, 3vw, 28px);
}
.case-diagram .cd-grid--3 { grid-template-columns: minmax(0, 1fr) 112px minmax(0, 1fr) 112px minmax(0, 1fr); }
.cd-label { font-family: var(--font-display); font-size: 0.9375rem; font-weight: 600; color: var(--slate); margin-bottom: 10px; }
.cd-nodes { list-style: none; display: grid; gap: 8px; }
.cd-node {
  padding: 9px 12px; font-size: 0.9375rem; font-weight: 500; color: var(--ink);
  background: var(--bone); border: 1px solid var(--border-ui); border-radius: var(--radius-sm);
}
.cd-node.is-hot { border: 2px solid var(--denim-brand); font-weight: 600; }
.cd-link { position: relative; align-self: center; height: 2px; margin: 0 10px;
  background: repeating-linear-gradient(90deg, var(--denim-brand) 0 6px, transparent 6px 11px); }
.cd-link::after {
  content: ''; position: absolute; right: -1px; top: -5px; width: 10px; height: 10px;
  border-top: 2px solid var(--denim-brand); border-right: 2px solid var(--denim-brand); transform: rotate(45deg);
}
.cd-link--rev::after { right: auto; left: -1px; transform: rotate(-135deg); }
.cd-link-label {
  position: absolute; left: 50%; bottom: 10px; transform: translateX(-50%);
  font-size: 0.8125rem; font-weight: 600; color: var(--denim); text-align: center; white-space: nowrap;
}
.cd-hub {
  background: var(--paper); border: 2px solid var(--denim-brand); border-radius: 14px;
  padding: 16px 18px; box-shadow: var(--shadow-lift);
}
:root[data-theme="dark"] .cd-hub { background: var(--bone); }
.cd-hub-title { font-family: var(--font-display); font-size: 1.125rem; font-weight: 600; color: var(--black); }
.cd-hub-detail { margin-top: 4px; font-size: 0.875rem; color: var(--slate); }
.cd-chips { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.cd-chips li {
  padding: 3px 9px; font-size: 0.8125rem; font-weight: 500; color: var(--ink);
  background: var(--bone); border: 1px solid var(--rule-strong); border-radius: var(--radius-sm);
}
.case-visual figcaption { margin-top: 14px; max-width: 68ch; font-size: 0.875rem; color: var(--slate); }

@media (max-width: 768px) {
  .case-diagram .cd-grid, .case-diagram .cd-grid--3 { grid-template-columns: 1fr; gap: 0; }
  .cd-link { justify-self: center; width: 2px; height: 56px; margin: 8px 0;
    background: repeating-linear-gradient(180deg, var(--denim-brand) 0 6px, transparent 6px 11px); }
  .cd-link::after { right: auto; left: -5px; top: auto; bottom: -1px; transform: rotate(135deg); }
  .cd-link--rev::after { bottom: auto; top: -1px; transform: rotate(-45deg); }
  .cd-link-label { left: 16px; bottom: 50%; transform: translateY(50%); }
}

.prose { max-width: 68ch; }
.prose h2 {
  font-family: var(--font-display); font-size: 1.25rem; font-weight: 600;
  color: var(--black); margin-top: 40px; margin-bottom: 12px;
}
.prose h2:first-child { margin-top: 0; }
.prose-tight { margin-top: -8px; }
.prose p + p { margin-top: 16px; }
.prose ul { margin-top: 14px; padding-left: 22px; }
.prose li + li { margin-top: 8px; }

.case-nav {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 16px;
  padding: 36px 0; border-top: 1px solid var(--rule);
}

/* ---------------------------------------------------------------- about */
/* Addendum 1 (D14): the portrait sits beside the intro and the contact card overlaps its
   bottom edge. The card is the one raised element over the photo, so its shadow marks a
   real elevation. */
.about-intro { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); gap: 56px; align-items: center; }
.about-portrait { width: 100%; max-width: 420px; justify-self: end; }
.portrait-img {
  display: block; width: 100%; height: auto; aspect-ratio: 4 / 5;
  object-fit: cover; object-position: 50% 20%;
  border-radius: var(--radius-lg); border: 1px solid var(--rule); background: var(--card);
}
.portrait-card {
  position: relative; margin: -56px 20px 0; padding: 16px 18px;
  background: var(--card); border: 1px solid var(--rule); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float);
}
.portrait-name { font-family: var(--font-display); font-weight: 600; font-size: 1rem; color: var(--black); }
.portrait-role { color: var(--slate); font-size: 0.875rem; margin-bottom: 12px; }
.portrait-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.portrait-actions .btn { flex: 1 1 auto; justify-content: center; }
.portrait-actions .btn svg { width: 18px; height: 18px; flex: none; }

/* About stats (2026-10-09, option E): one card under the hero buttons, the same floating
   surface as the portrait card, so the two read as a pair. The number is the focal point and
   takes the display face. Each proof link stretches over its whole cell (::after), so the cell
   is the tap target; flex order puts the number on top while the <dt> stays first for screen
   readers. Under 600px the card stacks into rows: number left, label and link right. */
.about-stats {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  margin-top: 36px; background: var(--card); border: 1px solid var(--rule);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-float);
}
.about-stat { position: relative; display: flex; flex-direction: column; gap: 6px; min-width: 0; padding: 22px 22px 20px; }
.about-stat + .about-stat { border-left: 1px solid var(--rule); }
.about-stat dt { order: 2; font-size: 0.875rem; font-weight: 500; line-height: 1.4; color: var(--ink); }
.about-stat .stat-value {
  order: 1; margin: 0; font-family: var(--font-display); font-weight: 700;
  font-size: clamp(2.25rem, 1.6rem + 2vw, 2.5rem); line-height: 1; letter-spacing: -0.02em;
  color: var(--black); font-variant-numeric: tabular-nums;
}
.about-stat .stat-proof { order: 3; margin: 8px 0 0; font-size: 0.8125rem; font-weight: 600; }
.stat-proof a { color: var(--denim); text-decoration: underline; text-underline-offset: 3px; }
.stat-proof a::after { content: ''; position: absolute; inset: 0; border-radius: var(--radius-lg); }
.about-stat:hover .stat-proof a { color: var(--denim-dark); text-decoration-thickness: 2px; }

/* Addendum 1 (D15): the skills ledger. Confidence is carried by type size, so the daily row
   is the one focal point and "Still learning" is quiet without being hidden. */
.ledger { border-top: 1px solid var(--rule); }
.ledger-row {
  display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 28px;
  padding: 24px 0; border-bottom: 1px solid var(--rule); align-items: baseline;
}
.ledger-row h3 { font-family: var(--font-body); font-size: 0.9375rem; font-weight: 600; color: var(--slate); }
.tools { list-style: none; display: flex; flex-wrap: wrap; }
/* Separators are drawn in CSS, with an empty alt so a screen reader still hears a plain list. */
.tools li:not(:last-child)::after { content: '/'; content: '/' / ''; padding-inline: 0.32em; color: var(--border-ui); }
.ledger-daily .tools {
  font-family: var(--font-display); font-weight: 600; line-height: 1.2; color: var(--black);
  font-size: clamp(1.5rem, 1rem + 2.2vw, 2.3rem);
}
.ledger-daily .tools li:not(:last-child)::after { color: var(--denim-brand); }
.ledger-search .tools { font-family: var(--font-display); font-weight: 600; font-size: 1.35rem; line-height: 1.4; color: var(--ink); }
.ledger-comfort .tools { font-weight: 500; font-size: 1.0625rem; line-height: 1.5; color: var(--ink); }
.ledger-learning .tools { font-size: 0.9375rem; line-height: 1.5; color: var(--slate); }
.ledger-note { margin-top: 8px; color: var(--slate); font-size: 0.9375rem; }
.ledger-prose { margin-top: 48px; max-width: 68ch; }
.ledger-prose h3 { font-family: var(--font-display); font-size: 1.25rem; font-weight: 600; color: var(--black); margin-bottom: 14px; }
.ledger-prose .prose-tight { margin-top: 0; }

/* Addendum 1 (D16): one row per school; the name is the largest text in the row. The logo
   tile stays white in both themes because the supplied logos are drawn for white. */
.edu-list { list-style: none; border-top: 1px solid var(--rule); }
.edu-row {
  display: grid; grid-template-columns: 200px 72px minmax(0, 1fr); gap: 28px; align-items: center;
  padding: 28px 0; border-bottom: 1px solid var(--rule);
}
.edu-when { color: var(--slate); font-size: 0.9375rem; font-weight: 500; }
.edu-logo {
  width: 72px; height: 72px; display: grid; place-items: center; overflow: hidden;
  background: #FFFFFF; border: 1px solid var(--rule); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float);
  transition: transform var(--t-exit), box-shadow var(--t-exit), border-color var(--t-exit);
}
.edu-logo img { width: 56px; height: 56px; object-fit: contain; }
.edu-school {
  font-family: var(--font-display); font-weight: 600; line-height: 1.15; color: var(--black);
  font-size: clamp(1.35rem, 1rem + 1.4vw, 2rem);
}
.edu-school a { color: inherit; text-decoration: underline; text-decoration-color: var(--rule); text-underline-offset: 4px; text-decoration-thickness: 2px; }
.edu-school a:hover { text-decoration-color: currentColor; }
.edu-degree { margin-top: 6px; color: var(--slate); }

/* Certificates (2026-10-09): MOVED here from "Proof" by peer-test revision 1. No hand-picked
   "featured" row any more: a key says what PDF and Verify mean, four subject tiles say what
   the 33 cover and jump into the list, and the list stays behind one disclosure. Verify is
   emerald because it is the affirmative "checked at the issuer" state; PDF stays ink. */
.cert-head {
  display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 24px 56px; align-items: end;
  margin-bottom: 32px;
}
.cert-head .section-head { margin-bottom: 0; }
.cert-key {
  display: flex; flex-direction: column; gap: 10px; padding: 16px 18px;
  background: var(--card); border: 1px solid var(--rule); border-radius: var(--radius-lg);
  font-size: 0.875rem; color: var(--slate);
}
.cert-key > div { display: flex; gap: 12px; align-items: baseline; }
.cert-key dt { flex: none; width: 4.5rem; font-weight: 600; color: var(--ink); }
.cert-key dt.cert-key-verify { color: var(--emerald); }
.cert-key dd { margin: 0; }
.cert-topics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }
.cert-topic {
  display: flex; flex-direction: column; gap: 8px; padding: 20px;
  background: var(--card); border: 1px solid var(--rule); border-radius: var(--radius-lg);
  color: var(--ink); text-decoration: none; transition: border-color var(--t-fast);
}
.cert-topic:hover { border-color: var(--border-ui); }
.cert-topic-n {
  font-family: var(--font-display); font-size: 2.25rem; font-weight: 700; line-height: 1;
  letter-spacing: -0.02em; color: var(--black); font-variant-numeric: tabular-nums;
}
.cert-topic-name { font-weight: 600; line-height: 1.35; color: var(--denim); text-decoration: underline; text-underline-offset: 3px; }
.cert-topic:hover .cert-topic-name { color: var(--denim-dark); }
.cert-topic-covers { font-size: 0.8125rem; line-height: 1.45; color: var(--slate); }
.cert-groups { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px 48px; padding: 8px 0 24px; }
.cert-col { display: flex; flex-direction: column; gap: 32px; min-width: 0; }
.cert-group h3 { font-family: var(--font-display); font-size: 1rem; font-weight: 600; color: var(--black); margin-bottom: 8px; }
.cert-count { margin-left: 6px; font-family: var(--font-body); font-size: 0.8125rem; font-weight: 400; color: var(--slate); }
.cert-group ul { list-style: none; }
/* Three columns per row: the name, PDF, then a fixed Verify column, so every PDF link lines
   up whether or not the row has a Verify link. */
.cert-group li {
  display: grid; grid-template-columns: minmax(0, 1fr) auto 5.25rem; gap: 0 8px; align-items: center;
  min-height: 52px; border-top: 1px solid var(--rule); font-size: 0.9rem;
}
.c-name { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 6px 0; }
.c-title { color: var(--ink); }
.c-issuer { color: var(--slate); font-size: 0.8125rem; }
.c-pdf, .c-verify {
  display: inline-flex; align-items: center; min-height: var(--tap); padding: 0 8px;
  font-size: 0.8125rem; font-weight: 600; text-decoration: underline; text-underline-offset: 3px;
}
.c-pdf { color: var(--ink); }
.c-verify { color: var(--emerald); }
.c-pdf:hover, .c-verify:hover { text-decoration-thickness: 2px; }
.cert-assess { margin-top: 40px; }
.cert-assess h3 { font-family: var(--font-display); font-size: 1.125rem; font-weight: 600; color: var(--black); }
.cert-assess p { margin-top: 6px; font-size: 0.875rem; color: var(--slate); }
.cert-assess ul { list-style: none; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 48px; margin-top: 12px; }
.cert-assess li {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2px 16px;
  padding: 14px 0; border-top: 1px solid var(--rule); font-size: 0.9rem;
}
.a-title { color: var(--ink); }
.a-meta { color: var(--slate); font-size: 0.8125rem; }

/* Wednesday revision R3 — the rest of a long group, on request. Same
   glyph-not-colour state marker as the FAQ; summary clears the 44 px target. */
.cert-more { margin-top: 10px; border-top: 1px solid var(--rule); }
.cert-more > summary {
  list-style: none; cursor: pointer;
  min-height: var(--tap); display: flex; align-items: center; gap: 8px;
  font-size: 0.875rem; font-weight: 600; color: var(--denim);
}
.cert-more > summary::-webkit-details-marker { display: none; }
/* + / − drawn as two bars: the vertical bar collapses to 0 as it turns, so the
   state is still a change of SHAPE, not colour (WCAG 1.4.1). Clay 5.40:1. */
.cert-more > summary::before,
.faq-item > summary::after {
  content: ''; flex: none; width: 14px; height: 14px;
  background:
    linear-gradient(var(--clay), var(--clay)) center / 14px 2px no-repeat,
    linear-gradient(var(--clay), var(--clay)) center / 2px 14px no-repeat;
  transition: transform var(--t-enter), background-size var(--t-enter);
}
.cert-more[open] > summary::before,
.faq-item[open] > summary::after { transform: rotate(180deg); background-size: 14px 2px, 2px 0; }
/* The glyph grows on keyboard focus as it does on hover (the [open] rules are more specific,
   so an open item keeps its half turn). Cancelled under reduced motion below. */
.cert-more > summary:focus-visible::before,
.faq-item > summary:focus-visible::after { transform: scale(1.15); }
.cert-more[open] > summary:focus-visible::before,
.faq-item[open] > summary:focus-visible::after { transform: rotate(180deg) scale(1.15); }
.cert-more > summary:hover { color: var(--denim-dark); text-decoration: underline; text-underline-offset: 3px; }
.cert-more ul { padding-bottom: 4px; }
/* The "all 33" disclosure is a bordered card. Written after .cert-more so it wins the cascade. */
.cert-more.cert-all {
  margin-top: 24px; padding: 0 18px;
  background: var(--card); border: 1px solid var(--border-ui); border-radius: var(--radius);
}
.cert-more.cert-all > summary { font-size: 1rem; }

/* ---------------------------------------------------------------- contact */
/* IA v2, Contact branch, "all on one page" (the note carried on the chart). Form on the
   left; the right column is Email, Working hours, Schedule a call, Follow (icon links),
   then the map. */
.contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 64px; align-items: start; }

/* Info stack (2026-09-29): email stays as copyable text; socials are icon-only
   links whose accessible name comes from aria-label. */
.info-list { list-style: none; display: grid; gap: 4px; }
.info-list li { display: flex; align-items: center; gap: 14px; min-height: 64px; }
.info-icon {
  width: var(--tap); height: var(--tap); flex: none;
  display: grid; place-items: center;
  border-radius: var(--radius); border: 1px solid var(--rule);
  background: var(--card); color: var(--denim);
}
.info-icon svg { width: 20px; height: 20px; }
.info-icon-brand { background-color: #4A70A9; background-image: var(--grad-brand); border-color: transparent; color: #FFFFFF; }
.info-label { display: block; font-family: var(--font-display); font-weight: 600; color: var(--black); line-height: 1.3; }
.info-value { font-size: 0.9375rem; color: var(--slate); }
/* Inline text links are ~19 px tall: pad them to a 44 px+ hit area and cancel the padding with
   a negative margin, so the rows do not move. */
.info-list a { font-size: 0.9375rem; display: inline-block; padding-block: 13px; margin-block: -13px; }

.follow { display: flex; align-items: center; gap: 14px; margin-top: 16px; }
.follow-label { font-family: var(--font-display); font-weight: 600; color: var(--black); }
.social-list { list-style: none; display: flex; gap: 10px; }
.social-link {
  width: var(--tap); height: var(--tap);
  display: grid; place-items: center;
  border-radius: var(--radius); border: 1px solid var(--rule);
  background: var(--card); color: var(--denim);
  transition: background-color var(--t-exit), color var(--t-exit), border-color var(--t-exit),
              transform var(--t-exit), box-shadow var(--t-exit);
}
.social-link svg { width: 20px; height: 20px; }
.social-link:hover { text-decoration: none; }
.social-link:focus-visible { background-color: #4A70A9; background-image: var(--grad-brand); color: #FFFFFF; }

.map { margin: 24px 0 0; }
.map iframe {
  display: block; width: 100%; aspect-ratio: 4 / 3;   /* reserved space: no layout shift */
  border: 1px solid var(--rule); border-radius: var(--radius-lg);
  background: var(--card);
}
.map figcaption { margin-top: 10px; font-size: 0.875rem; color: var(--slate); }

.form-field + .form-field { margin-top: 20px; }
.form-field label {
  display: block; margin-bottom: 8px;
  font-size: 0.875rem; font-weight: 600; color: var(--ink);
}
.form-field .hint { display: block; font-weight: 400; color: var(--slate); font-size: 0.8125rem; margin-top: 2px; }
.form-field input,
.form-field textarea {
  width: 100%; min-height: var(--tap); padding: 12px 14px;
  font-family: var(--font-body); font-size: 1rem; color: var(--ink);
  background: var(--card); border: 1px solid var(--border-ui); border-radius: var(--radius);
  transition: border-color var(--t-fast);
}
.form-field textarea { min-height: 140px; resize: vertical; line-height: 1.6; }
.form-field input:hover, .form-field textarea:hover { border-color: var(--slate); }

/* Placeholders show an example and vanish on typing; the label above stays
   (a placeholder alone fails WCAG 3.3.2). Slate: 6.2:1 on the card. */
.form-field input::placeholder,
.form-field textarea::placeholder { color: var(--slate); opacity: 1; }

/* Invalid state. :user-invalid fires only after the person has actually tried
   the field, so an empty form does not open covered in red. Colour is paired
   with a thicker edge, never colour alone (WCAG 1.4.1). */
.form-field input:user-invalid,
.form-field textarea:user-invalid {
  border-color: var(--crimson);
  border-width: 2px;
}
.form-note { margin-top: 18px; font-size: 0.875rem; color: var(--slate); }
.form-status {
  margin-top: 18px; padding: 14px 16px;
  background: var(--card); border: 1px solid var(--rule);
  border-left: 3px solid var(--emerald); border-radius: var(--radius);
  font-size: 0.9375rem; color: var(--ink);
}
.form-status[hidden] { display: none; }

/* -------------------------------------------------------------------- FAQ */
/* Declared addition beyond IA chart v2 (Revision Mapping §7). Built on native
   <details>/<summary>: the disclosure semantics, keyboard operation and the
   expanded/collapsed announcement come from the element itself, so there is no
   JS to desynchronise from the DOM and nothing to animate against
   prefers-reduced-motion. Depth stays at 2 — this is a section of Contact,
   not a fourth branch. */
.faq-list { border-top: 1px solid var(--rule); max-width: 80ch; }

.faq-item { border-bottom: 1px solid var(--rule); }

.faq-item > summary {
  display: flex; align-items: flex-start; gap: 20px;
  min-height: var(--tap); padding: 15px 0;
  cursor: pointer;
  color: var(--black);
  transition: color var(--t-fast);
}

/* The question is a real h3 inside the summary, so heading navigation reaches
   each question and not only the section title. It nests under the section's
   h2 — no level is skipped. */
.faq-q {
  font-family: var(--font-display);
  font-size: 1.0625rem; font-weight: 600; line-height: 1.45;
  color: inherit;
}

/* The default triangle is replaced, not merely hidden, so the control still
   has a state indicator. list-style covers Firefox, the pseudo-element covers
   older WebKit. */
.faq-item > summary { list-style: none; }
.faq-item > summary::-webkit-details-marker { display: none; }

/* State is carried by a change of GLYPH, not of colour (WCAG 1.4.1): + when
   collapsed, minus when open. Clay measures 5.40:1 on bone. */
.faq-item > summary::after { margin-left: auto; margin-top: 6px; }

.faq-item > summary:hover { color: var(--denim-dark); }

.faq-answer { padding: 2px 0 22px; max-width: 64ch; color: var(--slate); }
.faq-answer p + p { margin-top: 12px; }

/* Smooth open/close where the browser can animate to height:auto; elsewhere
   the answer simply appears. The global reduced-motion rule does not match
   ::details-content, so that block switches this transition off itself. */
@supports (interpolate-size: allow-keywords) {
  .faq-list { interpolate-size: allow-keywords; }
  .faq-item::details-content {
    height: 0; overflow: clip;
    transition: height var(--t-enter), content-visibility var(--t-enter) allow-discrete;
  }
  .faq-item[open]::details-content { height: auto; }
}

/* ----------------------------------------------------------------- footer */
.btn-light { background: #FFFFFF; color: #243B61; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12); }
.btn-light:hover { color: #243B61; }
.btn-light svg { width: 16px; height: 16px; }
/* Cal's own click handler ignores a click whose target is an SVG element, while script.js
   cancels the link for it: a click on the icon would do nothing. Let the <a> take the hit. */
[data-cal-link] svg { pointer-events: none; }
.btn-outline-light { background: transparent; color: #DCE6F3; border: 1.5px solid #8FABD4; }
.btn-outline-light:hover { color: #FFFFFF; border-color: #FFFFFF; background: rgba(255, 255, 255, 0.08); }

/* Closing CTA (D11): its own section, a centred card on the brand gradient. background-color is the
   gradient's lightest stop, #4A70A9: white text is 5.02:1 there, so every line on it is white. */
.closing-cta { padding: 24px 0 72px; }
.cta-card {
  background-color: #4A70A9; background-image: var(--grad-brand);
  color: #FFFFFF; text-align: center;
  border-radius: 20px; padding: clamp(36px, 6vw, 56px) clamp(20px, 5vw, 48px);
}
.cta-title {
  font-family: var(--font-display); font-size: clamp(1.625rem, 3.2vw, 2.25rem);
  font-weight: 600; line-height: 1.15; letter-spacing: -0.015em; color: #FFFFFF;
  max-width: 22ch; margin: 0 auto; text-wrap: balance;
}
.cta-body { max-width: 52ch; margin: 14px auto 0; color: #FFFFFF; }
.cta-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin-top: 28px; }
.cta-fine { margin-top: 18px; font-size: 0.875rem; color: #FFFFFF; }
.cta-card a:focus-visible { outline-color: #FFFFFF; }
/* #DCE6F3 (the footer's outline-button ink) is only 4.05:1 on #4A70A9: white here, 5.02:1. */
.cta-card .btn-outline-light { color: #FFFFFF; border-color: #FFFFFF; }

/* Information footer (D11): dark in both themes, full width, not a second header. background-color is
   --grad-footer's lightest stop, #243B61: #EFECE3 9.5:1, #DCE6F3 8.9:1, #A9B2C0 5.2:1. */
:root[data-theme="dark"] .site-footer { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14); }
.site-footer {
  background-color: #243B61; background-image: var(--grad-footer);
  color: #EFECE3; padding: 56px 0 24px;
}
.footer-columns { display: grid; grid-template-columns: 1.5fr 2.2fr 1.4fr; gap: 40px; }
.footer-nav-lists { display: grid; grid-template-columns: 1fr 1.3fr; gap: 40px; }
.footer-brand {
  display: inline-flex; align-items: center; gap: 10px; min-height: var(--tap);
  font-family: var(--font-display); font-size: 1.125rem; font-weight: 600; color: #FFFFFF;
}
.footer-brand:hover { color: #FFFFFF; text-decoration: none; }
.site-footer .footer-brand .logo-mark { width: 28px; height: 28px; stroke: #FFFFFF; }
.footer-blurb { margin: 10px 0 18px; max-width: 34ch; font-size: 0.9375rem; color: #A9B2C0; }
.footer-heading { font-family: var(--font-display); font-size: 1rem; font-weight: 600; color: #FFFFFF; margin-bottom: 12px; }
.footer-links { list-style: none; display: grid; gap: 2px; }
.footer-links li { display: flex; align-items: center; gap: 8px; min-height: var(--tap); font-size: 0.9375rem; color: #DCE6F3; }
.footer-links a { display: inline-flex; align-items: center; gap: 8px; min-height: var(--tap); color: #DCE6F3; }
.footer-links a:hover { color: #FFFFFF; }
.footer-links svg { width: 16px; height: 16px; flex: none; color: #8FABD4; }
.site-footer .social-link { background: rgba(255, 255, 255, 0.08); border-color: rgba(255, 255, 255, 0.12); color: #DCE6F3; }
.footer-bottom {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 36px; padding-top: 18px; border-top: 1px solid rgba(239, 236, 227, 0.15);
}
.footer-note { font-size: 0.875rem; color: #A9B2C0; }
.footer-consent {
  display: inline-flex; align-items: center; min-height: var(--tap); padding: 0; background: none; border: 0;
  font: inherit; font-size: 0.875rem; color: #DCE6F3; text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}
.footer-consent[hidden] { display: none; }
.footer-top { display: inline-flex; align-items: center; min-height: var(--tap); font-size: 0.875rem; color: #DCE6F3; }
.site-footer a:focus-visible { outline-color: #FFFFFF; }
.site-footer .social-link:focus-visible { background-color: #4A70A9; background-image: var(--grad-brand); color: #FFFFFF; }

@media (max-width: 1024px) {
  .footer-columns { grid-template-columns: 1fr 1fr; }
  .footer-about { grid-column: 1 / -1; }
}
@media (max-width: 600px) {
  .footer-columns { grid-template-columns: 1fr; gap: 28px; }
  /* 2-1 on phones (USERDES instructor's note, 2026-10-08): Pages beside Work, Contact full width
     below, because the email address needs the whole row. */
  .footer-nav-lists { grid-template-columns: 1fr 1fr; gap: 20px; }
  .cta-actions .btn { width: 100%; justify-content: center; }
  .footer-bottom { padding-right: 72px; }   /* keeps Cookie settings clear of the assistant launcher */
}
@media (max-width: 340px) {
  .footer-nav-lists { grid-template-columns: 1fr; gap: 28px; }
}
.footer-top[hidden] { display: none; }

/* A link inside running text must be told apart by more than colour (WCAG 1.4.1). */
main p a:not(.btn):not(.qa-link):not(.btn-ghost) { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }

/* -------------------------------------------------------------- assistant */
/* Quick answers (Pass 2). Launcher: brand gradient, 56 px. The visitor bubble is #3B5F99 in both
   themes (white 6.40:1); --denim would turn soft-blue in dark and fail with white text. */
.qa-launcher {
  position: fixed; right: 16px; bottom: 16px; z-index: 150;
  width: 56px; height: 56px; border-radius: 50%; border: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  background-color: #4A70A9; background-image: var(--grad-brand); color: #FFFFFF;
  box-shadow: 0 10px 24px rgba(36, 59, 97, 0.35);
}
.qa-launcher svg { width: 24px; height: 24px; }
/* Two-tone ring: white against the dark footer and the CTA card, navy against a light page. */
.qa-launcher:focus-visible { outline: 3px solid #FFFFFF; outline-offset: 0; box-shadow: 0 0 0 6px #243B61; }
html.drawer-open .qa-launcher, html.drawer-open .qa-panel { display: none; }

/* Back to top sits above the launcher, centred on it. Outlined, not filled, so the launcher
   stays the one brand-coloured control in the corner. Border and arrow use --denim: 3:1+ in both themes. */
.to-top {
  position: fixed; right: 22px; bottom: 84px; z-index: 150;
  width: var(--tap); height: var(--tap); border-radius: 50%; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--card); color: var(--denim); border: 2px solid var(--denim);
  box-shadow: 0 6px 16px rgba(36, 59, 97, 0.18);
}
.to-top[hidden] { display: none; }
.to-top svg { width: 20px; height: 20px; }
.to-top:hover { background: var(--denim); color: var(--card); }
/* Same two-tone ring as the launcher: readable over the light page and the dark footer. */
.to-top:focus-visible { outline: 3px solid #FFFFFF; outline-offset: 0; box-shadow: 0 0 0 6px #243B61; }
html.drawer-open .to-top, html.qa-open .to-top, html.consent-open .to-top { display: none; }
.qa-panel {
  position: fixed; right: 16px; bottom: 84px; z-index: 150;
  width: 360px; max-width: calc(100vw - 32px);
  max-height: min(560px, calc(100vh - 110px)); max-height: min(560px, calc(100dvh - 110px));
  display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--rule); border-radius: 18px;
  box-shadow: 0 18px 44px rgba(19, 22, 28, 0.18); overflow: hidden;
}
.qa-panel[hidden] { display: none; }
.qa-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 12px 12px 16px;
  background-color: #4A70A9; background-image: var(--grad-brand); color: #FFFFFF;
}
.qa-title { font-family: var(--font-display); font-size: 1.0625rem; font-weight: 600; }
.qa-sub { font-size: 0.8125rem; color: #FFFFFF; }
.qa-close {
  width: var(--tap); height: var(--tap); flex: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; color: #FFFFFF; border: 1px solid rgba(255, 255, 255, 0.55); border-radius: var(--radius);
}
.qa-close svg { width: 18px; height: 18px; }
.qa-head :focus-visible { outline-color: #FFFFFF; }
/* One scroll region holds the conversation and the question chips, so a short window can never
   squeeze the answer to a sliver: the header and the two buttons stay pinned, the rest scrolls. */
.qa-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.qa-log { padding: 14px 14px 10px; display: flex; flex-direction: column; gap: 10px; }
.qa-bubble {
  max-width: 92%; overflow-wrap: anywhere; padding: 10px 12px; font-size: 0.9375rem; color: var(--ink);
  background: var(--bone); border: 1px solid var(--rule); border-radius: 14px 14px 14px 4px;
}
.qa-bubble--me { align-self: flex-end; background: #3B5F99; color: #FFFFFF; border-color: #3B5F99; border-radius: 14px 14px 4px 14px; }
.qa-para + .qa-para { margin-top: 0.6em; }
.qa-link { display: inline-flex; align-items: center; min-height: var(--tap); font-weight: 600; color: var(--denim); }
.qa-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 14px 12px; }
.qa-chip {
  min-height: var(--tap); padding: 6px 12px; cursor: pointer;
  font: inherit; font-size: 0.8125rem; font-weight: 500; color: var(--denim);
  background: transparent; border: 1.5px solid var(--denim-brand); border-radius: 999px;
}
.qa-foot { display: flex; gap: 8px; padding: 10px 14px; border-top: 1px solid var(--rule); }
.qa-foot .btn { flex: 1; justify-content: center; }
.qa-note { padding: 0 14px 10px; font-size: 0.75rem; color: var(--slate); text-align: center; }

@media (max-width: 600px) {
  .qa-panel { left: 0; right: 0; bottom: 0; width: auto; max-width: none; max-height: 85vh; max-height: 85dvh; border-radius: 18px 18px 0 0; }
  html.qa-open .qa-launcher { display: none; }
}

/* A phone on its side: give the answer the room the header sub-line and the footnote were using. */
@media (max-height: 480px) {
  .qa-panel { bottom: 76px; max-height: calc(100vh - 88px); max-height: calc(100dvh - 88px); }
  .qa-sub, .qa-note { display: none; }
  .qa-head { padding-top: 6px; padding-bottom: 6px; }
}

@media (hover: hover) {
  .qa-chip:hover { background: rgba(143, 171, 212, 0.18); }
  .qa-launcher:hover { transform: translateY(-2px); }
}
@media (prefers-reduced-motion: reduce) { .qa-launcher:hover { transform: none; } }
@media print { .site-drawer, .drawer-shade, .qa-launcher, .qa-panel { display: none !important; } }

/* ------------------------------------------------ hover layer (2026-09-29) */
/* Hover only where a pointer can hover, so a tap on a phone never leaves a card
   stuck "lifted". Entering uses --t-enter; the base rules carry --t-exit, so
   leaving is faster. Tags and cards are not links: no pointer cursor. */
@media (hover: hover) {
  .btn-primary:hover { box-shadow: 0 8px 18px rgba(36, 59, 97, 0.28); transform: translateY(-1px);
                        transition-duration: 200ms; transition-timing-function: var(--ease-out); }
  .btn-secondary:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(74, 112, 169, 0.16); transition-duration: 200ms; transition-timing-function: var(--ease-out); }
  .btn-light:hover { transform: translateY(-1px); box-shadow: 0 8px 18px rgba(0, 0, 0, 0.25);
                     transition-duration: 200ms; transition-timing-function: var(--ease-out); }

  .stack li:hover {
    background: color-mix(in srgb, var(--soft-blue) 45%, transparent);
    border-color: var(--denim-brand);
    box-shadow: 0 2px 6px rgba(74, 112, 169, 0.18);
    transform: translateY(-1px); transition-duration: 200ms; transition-timing-function: var(--ease-out);
  }
  .edu-logo:hover,
  .edu-row:hover .edu-logo {
    transform: translateY(-3px);
    box-shadow: var(--shadow-lift);
    border-color: var(--denim-brand);
    transition: transform var(--t-enter), box-shadow var(--t-enter), border-color var(--t-enter);
  }
  .work-item:hover {
    background: var(--card); box-shadow: var(--shadow-lift); transform: translateY(-2px);
    transition: background-color var(--t-enter), box-shadow var(--t-enter), transform var(--t-enter);
  }
  .work-item:hover::after { opacity: 0; }
  .cert-more > summary:hover::before,
  .faq-item > summary:hover::after { transform: scale(1.15); }

  .social-link:hover {
    background-color: #4A70A9; background-image: var(--grad-brand);
    color: #FFFFFF; border-color: transparent;
    transform: translateY(-2px); box-shadow: var(--shadow-lift);
    transition: background-color var(--t-enter), color var(--t-enter), transform var(--t-enter), box-shadow var(--t-enter);
  }
  .cert-more[open] > summary:hover::before,
  .faq-item[open] > summary:hover::after { transform: rotate(180deg) scale(1.15); }
}

/* ======================================================================
   RESPONSIVE — 1280 px desktop → 360 px mobile
   IA v2 rule: "Same order at 360 px — stacked, never reordered or hidden."
   Every branch present on desktop is present on mobile, in the same order.
   ====================================================================== */

@media (max-width: 900px) {
  .contact-grid { grid-template-columns: 1fr; gap: 48px; }
  .about-intro { grid-template-columns: 1fr; gap: 32px; }
  .about-portrait { order: -1; justify-self: start; max-width: 420px; }
  .hero-grid { grid-template-columns: 1fr; gap: 40px; }
  .cert-head { grid-template-columns: 1fr; }
  .cert-topics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* About stats on a phone: one card, three rows (number left, label and proof link right). */
@media (max-width: 600px) {
  .about-stats { grid-template-columns: 1fr; margin-top: 28px; }
  .about-stat {
    display: grid; grid-template-columns: 64px minmax(0, 1fr); gap: 2px 14px; align-items: center;
    padding: 16px 18px;
  }
  .about-stat + .about-stat { border-left: 0; border-top: 1px solid var(--rule); }
  .about-stat .stat-value { grid-column: 1; grid-row: 1 / span 2; font-size: 2.125rem; }
  .about-stat dt { grid-column: 2; grid-row: 1; }
  .about-stat .stat-proof { grid-column: 2; grid-row: 2; margin: 0; }
}

@media (max-width: 440px) {
  .cert-topics { grid-template-columns: 1fr; }
}

@media (max-width: 720px) {
  :root { --gutter: 20px; }

  .header-inner { min-height: 60px; padding-inline: 12px 8px; }
  .wordmark-name { font-size: 1rem; }
  .logo-mark { width: 26px; height: 26px; }

  .hero { padding: 56px 0 48px; }
  .hero-intro { max-width: 100%; }
  .hero-actions .btn { width: 100%; }

  .section { padding: 52px 0; }

  /* Work rows collapse to a single column; the order is still strongest first. */
  .work-item { grid-template-columns: 1fr; gap: 12px; padding-block: 28px; }
  .work-action { padding-top: 4px; }

  .case-header { padding: 24px 0 40px; }
  .case-links .btn { width: 100%; justify-content: center; }
  .case-nav { gap: 12px; }
  .case-nav .btn { width: 100%; }

  .ledger-row { grid-template-columns: 1fr; gap: 8px; }
  .edu-row { grid-template-columns: 72px minmax(0, 1fr); gap: 8px 20px; }
  .edu-when { grid-column: 1 / -1; }
  .cert-groups { grid-template-columns: 1fr; }
  .cert-assess ul { grid-template-columns: 1fr; }
  .code-window pre { font-size: 0.75rem; padding: 16px; }


  .faq-item > summary { gap: 14px; padding: 14px 0; }
  .faq-q { font-size: 1rem; }
  .faq-answer { padding-bottom: 18px; }
}

/* Hard 360 px check — nothing may overflow the viewport at this width. */
@media (max-width: 380px) {
  :root { --gutter: 16px; }
  .hero-intro { font-size: 1.875rem; }
  .case-title { font-size: 1.75rem; }
  .logo-mark { width: 24px; height: 24px; }
}

/* WCAG 2.3.3 — respect a reduced-motion preference. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .faq-item::details-content { transition: none; }
  .site-drawer, .drawer-shade, .drawer-switch, .drawer-switch::after { transition: none; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .btn:hover, .btn:active, .btn-light:hover, .stack li:hover,
  .edu-logo:hover, .edu-row:hover .edu-logo,
  .work-item:hover, .social-link:hover, .wordmark:hover .logo-mark,
  .theme-toggle:hover svg, .theme-toggle:hover .icon-moon, .theme-toggle:hover .icon-sun,
  .cert-more > summary:hover::before, .faq-item > summary:hover::after,
  .cert-more > summary:focus-visible::before, .faq-item > summary:focus-visible::after { transform: none; }
  .cert-more[open] > summary:hover::before,
  .faq-item[open] > summary:hover::after,
  .cert-more[open] > summary:focus-visible::before,
  .faq-item[open] > summary:focus-visible::after { transform: rotate(180deg); }
}

/* Print — the CV and case studies should survive a paper review. */
@media print {
  .site-header, .nav-toggle, .site-footer, .closing-cta, .hero-actions, .to-top { display: none; }
  body { background: var(--paper); }
  .section, .hero { border-bottom: 1px solid var(--rule); padding: 24px 0; }
  .code-window { border: 1px solid var(--rule); }
}


/* -------------------------------------------------------------- consent */
/* Cookie and embed choice (2026-09-29). Bottom-left on a wide screen so the assistant launcher keeps
   the right corner; a full-width sheet on a phone. Both buttons are .btn-secondary: Decline must not
   weigh less than Allow. The one shadow is the elevation marker for a layer that sits over content. */
.consent {
  position: fixed; left: 16px; bottom: 16px; z-index: 160; width: min(440px, calc(100vw - 32px));
  padding: 20px; background: var(--card); color: var(--ink);
  border: 1px solid var(--rule); border-radius: var(--radius-lg);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.22);
}
.consent:focus { outline: none; }
.consent:focus-visible { outline: 3px solid var(--denim-brand); outline-offset: 2px; }
.consent-title { font-family: var(--font-display); font-size: 1.0625rem; font-weight: 600; color: var(--black); }
.consent-text { margin-top: 8px; font-size: 0.9375rem; line-height: 1.5; }
.consent-actions { display: flex; gap: 12px; margin-top: 16px; }
.consent-actions .btn { flex: 1 1 0; }
/* The banner covers the bottom of the page until it is answered; the extra padding keeps the footer reachable. */
html.consent-open body { padding-bottom: var(--consent-h, 0px); }
html.consent-open .qa-launcher, html.consent-open .qa-panel { display: none; }
@media (max-width: 600px) {
  .consent { left: 12px; right: 12px; bottom: 12px; width: auto; padding: 16px; }
  .consent-text { font-size: 0.875rem; }
}
@media (max-width: 420px) { .consent-actions { flex-direction: column; } }

/* The map before it is allowed: same 4:3 box as the iframe, so nothing shifts when it loads. */
.map-gate {
  display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 10px;
  aspect-ratio: 4 / 3; min-height: 240px; padding: 24px;
  border: 1px solid var(--rule); border-radius: var(--radius-lg); background: var(--card);
}
.map-gate[hidden] { display: none; }
.map-gate-title { font-family: var(--font-display); font-size: 1.125rem; font-weight: 600; color: var(--black); }
.map-gate p:not(.map-gate-title) { max-width: 46ch; color: var(--slate); font-size: 0.9375rem; }
.map-gate .btn[hidden] { display: none; }

/* Publish copy only: honest labelling of the practice builds. */
.case-note {
  position: relative; margin-top: 20px; max-width: 64ch; padding-left: 28px;
  font-size: 0.9375rem; color: var(--slate);
}
.case-note strong { color: var(--ink); font-weight: 600; }
.case-note::before {
  content: ''; position: absolute; left: 0; top: 3px; width: 18px; height: 18px;
  background: var(--denim-brand);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 11v5M12 8h.01'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 11v5M12 8h.01'/%3E%3C/svg%3E") center / contain no-repeat;
}
.work-tag {
  font-family: var(--font-mono); font-size: 0.75rem;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--clay); margin-bottom: 6px;
}
