/*
 * Shared site styles.
 *
 * Canonical for all six pages. Per-page theme colors (--accent, --blue,
 * etc. on case-study pages other than homemade) are overridden in a small
 * inline <style> block in that page's own <head> — see README.md.
 */
  :root {
    --bg: #F2F2F1;
    --ink: #1A1917;
    --muted: #6E6E6C;
    --line: #E2E2E0;
    --accent: #35734C;
    --accent-soft: #E4EDE7;
    --blue: #35734C;
    --blue-soft: #E4EDE7;
    --sidebar-bg: #F8F8F7;
    --card: #FFFFFF;
    /* Reading measure, and the spacing scale the case studies run on:
       24 between blocks, 40 around evidence, 72 before a new section. */
    /* A fixed px measure, not `ch`. `ch` resolves against each element's
       own font-size, so one token gave paragraphs 708px, lists 666px and
       captions 634px — every block ended on a different right edge, which
       is what read as a zigzag. 708px is what 66ch gave at body size. */
    --measure: 768px;
    /* Header, hero, metrics and body all share this one column, centred in
       the panel, so the white space either side is even and every left and
       right edge on the page lines up. */
    --column: calc(var(--measure) + 112px);
    --body: #2B2A26;
    --sp: 24px;
    --sp-fig: 40px;
    --sp-sec: 72px;
    --fig-h: 520px;
    --fig-edge: none;
    --quote-mark: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 15 11'%3E%3Cpath d='M6.1 0v3.6C4.5 4 3.6 4.9 3.5 6.3h2.3V11H0V6.6C0 2.9 2 .7 6.1 0zm8.9 0v3.6c-1.6.4-2.5 1.3-2.6 2.7H15V11H9.2V6.6C9.2 2.9 11.2.7 15 0z'/%3E%3C/svg%3E");
    --serif: 'Source Serif 4', Georgia, serif;
    --sans: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
    --mono: 'IBM Plex Mono', ui-monospace, monospace;
  }

  * { box-sizing: border-box; }

  p, li, .case-header .dek, .pull-quote, .decision-block p, .evidence-item p {
    text-wrap: pretty;
  }

  body {
    margin: 0;
    font-family: var(--sans);
    color: var(--ink);
    background: var(--bg);
    line-height: 1.6;
  }

  a { color: inherit; text-decoration: none; }

  /* ---- Skip link ----
     First focusable element on the page. The nav sits before the content in
     the DOM, so without this a keyboard user tabs through 11 links before
     reaching the article on every page. Off-screen until focused. ---- */
  .skip-link {
    position: absolute;
    left: 8px;
    top: -60px;
    z-index: 100;
    padding: 10px 16px;
    background: var(--card);
    color: var(--ink);
    border: 1px solid var(--accent);
    border-radius: 6px;
    font-size: 14px;
    transition: top 0.15s ease;
  }
  .skip-link:focus { top: 8px; outline: 2px solid var(--accent); outline-offset: 2px; }
  @media (prefers-reduced-motion: reduce) { .skip-link { transition: none; } }


  /* ---- Press feedback ----
     Apple's first principle of fluid interfaces: respond on pointer-down, not
     on release. Hover doesn't exist on touch, so before this a tap on a nav
     card produced no acknowledgement at all until the next page painted.
     Scale only — compositor-friendly, and short enough to feel like contact
     rather than an animation. ---- */
  .nav-card:active {
    transform: scale(0.985);
    transition: transform 100ms ease-out;
  }
  .mb-toggle:active,
  .read-toggle .rt-off:active {
    transform: scale(0.96);
    transition: transform 100ms ease-out;
  }
  .back-link:active,
  .mb-home:active,
  .identity .home-link:active,
  /* ---- Mobile navigation ----
     The panel was a display:none/block hard cut. It now grows out of the bar
     that revealed it and collapses back the same way — Apple's spatial
     consistency rule: if something emerges from somewhere, it should return
     there. transform-origin sits at the top edge so the panel is visibly
     anchored to the toggle rather than appearing around its own centre.

     Done with keyframes and a .nav-closing class rather than a transition,
     because display can't be transitioned reliably and an exit that doesn't
     mirror the entrance is worse than no motion at all. ---- */
  @keyframes nav-in {
    from { opacity: 0; transform: translateY(-8px) scaleY(0.97); }
    to   { opacity: 1; transform: translateY(0) scaleY(1); }
  }
  @keyframes nav-out {
    from { opacity: 1; transform: translateY(0) scaleY(1); }
    to   { opacity: 0; transform: translateY(-8px) scaleY(0.97); }
  }
  .has-mobile-nav.nav-open .sidebar-body {
    transform-origin: top center;
    animation: nav-in 220ms cubic-bezier(0.2, 0, 0, 1) both;
  }
  .has-mobile-nav.nav-closing .sidebar-body {
    display: block;
    transform-origin: top center;
    animation: nav-out 180ms cubic-bezier(0.4, 0, 1, 1) both;
  }

  @media (prefers-reduced-motion: reduce) {
    /* A gentler equivalent, not the absence of feedback: the press still
       registers and the panel still fades, it just doesn't travel. */
    .nav-card:active,
    .mb-toggle:active,
    .read-toggle .rt-off:active { transform: none; opacity: 0.7; }
    .has-mobile-nav.nav-open .sidebar-body,
    .has-mobile-nav.nav-closing .sidebar-body {
      animation-duration: 120ms;
      animation-name: nav-fade;
    }
    @keyframes nav-fade { from { opacity: 0 } to { opacity: 1 } }
  }

  /* ---- Focus states ----
     The hero tiles and the read toggle already had these; the navigation that
     appears on every page did not, and the browser default is close to
     invisible against these panels. ---- */
  .nav-card:focus-visible,
  .back-link:focus-visible,
  .identity .home-link:focus-visible,
  .identity .link:focus-visible,
  .sidebar-footer a:focus-visible,
  .case-content a:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 4px;
  }

  .shell {
    display: flex;
    min-height: 100vh;
  }

  /* ---- Fixed sidebar ---- */
  .sidebar {
    width: 320px;
    flex-shrink: 0;
    height: 100vh;
    position: sticky;
    top: 0;
    background: var(--sidebar-bg);
    padding: 32px 28px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
  }
  /* The column that .sidebar-footer's margin-top:auto pushes against. The
     footer is nested inside this wrapper (which exists so the mobile bar can
     collapse everything below it), so the wrapper — not .sidebar — has to be
     the full-height flex context. */
  .sidebar-body {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }
  /* Was an <h1> wrapping a link. The page title is the h1 now, so this is a
     plain link and carries the type itself. */
  .identity .home-link {
    display: block;
    font-family: var(--serif);
    font-weight: 500;
    font-size: 22px;
    line-height: 1.2;
    margin: 0 0 12px;
    transition: color 0.15s;
  }
  .identity .home-link:hover { color: var(--accent); }
  .identity p {
    font-size: 14px;
    line-height: 1.6;
    color: var(--muted);
    margin: 0 0 8px;
  }
  .identity .link {
    display: block;
    width: fit-content;
    font-size: 14px;
    color: var(--accent);
    border-bottom: 1px solid var(--accent);
    margin-bottom: 8px;
  }
  .header-top-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    row-gap: 8px;
    column-gap: 16px;
    margin-bottom: 16px;
  }
  .back-link {
    flex-shrink: 0;
    font-family: var(--mono);
    font-size: 11px;
    color: var(--muted);
    transition: color 0.15s;
  }
  .back-link:hover {
    color: var(--accent);
  }

  .sidebar-section {
    margin-top: 36px;
  }
  .sidebar-section .heading {
    font-family: var(--mono);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--muted);
    margin-bottom: 14px;
  }

  .nav-card {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border: 1px solid var(--line);
    border-radius: 10px;
    margin-bottom: 10px;
    background: var(--card);
    cursor: pointer;
    transition: border-color 0.15s;
  }
  .nav-card:hover { border-color: var(--blue); }
  .nav-card.active { border-color: var(--blue); border-width: 2px; background: var(--card); }
  .nav-card.disabled { opacity: 0.45; cursor: default; }

  .nav-card .swatch {
    width: 34px;
    height: 34px;
    border-radius: 8px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--serif);
    font-weight: 600;
    font-size: 15px;
    color: #fff;
  }

  .nav-card .info .name {
    font-size: 14px;
    font-weight: 600;
  }
  .nav-card .info .tag {
    font-size: 12px;
    color: var(--muted);
  }

  .coming-soon-tag {
    font-family: var(--mono);
    font-size: 9px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    background: var(--line);
    padding: 2px 6px;
    border-radius: 4px;
    margin-left: auto;
    color: var(--muted);
  }

  .sidebar-footer {
    margin-top: auto;
    padding-top: 24px;
    font-size: 12px;
    color: var(--muted);
  }
  .sidebar-footer a { color: var(--accent); }

  /* ---- Scrolling content ---- */
  .content-area {
    flex: 1;
    height: 100vh;
    display: flex;
    flex-direction: column;
    position: relative;
    background: var(--sidebar-bg);
  }

  .content-panel {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    margin: 10px 10px 10px 0;
    background: #FFFFFF;
    border-radius: 10px;
    border: 1px solid var(--line);
    overflow: hidden;
  }

  .scroll-area {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
  }

  /* The masthead runs the full panel width; only the reading column below
     is centred. */
  .case-header {
    padding: 74px 56px 8px;
  }
  .case-header .eyebrow {
    font-family: var(--mono);
    font-size: 12px;
    color: var(--accent);
    text-transform: uppercase;
    letter-spacing: 0.08em;
  }
  .case-header h1 {
    font-family: var(--serif);
    font-weight: 500;
    font-size: clamp(36px, 4.8vw, 54px);
    line-height: 1.03;
    letter-spacing: -0.026em;
    margin: 0 0 22px;
    max-width: 26ch;
  }
  /* The dek is the thesis of the case study, so it is set in ink. */
  .case-header .dek {
    font-size: 20px;
    line-height: 1.5;
    letter-spacing: -0.012em;
    color: var(--ink);
    max-width: 64ch;
    margin: 0 0 36px;
  }

  /* Sits below the hero image, not inside the header. A label column and a
     value column, one row per fact — the same 168px margin-label width the
     decision blocks and figure captions use, so it reads as the same kind
     of annotation and takes a fifth of the height a card grid did. */
  .meta-row {
    display: grid;
    grid-template-columns: 168px minmax(0, 1fr);
    column-gap: 28px;
    margin: 0 56px 56px;
    padding-top: 16px;
    border-top: 1px solid var(--ink);
  }
  .meta-item { display: contents; }
  .meta-item .label,
  .meta-item .value {
    padding: 7px 0;
    border-top: 1px solid var(--line);
  }
  .meta-item:first-child .label,
  .meta-item:first-child .value { border-top: 0; padding-top: 0; }
  .meta-item .label {
    font-family: var(--mono);
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
    margin-bottom: 0;
    line-height: 1.7;
  }
  .meta-item .value { font-size: 15px; line-height: 1.55; }


  /* Reading-length switch. Two real pages rather than an accordion, so the
     case study stays one continuous argument in whichever length you pick. */
  .read-toggle {
    display: inline-flex;
    align-items: stretch;
    margin-top: 26px;
    border: 1px solid var(--line);
    border-radius: 100px;
    overflow: hidden;
    font-family: var(--mono);
    font-size: 11.5px;
    letter-spacing: 0.02em;
  }
  .read-toggle > * { padding: 8px 16px; white-space: nowrap; }
  .read-toggle .rt-on {
    background: var(--accent);
    color: #FFFFFF;
  }
  .read-toggle .rt-off {
    color: var(--muted);
    text-decoration: none;
    transition: background 0.16s ease, color 0.16s ease;
  }
  .read-toggle .rt-off:hover { background: var(--accent-soft); color: var(--accent); }
  .read-toggle .rt-off:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

  .hero-image { padding: 0 56px 40px; }
  /* height:auto is required now that every <img> carries real width/height
     attributes: without it the attribute's height wins over the CSS width
     and the image renders at its intrinsic pixel height. */
  .hero-image img { width: 100%; border-radius: 8px; border: 0; display: block;
    height: auto;
    box-shadow: var(--fig-edge);
  }

  .case-content { max-width: var(--column); margin-inline: auto; padding: 0 56px 48px; }
  /* Pages that end on the reading column, with no footer block after it,
     keep the deeper base. */
  .case-content:last-child { padding-bottom: 120px; }

  /* Sections are chaptered by a hairline so the page chunks visibly when
     skimmed. h2/h3 are sized so each level clears the one below it —
     the sub-heading previously sat under the body size and read as a bold
     sentence. */
  /* A section opens with a short accent mark, then its number and date on
     one line. The mark is a background so ::before is free to carry the
     counter. */
  .case-content { counter-reset: sec; }
  .case-content section {
    counter-increment: sec;
    margin-bottom: var(--sp-sec);
    padding-top: 22px;
    border-top: 0;
    background-image: linear-gradient(var(--accent), var(--accent));
    background-repeat: no-repeat;
    background-size: 34px 2px;
    background-position: 0 0;
  }
  .case-content section:first-child { padding-top: 22px; border-top: none; }
  /* The last section shouldn't hold a full section gap open above the
     footer — the content padding and the footer margin already do that. */
  .case-content section:last-of-type { margin-bottom: 0; }
  .case-content section::before {
    content: counter(sec, decimal-leading-zero);
    display: inline-block;
    margin-right: 9px;
    font-family: var(--mono);
    font-size: 10.5px;
    letter-spacing: 0.1em;
    color: var(--accent);
  }
  /* Date marker above each section heading. The HomeMade story runs Apr 2025
     to Aug 2026 and the headings name the design stage, not the moment, so
     without this a reader loses the chronology. */
  /* Inline, so it shares a line with the section number above it. */
  .case-content .when-marker {
    display: inline;
    font-family: var(--mono);
    font-size: 10.5px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--muted);
    margin: 0;
  }
  .case-content .when-marker::before {
    content: "\00b7";
    color: var(--line);
    margin-right: 9px;
  }

  .case-content h2 {
    font-family: var(--serif);
    font-weight: 500;
    font-size: clamp(28px, 2.8vw, 34px);
    line-height: 1.1;
    letter-spacing: -0.022em;
    margin: 14px 0 var(--sp);
    max-width: var(--measure);
  }
  /* h4 was 18px bold sans, which is what a bolded sentence in the body
     already looks like — the third level did not read as a level. Serif
     makes it one without taking more space. */
  .case-content h3 {
    font-family: var(--serif);
    font-weight: 600;
    font-size: 21px;
    line-height: 1.28;
    letter-spacing: -0.014em;
    margin: var(--sp-fig) 0 12px;
    max-width: var(--measure);
  }
  .case-content > section > p,
  .case-content > section > ul { max-width: var(--measure); }
  .case-content p {
    font-size: 17px;
    line-height: 1.62;
    letter-spacing: -0.004em;
    color: var(--body);
    margin: 0 0 var(--sp);
  }
  .case-content p strong, .case-content li strong { font-weight: 600; color: var(--ink); }
  .case-content ul { padding-left: 20px; margin: 0 0 var(--sp); }
  .case-content li {
    margin-bottom: 10px;
    font-size: 17px;
    line-height: 1.62;
    color: var(--body);
  }

  /* No rule and no fill: the quote earns attention by size, and the air
     comes from padding. The mark is a mask so it takes the page's accent. */
  .pull-quote {
    font-family: var(--sans);
    font-style: normal;
    font-weight: 200;
    font-size: 24px;
    line-height: 1.5;
    letter-spacing: 0;
    color: var(--ink);
    border-left: 0;
    /* Left inset carries the quote mark; no right inset, so the quote ends
       on the same line as the body text above it. */
    padding: 32px 0 32px 16px;
    margin: var(--sp) 0;
    max-width: var(--measure);
  }
  .pull-quote::before {
    content: "";
    display: block;
    width: 15px;
    height: 11px;
    margin-bottom: 16px;
    background: var(--accent);
    -webkit-mask: var(--quote-mark) no-repeat center / contain;
            mask: var(--quote-mark) no-repeat center / contain;
  }
  .pull-quote cite {
    display: block;
    font-family: var(--sans);
    font-style: normal;
    font-weight: 300;
    font-size: 15px;
    line-height: 1.6;
    letter-spacing: 0.013em;
    color: var(--ink);
    margin-top: 16px;
  }

  /* A research set, not four separate testimonies: same treatment, lower
     volume. */
  .quote-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 40px; margin: var(--sp) 0;
                max-width: var(--measure); }
  .quote-grid .pull-quote { margin: 0; padding: 24px 0 24px 16px; font-size: 19px; max-width: none; }
  .quote-grid .pull-quote::before { width: 13px; height: 9px; margin-bottom: 12px; }
  @media (max-width: 700px) { .quote-grid { grid-template-columns: 1fr; } }

  /* An aside whose label sits out in the margin beside the text, so a
     reader can scan the labels down the page as a spine of arguments.
     Paragraphs are pushed to column 2 in CSS so the markup stays flat. */
  .decision-block {
    display: grid;
    grid-template-columns: 168px 1fr;
    column-gap: 28px;
    margin: var(--sp-fig) 0;
    padding: 20px 0 4px;
    max-width: var(--measure);
    border-top: 1px solid var(--line);
  }
  .decision-block .tag {
    grid-column: 1;
    grid-row: 1;
    font-family: var(--mono);
    font-size: 14px;
    line-height: 1.5;
    letter-spacing: 0;
    color: var(--blue);
    padding-top: 1px;
  }
  .decision-block p { grid-column: 2; max-width: none; }
  .decision-block p:last-child { margin-bottom: 0; }
  @media (max-width: 700px) {
    .decision-block { grid-template-columns: 1fr; }
    .decision-block .tag { grid-column: 1; margin-bottom: 10px; }
    .decision-block p { grid-column: 1; }
  }

  .evidence-item { display: grid; grid-template-columns: 28px 1fr; gap: 14px; margin-bottom: 18px;
                   max-width: var(--measure); }
  .evidence-item .num { font-family: var(--serif); font-style: italic; font-size: 18px; color: var(--blue); }
  .evidence-item p { margin: 0; max-width: none; }

  .img-row { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; margin: var(--sp-fig) 0; }
  @media (max-width: 800px) { .img-row { grid-template-columns: 1fr; } }
  .img-row img { width: 100%; border-radius: 8px; border: 0; box-shadow: var(--fig-edge);
    height: auto;
  }
  .img-row .cap {
    position: relative;
    font-family: var(--sans); font-weight: 300;
    font-size: 15px; line-height: 1.6; letter-spacing: 0.013em;
    color: var(--muted); margin-top: 16px; padding: 0 32px 0 16px;
  }
  .img-row .cap::before {
    content: ""; position: absolute; left: 0; top: 5px;
    width: 1px; height: 15px; background: var(--accent);
  }

  figure { margin: var(--sp-fig) 0; }
  figure img {
    width: 100%;
    height: auto;
    /* Several exports are portrait enough to render over 900px tall in this
       column - one hit 1,176px, taller than most viewports, so it filled the
       screen and broke the reading rhythm. Capping the height and letting the
       width follow keeps a figure to something you can take in at a glance. */
    max-height: var(--fig-h);
    width: auto;
    max-width: 100%;
    margin-inline: auto;
    border-radius: 8px;
    border: 0;
    box-shadow: var(--fig-edge);
    display: block;
  }
  /* Paired images share a row and are already half-width; letting them size
     independently would leave the pair visibly mismatched. */
  figure:has(img + img) img { width: 100%; max-height: none; }
  figure:has(img + img) { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
  /* Opt out of the 2-up grid where images are wide banners or full pages
     that become unreadable at half width. */
  figure.stacked { display: flex; flex-direction: column; gap: 14px; }
  figure:has(img + img) figcaption { grid-column: 1 / -1; }
  @media (max-width: 700px) { figure:has(img + img) { grid-template-columns: 1fr; } }
  /* A caption is readable prose, not a label: 15px, only two under body
     size, anchored by a short accent tick rather than a rule. */
  figcaption {
    position: relative;
    font-family: var(--sans);
    font-weight: 300;
    font-size: 15px;
    line-height: 1.6;
    letter-spacing: 0.013em;
    color: var(--muted);
    margin-top: 16px;
    padding: 0 0 0 16px;
    max-width: var(--measure);
  }
  figcaption::before {
    content: "";
    position: absolute;
    left: 0;
    top: 5px;
    width: 1px;
    height: 15px;
    background: var(--accent);
  }

  .lessons-list { list-style: disc; padding-left: 20px; max-width: var(--measure); margin: 0 0 var(--sp); }
  .lessons-list li { padding: 0; border-top: 0; margin-bottom: 10px; font-size: 17px; line-height: 1.62; }

  /* ---- Role entries (work-experience.html) ---- */
  .role + .role {
    margin-top: 40px;
    padding-top: 40px;
    border-top: 1px solid var(--line);
  }
  .role-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 4px 16px;
  }
  .role-header h3 { margin: 0; font-size: 17px; }
  .role-dates {
    font-family: var(--mono);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--muted);
    white-space: nowrap;
  }
  .role-location { font-size: 13px; color: var(--muted); margin: 4px 0 16px; }
  .role ul { margin: 0; }

  /* ---- Label/value list (work-experience.html core competencies) ---- */
  .skills-list { margin: 0; }
  .skills-row {
    display: grid;
    grid-template-columns: 200px 1fr;
    gap: 8px 24px;
    padding: 16px 0;
    border-top: 1px solid var(--line);
  }
  .skills-row:first-child { border-top: none; padding-top: 0; }
  .skills-row dt {
    font-family: var(--mono);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--muted);
  }
  .skills-row dd { margin: 0; font-size: 15.5px; }

  /* Links out to the case study for a role, where one exists. */
  .role-links {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 22px;
    margin: 18px 0 0;
  }
  .role-links .link { margin-bottom: 0; }
  @media (max-width: 700px) {
    .skills-row { grid-template-columns: 1fr; gap: 4px; }
  }

  .mockup-note {
    font-family: var(--mono);
    font-size: 11.5px;
    color: var(--muted);
    background: var(--accent-soft);
    padding: 12px 18px;
    border-radius: 4px;
    margin: 0 56px 40px;
  }

  /* ---- Compact bar shown only once the sidebar collapses (see
     assets/js/mobile-nav.js). Hidden at every width where the full
     sidebar is on screen. ---- */
  .mobile-bar { display: none; }

  @media (max-width: 860px) {
    .shell { flex-direction: column; }
    .sidebar {
      width: 100%; height: auto; position: static;
      border-right: none; border-bottom: 1px solid var(--line);
      padding: 0;
    }
    .content-area { height: auto; }
    .case-header, .hero-image, .case-content { padding-left: 24px; padding-right: 24px; }
    .mockup-note { margin-left: 24px; margin-right: 24px; }

    /* The panel's left margin is 0 on desktop so it sits flush against the
       sidebar. Once the sidebar moves above it, that reads as the card
       falling off the left edge while the right keeps its inset. */
    .content-panel { margin: 10px; }

    .sidebar-body { padding: 24px 35px 28px; }

    /* Only collapse once the script has confirmed it can also reopen it. */
    .sidebar.has-mobile-nav .mobile-bar {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 16px;
      padding: 0 35px;
      min-height: 56px;
      background: var(--sidebar-bg);
    }
    .sidebar.has-mobile-nav .sidebar-body { display: none; }
    .sidebar.has-mobile-nav.nav-open .sidebar-body { display: block; }

    .mb-home {
      font-family: var(--serif);
      font-size: 19px;
      font-weight: 500;
      color: var(--ink);
      text-decoration: none;
      /* Fills the bar height so the tap target clears 44px without
         padding the visible text away from the baseline. */
      display: flex;
      align-items: center;
      align-self: stretch;
      transition: color 0.15s;
    }
    .mb-home:hover { color: var(--accent); }
    .mb-home:focus-visible {
      outline: 2px solid var(--accent);
      outline-offset: 2px;
      border-radius: 4px;
    }
    .mb-toggle {
      display: flex;
      align-items: center;
      gap: 9px;
      align-self: stretch;
      padding: 0 4px 0 12px;
      min-height: 44px;
      background: none;
      border: 0;
      cursor: pointer;
      font-family: var(--mono);
      font-size: 12px;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      color: var(--muted);
    }
    .mb-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    .mb-toggle-icon {
      width: 16px; height: 2px;
      background: currentColor;
      position: relative;
    }
    .mb-toggle-icon::before,
    .mb-toggle-icon::after {
      content: ""; position: absolute; left: 0;
      width: 16px; height: 2px; background: currentColor;
    }
    .mb-toggle-icon::before { top: -5px; }
    .mb-toggle-icon::after  { top: 5px; }
    .nav-open .mb-toggle-icon { background: transparent; }
    .nav-open .mb-toggle-icon::before { top: 0; transform: rotate(45deg); }
    .nav-open .mb-toggle-icon::after  { top: 0; transform: rotate(-45deg); }

    /* The bar already carries the name, so the identity block would show it
       twice over the moment the panel opens. */
    .sidebar.has-mobile-nav .identity .home-link { display: none; }
    .sidebar.has-mobile-nav .identity { padding-top: 4px; }

    /* ---- Touch targets. These sit at 15-23px tall on desktop, which is
       fine for a cursor and too small for a thumb. ---- */
    .sidebar-footer { line-height: 2.4; }
    .sidebar-footer a { display: inline-block; padding: 4px 0; }
    .identity .link { display: inline-block; padding: 6px 0; }
    .back-link { display: inline-block; padding: 8px 0; }

    /* 11px captions are legible on a desktop monitor and not on a phone. */
    figure figcaption { font-size: 12.5px; }
    .eyebrow, .nav-card .tag { font-size: 12.5px; }
  }

  /* ---- Inline SVG diagrams (figure > svg). Inert unless the pattern exists. ---- */
  figure.diagram {
    background: #FFFFFF;
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: 26px 22px 16px;
    overflow-x: auto;
  }
  figure.diagram svg { width: 100%; min-width: 520px; height: auto; display: block; }
  @media (max-width: 700px) {
    figure.diagram { padding: 18px 14px 12px; }
    /* Below the SVG's own floor the figure becomes a horizontal scroller.
       Say so, rather than leaving it looking like a cropped image. */
    figure.diagram::before {
      content: "Scroll to see the full diagram →";
      display: block;
      position: sticky;
      left: 0;
      margin-bottom: 12px;
      font-family: var(--mono);
      font-size: 11.5px;
      color: var(--muted);
    }
  }
  /* The diagram carries its own 22px frame, so its caption needs that
     subtracted to finish on the same right edge as every other caption. */
  figure.diagram figcaption { margin-top: 16px; padding-top: 0; border-top: 0;
                              max-width: calc(var(--measure) - 44px); }
  .dgm-label { font-family: var(--sans); font-size: 13px; font-weight: 600; fill: currentColor; }
  .dgm-sub   { font-family: var(--sans); font-size: 11.5px; fill: currentColor; opacity: 0.6; }
  .dgm-edge  { font-family: var(--mono); font-size: 11px; fill: currentColor; opacity: 0.7; }
  .dgm-accent{ font-family: var(--mono); font-size: 11px; fill: var(--accent); }
  .dgm-head  { font-family: var(--mono); font-size: 10px; letter-spacing: 0.09em; fill: currentColor; opacity: 0.55; }
  .dgm-box   { fill: none; stroke: currentColor; stroke-width: 1.25; opacity: 0.85; }
  .dgm-flow  { stroke: currentColor; stroke-width: 1.25; fill: none; opacity: 0.7; }
  .dgm-flow-a{ stroke: var(--accent); stroke-width: 1.75; fill: none; }
  .dgm-fill-a{ fill: var(--accent); }
  .dgm-stroke-a{ stroke: var(--accent); }

  /* Paired with assets/js/protect-images.js — suppresses the long-press save
     sheet on iOS and the drag ghost. Friction only; see that file. */
  img {
    -webkit-touch-callout: none;
    -webkit-user-drag: none;
    user-select: none;
  }

  /* ---- Tall figures ----
     A portrait screenshot at the shared 520px cap costs half a screen and
     leaves a wide empty gutter beside it. Floating it right with the prose
     alongside was the obvious fix and it measured worse: the paragraphs
     between these figures run 39-86 words, so the text never fills the
     figure's height and every float leaves dead space under it. Capping
     portrait figures lower is what actually buys the height back. ---- */
  .case-content > section > figure.aside img { max-height: 420px; }

  /* A portrait figure leaves a wide empty gutter beside it and then adds
     its caption underneath. Moving the caption into that gutter costs no
     height at all and reads as a plate label rather than a footnote.
     Needs the room, so it stacks again below 900px. */
  /* 1060px, not 900: the caption column is a fixed 168px, so in the
     900-1050 band it took 39% of the figure and squeezed the screenshot to
     265px wide. Below this the caption goes back under the image. */
  @media (min-width: 1060px) {
    .case-content > section > figure.aside {
      display: grid;
      /* 168px + 28px is the same margin-label column the decision blocks
         use, so a caption beside a figure reads as the same kind of note.
         justify-content:start keeps the image next to its caption instead
         of pinned to the far right with a hole in the middle. */
      grid-template-columns: 168px auto;
      column-gap: 28px;
      justify-content: start;
      align-items: start;
    }
    .case-content > section > figure.aside img {
      grid-column: 2;
      grid-row: 1;
      margin-inline: 0;
      /* A definite height. The width/height attributes give the element an
         aspect-ratio, so a fixed height means the browser can size the
         column before the image loads — with `auto` the column is 0 wide
         until then and the caption visibly snaps from full width. */
      height: 420px;
      width: auto;
    }
    .case-content > section > figure.aside figcaption {
      grid-column: 1;
      grid-row: 1;
      margin-top: 4px;
      max-width: none;
      padding-right: 0;
    }
  }

  /* ---- State comparison ----
     A pair of screenshots showing two states of one thing. Side by side
     halves both; here they share a frame and you flip between them, so the
     only thing that moves is the thing that actually changed. Built from
     <figure data-compare="Label A|Label B"> with two <img> children —
     without the attribute the pair stays a plain side-by-side.
     §1 feedback on press, §7 nothing repositions, §11 opacity only. ---- */
  .fig-compare { display: block !important; grid-template-columns: none !important; }
  .fig-switch {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    margin: 0 0 14px;
    border: 1px solid var(--line);
    border-radius: 100px;
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: 0.02em;
  }
  .fig-switch button {
    border: 0;
    background: transparent;
    color: var(--muted);
    padding: 6px 15px;
    border-radius: 100px;
    cursor: pointer;
    font: inherit;
    white-space: nowrap;   /* labels like "In Sidekicker" were wrapping */
    transition: color 120ms ease, background 160ms ease;
  }
  .fig-switch button[aria-pressed="true"] { background: var(--accent); color: #FFFFFF; }
  .fig-switch button:hover[aria-pressed="false"] { color: var(--ink); }
  .fig-switch button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .fig-switch button:active { transform: scale(0.97); }

  /* An explicit full-width track: with an `auto` column the images size
     the column and the column sizes the images, and the pair collapses to
     a couple of hundred pixels. */
  .fig-stage {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    /* Both frames are stacked in one cell and the images are lazy, so
       nothing reserves the height until they decode. Every pair on the
       site hits the height cap, so holding it here removes the shift. */
    min-height: var(--fig-h);
  }
  .fig-stage img {
    grid-area: 1 / 1;
    width: auto !important;
    max-width: 100%;
    max-height: var(--fig-h) !important;
    opacity: 0;
    transition: opacity 200ms cubic-bezier(0.2, 0, 0, 1);
  }
  .fig-stage img.on { opacity: 1; }

  /* ---- End of a case study ----
     The page used to stop dead. §16 wayfinding: every screen answers
     "where can I go next". ---- */
  /* Sits outside .case-content so it spans the panel, as it did before the
     reading column was centred. */
  .case-end {
    margin: 40px 0 0;
    padding: 52px 56px 60px;
    background: var(--ink);
    color: #F2F2F1;
    border-radius: 4px 4px 0 0;
  }
  .case-end .ce-label {
    font-family: var(--mono);
    font-size: 10.5px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #9A9893;
    margin-bottom: 14px;
  }
  .case-end .ce-title {
    font-family: var(--serif);
    font-weight: 500;
    font-size: 30px;
    line-height: 1.14;
    letter-spacing: -0.018em;
    color: #F7F7F5;
    margin: 0 0 8px;
  }
  .case-end .ce-sub {
    font-size: 16.5px;
    line-height: 1.55;
    color: #B4B2AD;
    margin: 0 0 26px;
    max-width: 52ch;
  }
  .case-end .ce-links { display: flex; gap: 12px; flex-wrap: wrap; }
  .case-end a {
    display: inline-block;
    padding: 10px 18px;
    border-radius: 100px;
    font-size: 14px;
    font-weight: 500;
    letter-spacing: -0.006em;
    background: #F2F2F1;
    color: var(--ink);
    transition: transform 100ms ease-out, background 150ms ease;
  }
  .case-end a.ghost { background: transparent; color: #F2F2F1; box-shadow: inset 0 0 0 1px #4A4844; }
  .case-end a:hover { background: #FFFFFF; }
  .case-end a.ghost:hover { background: rgba(255, 255, 255, 0.07); }
  .case-end a:active { transform: scale(0.97); }

  /* ---- Reveal on scroll ----
     Compositor-only properties. The script adds .reveal itself, so with
     JS off nothing is ever hidden. ---- */
  .reveal { opacity: 0; transform: translateY(12px); will-change: transform, opacity; }
  .reveal.in {
    opacity: 1;
    transform: none;
    transition: opacity 560ms cubic-bezier(0.2, 0, 0, 1),
                transform 560ms cubic-bezier(0.2, 0, 0, 1);
  }

  @media (prefers-reduced-motion: reduce) {
    .reveal, .reveal.in { opacity: 1; transform: none; transition: none; }
    .fig-stage img, .fig-switch button, .case-end a { transition: none; }
  }
  @media (prefers-contrast: more) {
    .fig-switch { border-width: 2px; }
  }

  @media (max-width: 860px) {
    /* Label above value once there isn't room for two columns. */
    .meta-row { grid-template-columns: minmax(0, 1fr); margin-left: 24px; margin-right: 24px; }
    .meta-item .label { padding-bottom: 0; }
    .meta-item .value { padding-top: 2px; border-top: 0; }
    .meta-item:first-child .value { padding-top: 2px; }
    .pull-quote { font-size: 20px; padding: 24px 0 24px 16px; }
    .quote-grid .pull-quote { font-size: 18px; padding: 16px 0 16px 16px; }
    /* No negative margins now it sits outside .case-content — those made
       it wider than the panel and it was being clipped. */
    .case-end { margin-left: 0; margin-right: 0; padding-left: 24px; padding-right: 24px; }
  }

  /* ---- One composition, at every width ----
     The reading column centres itself, but the masthead used to stay flush
     left, so the moment the panel grew past its design width the two drifted
     apart: 34px of indent at 1280, 144px at 1499, 200px on an ultrawide.
     Giving both the same bound and the same axis holds the composition
     together at any size. 950px is the panel width this was drawn against,
     so below it nothing binds and nothing moves. */
  .case-header,
  .hero-image { max-width: 950px; margin-inline: auto; }
  .meta-row   { max-width: 838px; margin: 0 auto 56px; }
  .case-end   { max-width: 950px; margin: 40px auto 0; }
