/* ==========================================================================
   Shannon Austin Design — shared stylesheet
   Palette: cream / navy / teal / orange / gray (matches the art-portfolio
   brand review). Type: Fraunces (display), Libre Franklin (body),
   IBM Plex Mono (labels/eyebrows).
   ========================================================================== */

:root {
  --paper: #F6F3EC;
  --paper-raised: #FCFAF5;
  --ink: #181610;
  --ink-soft: #4B4D53;      /* darkened from #6D6F76 for WCAG contrast at small sizes */
  --line: #D7D5CC;

  --accent: #AF5705;        /* text-safe orange — eyebrows, links, em, stats */
  --accent-bright: #FAA353; /* decorative-only orange — thumbnails, dark-bg text */
  --accent-soft: #FDEAD6;

  --navy: #33518C;
  --navy-soft: #E2E6EE;

  --teal: #173235;
  --teal-soft: #E1E8E8;

  --gray: #C0C3CC;
  --gray-soft: #ECEDF0;

  --max: 1160px;
}

* { box-sizing: border-box; }

html.smooth-scroll { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: 'Libre Franklin', -apple-system, BlinkMacSystemFont, sans-serif;
  line-height: 1.6;
  font-size: 17px;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--ink); }
a:focus-visible, button:focus-visible { outline: 2px solid var(--navy); outline-offset: 3px; }

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

.serif { font-family: 'Fraunces', Georgia, serif; }
.mono  { font-family: 'IBM Plex Mono', ui-monospace, SFMono-Regular, monospace; }

em { font-style: italic; color: var(--accent); }

h1, h2, h3 {
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.005em;
  margin: 0 0 0.5em;
  text-wrap: balance;
}

h1 { font-size: clamp(2.1rem, 4.6vw, 3.2rem); line-height: 1.08; }
h2 { font-size: clamp(1.5rem, 3vw, 2rem); }
h3 { font-size: 1.15rem; }

p { margin: 0 0 1.1em; max-width: 68ch; }

.wrap {
  max-width: var(--max);
  margin: 0 auto;
  padding: 0 clamp(20px, 5vw, 64px);
}

.hero-band {
  position: relative;
  background-color: var(--paper);
}
.hero-band::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: radial-gradient(circle, rgba(109,111,118,0.55) 1px, transparent 1.2px);
  background-size: 16px 16px;
  -webkit-mask-image: radial-gradient(ellipse 78% 88% at 50% 45%, #000 8%, rgba(0,0,0,0.6) 40%, rgba(0,0,0,0.14) 72%, transparent 100%);
  mask-image: radial-gradient(ellipse 78% 88% at 50% 45%, #000 8%, rgba(0,0,0,0.6) 40%, rgba(0,0,0,0.14) 72%, transparent 100%);
  pointer-events: none;
}
.hero-band > header {
  position: relative;
  z-index: 1;
  overflow: hidden;
}
.hero-spiral {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
}

/* Draws the hero spiral as if traced by a pencil in one continuous stroke,
   segment by segment. Each polyline's stroke-dasharray / stroke-dashoffset /
   animation-duration / animation-delay are set inline (computed from its
   own length, chained end-to-end) so this rule only supplies the shared
   animation behavior. The animation is held off (dashoffset stays put)
   until a small inline script adds .spiral-go right after the SVG parses
   — that keeps the "start line" tied to when the spiral actually becomes
   visible, instead of ticking silently while fonts/assets are still
   loading, which could otherwise show the spiral already half-drawn. */
.hero-spiral.spiral-go polyline {
  animation-name: spiral-draw;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
}

@keyframes spiral-draw {
  to { stroke-dashoffset: 0; }
}

@media (prefers-reduced-motion: reduce) {
  /* !important here because .hero-spiral.spiral-go polyline (two classes)
     otherwise outranks this plain .hero-spiral polyline selector, and each
     polyline's own inline stroke-dashoffset is more specific still —
     without it the spiral kept animating (just silently) for anyone who
     has reduced motion turned on. */
  .hero-spiral polyline {
    animation: none !important;
    stroke-dashoffset: 0 !important;
  }
}

/* The spiral is absolutely positioned (inset:0) to fill the whole hero
   header and uses preserveAspectRatio="none" so its 1200x600 coordinate
   space stretches to match that header's box exactly — deliberate on
   desktop, where the header stays a short, wide rectangle. Below 560px
   .grid-2 (further down) stacks to a single column, so the same header
   has to grow tall enough to hold the stacked hero text AND the squares
   graphic beneath it. Stretching the same short/wide 1200x600 coordinate
   space over that much taller, much narrower box used to smear the curve
   into a long diagonal streak bleeding behind all the stacked content.
   Rather than hiding it, give the SVG its own height via aspect-ratio
   (matching the 1200x600 viewBox, 2:1) instead of stretching to the
   header's full (now much taller) height. Its box is then always exactly
   as wide-and-short as the artwork itself, so even
   preserveAspectRatio="none" scales it uniformly with no distortion.
   On desktop the drawn curve's own endpoint — the last polyline point,
   (876.1, 308.5) in the 1200x600 viewBox — lands right where the three
   squares overlap, because the spiral is stretched over the FULL header
   and the squares happen to sit at roughly that same fraction (~73%
   across, ~51% down) of the header box. Simply anchoring the whole SVG to
   the bottom of the stacked mobile header (an earlier pass at this fix)
   got the spiral into the right general area but lost that specific
   relationship — the curve no longer actually terminates at the squares'
   meeting point, just somewhere near them.
   To restore it without distorting the artwork, the box keeps its
   natural width:100%/aspect-ratio sizing (so preserveAspectRatio="none"
   still only scales it uniformly), and is then nudged with left/top so
   that same (876.1, 308.5) point in SVG-space lands exactly on the
   squares' convergence point in page-space instead. That point was
   measured (via the squares' own rendered bounding box) at a very stable
   ~50% of the viewport width and ~75% of the header's height across the
   whole mobile range (360–540px tested) — stable enough to hardcode:
     left%  = 50 - (876.1/1200)*100*S     → the horizontal offset, as a
              plain percentage of this element's own width (S*100% =
              header width = viewport width here, so a left percentage
              and a vw-based offset are interchangeable for this
              property)
     top    = 75% of header height, minus the vertical reach of the
              aspect-ratio'd box up to that same point: (308.5/600) of
              its own height, i.e. (308.5/600)*(S*100%width/2) —
              expressed in vw since 'top' percentages resolve against the
              header's HEIGHT, not its width, so the width-based term has
              to be a viewport-width unit instead of a percentage here.
   S is the mobile-only size bump below (1.15 = 15% bigger than the
   artwork's natural width:100% / height:auto size) — made bolder/larger
   than the desktop version on request, since the finer line weight that
   reads fine stretched across a wide desktop header all but disappears
   at phone scale. Scaling S back to 1 would need left%/top's vw
   coefficient recomputed to match (see the formulas above). */
@media (max-width: 560px) {
  .hero-spiral {
    top: calc(75% - 29.56vw);
    right: auto;
    bottom: auto;
    left: -33.96%;
    width: 115%;
    height: auto;
    aspect-ratio: 1200 / 600;
  }
  .hero-spiral polyline {
    stroke-width: 3.2px;
  }
}

/* Converging squares graphic ("WHERE THE WORK COMES FROM"): each square
   slides in from a different direction while its outline draws itself in
   — the same stroke-dasharray/dashoffset pencil technique as the hero
   spiral (each is a 130x130 square, perimeter 520, so one dasharray value
   covers all three). Both the slide and the line-draw run the spiral's
   exact 2.6s duration (see .hero-spiral.spiral-go above) on LINEAR timing
   rather than an ease-out — an ease-out covers most of the distance in
   the first half of the duration and barely-perceptibly creeps the rest
   of the way, which made the squares look fully arrived well before the
   spiral actually finished drawing. Linear timing keeps the pace constant
   the whole way, so motion is still visibly happening right up to the
   same instant the spiral's last segment completes. Triggered the same
   way as the spiral — a small inline script adds .converge-go right after
   the SVG parses, so it doesn't start ticking early during font load. */
.converge-svg rect {
  stroke-dasharray: 520;
  stroke-dashoffset: 520;
}
.converge-svg .sq-orange { transform: translateX(-250px); }
.converge-svg .sq-blue { transform: translateY(-160px); }
.converge-svg .sq-teal { transform: translateX(220px); }

.converge-svg.converge-go .sq-orange {
  animation: converge-from-left 2.6s linear forwards;
}
.converge-svg.converge-go .sq-blue {
  animation: converge-from-top 2.6s linear forwards;
}
.converge-svg.converge-go .sq-teal {
  animation: converge-from-right 2.6s linear forwards;
}
@keyframes converge-from-left {
  to { transform: translateX(0); stroke-dashoffset: 0; }
}
@keyframes converge-from-top {
  to { transform: translateY(0); stroke-dashoffset: 0; }
}
@keyframes converge-from-right {
  to { transform: translateX(0); stroke-dashoffset: 0; }
}
@media (prefers-reduced-motion: reduce) {
  /* !important for the same reason as the spiral's reduced-motion rule
     above: .converge-svg.converge-go .sq-orange (etc.) is more specific
     than this plain .converge-svg rect selector and would otherwise win. */
  .converge-svg rect {
    stroke-dashoffset: 0 !important;
    transform: none !important;
    animation: none !important;
  }
}

/* ---------- nav ---------- */

.site-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 26px clamp(20px, 5vw, 64px);
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
  gap: 16px;
}

.wordmark {
  display: flex;
  align-items: center;
  gap: 14px;
  color: var(--ink);
}
.wordmark .name { font-style: italic; font-weight: 500; font-size: 21px; }
.wordmark .tag {
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--ink-soft);
  background: none;
  padding: 0;
  border-radius: 0;
}
.wordmark .logo-mark { height: 48px; width: auto; display: block; }
.wordmark .type-stack { display: flex; flex-direction: column; justify-content: center; gap: 2px; }

.nav-links { display: flex; align-items: center; gap: clamp(16px, 3vw, 36px); flex-wrap: wrap; }
.nav-links a:not(.btn) { color: var(--ink-soft); font-size: 15px; }
.nav-links a.current { color: var(--ink); font-weight: 600; }

/* Hamburger toggle — hidden by default (desktop/tablet show .nav-links
   inline); only switched on below the 560px breakpoint where .nav-links
   no longer fits beside the wordmark and would otherwise flex-wrap onto
   its own flush-left row with no separation from the logo above it. */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  padding: 0;
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
  flex-shrink: 0;
}
.nav-toggle-bar {
  display: block;
  width: 18px;
  height: 2px;
  background: var(--ink);
  border-radius: 1px;
  transition: transform 0.25s ease, opacity 0.2s ease;
}
.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) {
  .nav-toggle-bar { transition: none; }
}

/* Pencil-stroke underline on hover: a slightly wobbled hand-drawn line
   "sketches in" left-to-right, echoing the hero spiral's pencil motif
   site-wide in a small, low-key way. */
.nav-links a:not(.btn) {
  position: relative;
  padding-bottom: 2px;
}
.nav-links a:not(.btn)::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -4px;
  height: 7px;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewBox%3D%220%200%20120%2012%22%3E%3Cpath%20d%3D%22M2%207%20Q%2010%203%2C%2018%207%20T%2034%205%20T%2050%208%20T%2066%204%20T%2082%207%20T%2098%205%20T%20118%207%22%20fill%3D%22none%22%20stroke%3D%22%23AF5705%22%20stroke-width%3D%222.4%22%20stroke-linecap%3D%22round%22/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-size: 100% 100%;
  clip-path: inset(0 100% 0 0);
  transition: clip-path 0.45s cubic-bezier(0.65, 0, 0.35, 1);
  pointer-events: none;
}
.nav-links a:not(.btn):hover::after {
  clip-path: inset(0 0 0 0);
}
@media (prefers-reduced-motion: reduce) {
  .nav-links a:not(.btn)::after { transition: none; }
}

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  font-weight: 600;
  font-size: 15px;
  padding: 13px 24px;
  border-radius: 6px;
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}
.btn-primary { background: var(--navy); color: var(--paper); }
.btn-primary:hover { background: #2A4373; color: var(--paper); }
/* Outline buttons ("Resume", "See my fine art") used to just darken their
   text/border on hover while staying transparent — easy to miss. Hovering
   now fills the button solid navy with cream text instead, the same
   "infill" treatment a filled button already has, so the state change
   reads clearly rather than as a faint color shift. */
.btn-outline { background: transparent; color: var(--navy); border-color: var(--navy); }
.btn-outline:hover { background: var(--navy); color: var(--paper); border-color: var(--navy); }
/* Invert buttons sit on a dark band (cta-band is teal) and used to hover
   from cream to a barely-different off-white (--gray-soft) — almost
   invisible against the cream default. Hovering now fills with the
   brand's bright accent orange instead, which reads as a clear, deliberate
   state change against the teal backdrop (and echoes the accent color
   already used for the eyebrow/emphasis text in the same band). */
.btn-invert { background: var(--paper); color: var(--ink); }
.btn-invert:hover { background: var(--accent-bright); color: var(--ink); }

/* ---------- eyebrow / labels ---------- */

.eyebrow {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 16px;
  display: block;
}

/* ---------- availability badge ---------- */

.status-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--teal);
  background: var(--teal-soft);
  border: 1px solid var(--teal);
  padding: 7px 14px 7px 11px;
  border-radius: 999px;
  margin-bottom: 18px;
}
.status-dot {
  position: relative;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--teal);
  flex-shrink: 0;
}
.status-dot::after {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  background: var(--teal);
  opacity: 0.4;
  animation: status-pulse 2.2s ease-out infinite;
}
@keyframes status-pulse {
  0% { transform: scale(0.6); opacity: 0.45; }
  100% { transform: scale(2); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .status-dot::after { animation: none; opacity: 0; }
}

.tag {
  display: inline-block;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 5px 10px;
  border-radius: 4px;
  background: var(--gray-soft);
  color: var(--ink-soft);
}

/* ---------- sections / cards ---------- */

.section { padding: 76px 0; border-top: 1px solid var(--line); }
.section:first-of-type { border-top: none; }
.section-lede { color: var(--ink-soft); max-width: 70ch; margin: 0 0 40px; }

.card {
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 26px;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 14px 28px -18px rgba(24, 22, 16, 0.35);
}
@media (prefers-reduced-motion: reduce) {
  .card { transition: none; }
  .card:hover { transform: none; }
}

.grid { display: grid; gap: 24px; }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }

@media (max-width: 860px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 560px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  /* 20px vertical keeps the bar's height as before; 24px horizontal gives
     the logo/hamburger a bit more breathing room off the screen edges than
     a plain 20px did once the nav's old padding-zeroing inline style (see
     the <nav> tag in each page) was removed — that inline style used to
     override this rule's horizontal padding down to 0 at every breakpoint,
     which is the real reason the logo and toggle sat flush against the
     edges on phones. */
  .site-nav { padding: 20px 24px; position: relative; flex-wrap: nowrap; }
  .nav-toggle { display: flex; }

  /* .nav-links becomes a dropdown panel anchored under the nav bar,
     toggled by .nav-toggle via JS (assets/site.js initMobileNav), instead
     of flex-wrapping onto a flush-left second row under the logo. */
  .nav-links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--paper);
    border-bottom: 1px solid var(--line);
    padding: 4px 24px 20px;
    box-shadow: 0 16px 28px -16px rgba(24, 22, 16, 0.22);
    z-index: 20;
  }
  .nav-links.is-open { display: flex; }
  .nav-links a:not(.btn) {
    padding: 14px 2px;
    border-bottom: 1px solid var(--line);
    font-size: 16px;
  }
  .nav-links a:not(.btn)::after { display: none; }
  .nav-links .btn { margin-top: 14px; justify-content: center; }
}

.check-row { display: flex; align-items: flex-start; gap: 11px; }
.check-row svg { flex-shrink: 0; margin-top: 3px; }

/* ---------- case-study two-column layout (sticky TL;DR sidebar + content) ---------- */

.cs-layout {
  display: grid;
  grid-template-columns: 248px minmax(0, 1fr);
  grid-template-areas:
    "aside header"
    "aside main";
  gap: clamp(28px, 4vw, 56px) clamp(28px, 4vw, 56px);
  max-width: 1340px;
  margin: 0 auto;
  padding: 56px clamp(20px, 5vw, 64px) 0;
  align-items: start;
}
.cs-header { grid-area: header; }
.cs-main { grid-area: main; min-width: 0; }

/* .wrap elements nested anywhere inside the case-study grid already sit
   inside the padded/centered .cs-layout grid, so they should fill their
   grid cell rather than re-applying their own side padding + max-width
   (the header title block keeps its narrower max-width via inline style) */
.cs-layout .wrap { padding-left: 0; padding-right: 0; max-width: none; }

.tldr-sidebar {
  grid-area: aside;
  position: sticky;
  top: 28px;
  align-self: start;
}

@media (max-width: 960px) {
  .cs-layout {
    grid-template-columns: 1fr;
    grid-template-areas: "header" "aside" "main";
  }
  .tldr-sidebar { position: static; top: auto; margin-bottom: 8px; }
}

/* ---------- TL;DR summary (case study pages) ---------- */

.tldr {
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-left: 3px solid var(--navy);
  border-radius: 10px;
  padding: 24px 22px;
}
.tldr-label {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--navy);
  margin-bottom: 20px;
  display: block;
}
.tldr-grid { display: flex; flex-direction: column; gap: 22px; }
.tldr-grid .label {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 7px;
}
a.tldr-jump {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--ink-soft);
}
a.tldr-jump:hover { color: var(--navy); }
a.tldr-jump .arrow {
  display: inline-block;
  opacity: 0;
  transform: translateX(-3px);
  transition: opacity 0.15s ease, transform 0.15s ease;
}
a.tldr-jump:hover .arrow { opacity: 1; transform: translateX(0); }
.tldr-grid p { margin: 0; font-size: 14px; line-height: 1.55; color: var(--ink); }
.tldr-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--line); }

.section[id] { scroll-margin-top: 24px; }

/* ---------- meta strip ---------- */

.meta-strip {
  background: var(--paper-raised);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.meta-strip .wrap {
  padding: 32px clamp(20px, 5vw, 64px);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 22px;
}
.meta-strip .label {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--ink-soft);
  margin-bottom: 7px;
  text-transform: uppercase;
}
.meta-strip .value { font-weight: 600; font-size: 15px; }

/* ---------- placeholder media (swap for real screenshots) ---------- */

.shot {
  border-radius: 10px;
  background: linear-gradient(135deg, var(--gray-soft), var(--gray));
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-soft);
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-align: center;
  padding: 20px;
  min-height: 220px;
}
.shot.dark {
  background: linear-gradient(160deg, #232323, var(--teal));
  color: #EDE8E0;
}
.shot small { display: block; margin-top: 6px; color: inherit; opacity: 0.7; font-size: 10.5px; }

/* ---------- real media frames (photos/screens that replace .shot) ---------- */

.media-frame {
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--paper-raised);
}
.media-frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
.media-frame.dark { background: #15181c; border-color: #15181c; }
.media-frame.contain img { height: auto; object-fit: contain; }
.media-caption {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  margin-top: 10px;
}

/* ---------- stat chips (real metrics, e.g. usability testing results) ---------- */

.stat-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
@media (max-width: 760px) { .stat-row { grid-template-columns: 1fr; } }
.stat-chip {
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 22px;
}
.stat-chip .num {
  font-family: 'Fraunces', Georgia, serif;
  font-size: 2.1rem;
  color: var(--navy);
  line-height: 1;
  margin-bottom: 8px;
}
.stat-chip .label {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* ---------- quote card ---------- */

.quote-card {
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  border-radius: 10px;
  padding: 30px;
}
.quote-card blockquote {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-size: 1.2rem;
  line-height: 1.5;
  margin: 0 0 16px;
  color: var(--ink);
}
.quote-card .who {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--ink);
}

/* ---------- process steps ---------- */

.steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 26px; position: relative; }
.steps::before {
  content: "";
  position: absolute;
  top: 19px; left: 9%; right: 9%;
  height: 1px;
  background: var(--line);
}
.step-num {
  width: 38px; height: 38px;
  border-radius: 50%;
  background: var(--paper);
  border: 1px solid var(--navy);
  color: var(--navy);
  display: flex; align-items: center; justify-content: center;
  font-family: 'Fraunces', Georgia, serif;
  font-size: 15px;
  margin-bottom: 16px;
  position: relative;
}
@media (max-width: 760px) {
  .steps { grid-template-columns: 1fr; gap: 22px; }
  .steps::before { display: none; }
}

/* ---------- work grid (homepage) ---------- */

.work-thumb { height: 150px; border-radius: 10px; margin-bottom: 16px; overflow: hidden; display: flex; align-items: center; justify-content: center; background: var(--teal-soft); }
.work-thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.4s ease; }
.grid-4 a:hover .work-thumb img { transform: scale(1.05); }
.work-title { font-weight: 600; font-size: 16px; margin-bottom: 4px; }
.work-cat { font-size: 13px; color: var(--ink-soft); }

/* soft hover-lift on the case-study thumbnail cards */
.grid-4 a:has(.work-thumb) {
  display: block;
  border-radius: 10px;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
  will-change: transform;
}
.grid-4 a:has(.work-thumb):hover {
  transform: translateY(-4px);
  box-shadow: 0 14px 28px -18px rgba(24, 22, 16, 0.35);
}
@media (prefers-reduced-motion: reduce) {
  .grid-4 a:has(.work-thumb) { transition: none; }
  .grid-4 a:has(.work-thumb):hover { transform: none; }
}

/* ---------- cta band ---------- */

.cta-band {
  background: var(--teal);
  color: var(--paper);
  text-align: center;
  padding: 84px clamp(20px, 5vw, 64px);
}
.cta-band .eyebrow { color: var(--accent-bright); }
.cta-band h2 { color: var(--paper); }
.cta-band p { color: var(--gray); max-width: 50ch; margin: 0 auto 30px; }

/* ---------- footer ---------- */

footer.site-footer {
  padding: 40px clamp(20px, 5vw, 64px);
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: center;
  justify-content: space-between;
  color: var(--ink-soft);
  font-size: 14px;
}
footer.site-footer a { color: var(--ink-soft); }
footer.site-footer a:hover { color: var(--accent); }
.footer-links { display: flex; gap: 22px; flex-wrap: wrap; }

/* On desktop the footer is one row (space-between pushes the copyright
   left and the link cluster right); below 560px that row has no room, so
   both the outer footer and .footer-links fall back to flex-wrap with no
   further styling — the copyright drops to its own line, then the four
   links wrap into a ragged second block with uneven side/row gaps and no
   alignment, reading as chunky and unintentional rather than a designed
   stack. Centering everything as a single column, with one consistent
   gap used for both the row-to-row and link-to-link spacing, turns that
   same wrap into a tidy, deliberate block that matches how other stacked
   mobile sections on the site (e.g. the nav dropdown) present themselves. */
@media (max-width: 560px) {
  footer.site-footer {
    flex-direction: column;
    text-align: center;
    gap: 14px;
    padding: 36px 24px 44px;
  }
  .footer-links { justify-content: center; gap: 10px 22px; }
}

/* ---------- misc utility ---------- */

.stack { display: flex; flex-direction: column; gap: 14px; }
.req-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.req-list li { position: relative; padding-left: 20px; color: var(--ink-soft); max-width: 68ch; }
.req-list li::before { content: ""; position: absolute; left: 0; top: 0.65em; width: 8px; height: 1px; background: var(--accent); }
.center { text-align: center; }
.mt-0 { margin-top: 0; }
.small { font-size: 14px; }

/* ---------- breadcrumb (case study pages) ---------- */

.breadcrumb {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  margin-bottom: 18px;
}

/* ---------- section number label (case study pages) ---------- */

.sec-num {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 14px;
  display: block;
}

/* ---------- page-to-page transitions ---------- */
/* Replaces the default instant jump between pages (e.g. Next Project,
   back to all work) with a soft crossfade; the incoming page always
   loads at the top, it just fades in instead of cutting in abruptly.
   Supporting browsers (Chrome/Edge) animate this automatically; browsers
   without support simply navigate as before. */

@view-transition {
  navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.38s;
  animation-timing-function: ease;
}

/* ---------- scroll-position rail (case study pages) ---------- */
/* Injected by assets/site.js; shows which numbered section the reader
   is currently in and lets them jump between them. */

.scroll-rail {
  position: fixed;
  right: clamp(10px, 2vw, 26px);
  top: 50%;
  transform: translateY(-50%) translateX(8px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  z-index: 40;
  opacity: 0;
  transition: opacity 0.3s ease, transform 0.3s ease;
  pointer-events: none;
}
.scroll-rail.is-visible {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
  pointer-events: auto;
}
.scroll-rail-dot {
  position: relative;
  width: 9px;
  height: 9px;
  padding: 0;
  border-radius: 50%;
  border: 1.5px solid var(--gray);
  background: var(--paper-raised);
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}
.scroll-rail-dot:hover { transform: scale(1.25); border-color: var(--accent); }
.scroll-rail-dot.is-active {
  background: var(--accent);
  border-color: var(--accent);
  transform: scale(1.25);
}
.scroll-rail-dot::after {
  content: attr(data-label);
  position: absolute;
  right: 20px;
  top: 50%;
  transform: translateY(-50%);
  white-space: nowrap;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink);
  background: var(--paper-raised);
  border: 1px solid var(--line);
  padding: 4px 9px;
  border-radius: 5px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}
.scroll-rail-dot:hover::after,
.scroll-rail-dot:focus-visible::after {
  opacity: 1;
}
@media (max-width: 860px) {
  .scroll-rail { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .scroll-rail, .scroll-rail-dot { transition: none; }
}

/* ---------- drag-to-compare slider (wireframe vs. hi-fi) ---------- */

.compare-slider {
  position: relative;
  width: 100%;
  aspect-ratio: var(--compare-ratio, 1625 / 984);
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--paper-raised);
  -webkit-user-select: none;
  user-select: none;
  touch-action: pan-y;
}
.compare-slider img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
  display: block;
}
.compare-slider .compare-before {
  z-index: 1;
}
.compare-slider .compare-after {
  z-index: 2;
  clip-path: inset(0 0 0 var(--compare-pos, 50%));
}
.compare-slider .compare-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--compare-pos, 50%);
  width: 0;
  z-index: 3;
  cursor: ew-resize;
}
.compare-slider .compare-handle::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -1px;
  width: 2px;
  background: var(--paper);
  box-shadow: 0 0 0 1px rgba(24, 22, 16, 0.25);
}
.compare-slider .compare-grip {
  position: absolute;
  top: 50%;
  left: 0;
  transform: translate(-50%, -50%);
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--paper);
  border: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 6px 16px -8px rgba(24, 22, 16, 0.45);
  color: var(--navy);
}
.compare-slider .compare-tag {
  position: absolute;
  bottom: 12px;
  z-index: 4;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 5px 10px;
  border-radius: 4px;
  background: rgba(246, 243, 236, 0.92);
  color: var(--ink-soft);
  pointer-events: none;
}
.compare-slider .compare-tag.tag-before { left: 12px; }
.compare-slider .compare-tag.tag-after { right: 12px; }
