/* ============================================================================
   Brad Fitch — portfolio
   Vanilla CSS. Mobile-first. Theming is one attribute swap on <html>.
   ========================================================================== */

/* ---------------------------------------------------------------- tokens -- */

:root {
  /* Surfaces */
  --bg: #BFDCEC;              /* landing background, light */
  --bg-dim: #D4E7F2;          /* case-study route background, light */
  --surface: #E8EFF4;         /* case-study panel / modal panel */
  --surface-solid: #FFFFFF;   /* toggle track, small chrome */

  /* Ink */
  --ink: #0F2417;             /* 11.4:1 on --bg */
  --ink-muted: #3E5A48;       /* 6.0:1 on --bg-dim — used for de-emphasised chrome */

  /* Blocks */
  --block-case: #0F2417;
  --block-case-ink: #EAF2EC;  /* 16.3:1 */
  --block-products: #3BA5D0;
  --block-products-ink: #0F2417; /* 5.82:1 — white here would be 2.81:1 */

  /* Dialog panel — its own token so it does not follow the Case Studies block
     into lime when the theme flips. */
  --panel: #0F2417;
  --panel-ink: #EAF2EC;

  /* Accent */
  --accent: #F5D547;
  --accent-ink: #0F2417;      /* 11.3:1 */

  /* Geometry */
  --radius-block: 22px;
  --radius-panel: 20px;
  --radius-pill: 999px;
  --gap: clamp(0.75rem, 2.2vw, 1.25rem);
  --pad-page: clamp(1rem, 4vw, 2.75rem);
  --measure: 68ch;            /* ~65–75 characters */

  /* Motion — total landing entrance stays under 300ms */
  --dur-fast: 120ms;
  --dur: 180ms;
  --ease: cubic-bezier(0.2, 0.7, 0.3, 1);
}

[data-theme="dark"] {
  --bg: #0F2417;
  --bg-dim: #0A1A10;
  --surface: #16321F;
  --surface-solid: #EAF2EC;

  --ink: #EAF2EC;             /* 16.3:1 on --bg, 12.2:1 on --surface */
  --ink-muted: #9CB4A4;       /* 7.4:1 on --bg-dim */

  --block-case: #A3B93C;
  --block-case-ink: #0F2417;  /* 7.44:1 — white here would be 2.20:1 */
  --block-products: #3BA5D0;
  --block-products-ink: #0F2417;

  --panel: #16321F;           /* lifted off the page, 12.2:1 with --panel-ink */
  --panel-ink: #EAF2EC;
}

/* -------------------------------------------------------------- baseline -- */

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100dvh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding: var(--pad-page);
  gap: clamp(1.25rem, 3vw, 2rem);
  font-family: Arial, Helvetica, sans-serif;
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  line-height: 1.55;
  color: var(--ink);
  background: var(--bg);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

/* The case-study route dims the page behind the panel, as in the mockup. */
body.is-case { background: var(--bg-dim); }

h1, h2, h3 { line-height: 1.2; margin: 0; text-wrap: balance; }
p { margin: 0; }
img, video { display: block; max-width: 100%; }

/* Class-level `display` would otherwise beat the UA rule for [hidden], and the
   router leans on [hidden] for every view. */
[hidden] { display: none !important; }

a { color: inherit; }

.icon {
  width: 1em;
  height: 1em;
  flex: none;
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ------------------------------------------------------------ focus ring -- */
/* Ring colour is inherited per surface so it always clears 3:1 where it lands. */

:root { --focus-ring: var(--ink); }

:where(a, button, select, [tabindex]):focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Route changes move focus to the new view's heading so screen readers land in
   the right place. The heading is not interactive, so it gets no ring. */
[tabindex="-1"]:focus,
[tabindex="-1"]:focus-visible { outline: none; }

.skip-link {
  position: absolute;
  left: var(--pad-page);
  top: 0.5rem;
  z-index: 50;
  padding: 0.6rem 1rem;
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: bold;
  text-decoration: none;
  transform: translateY(-200%);
}
.skip-link:focus { transform: translateY(0); }

/* ---------------------------------------------------------------- header -- */

.site-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem 1.5rem;
}

.brand {
  font-size: clamp(1.5rem, 1.1rem + 2vw, 2.35rem);
  font-weight: bold;
  letter-spacing: -0.01em;
  text-decoration: none;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 3vw, 1.75rem);
}

.site-nav__link {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: clamp(0.95rem, 0.9rem + 0.25vw, 1.1rem);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  transition: border-color var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
}
.site-nav__link:hover { border-bottom-color: currentColor; }
.site-nav__link .icon { font-size: 1.15em; }

/* On a case-study route the header steps back, per the mockup, but stays
   above 4.5:1 against the dimmed background. */
body.is-case .site-header { color: var(--ink-muted); }
body.is-case .brand { font-size: clamp(1.05rem, 0.95rem + 0.6vw, 1.3rem); }
body.is-case .site-nav__link { font-size: 0.9rem; }

/* ----------------------------------------------------------------- views -- */

main { min-width: 0; }

.view--landing {
  display: flex;
  flex-direction: column;
  gap: clamp(1.5rem, 4vw, 2.75rem);
  height: 100%;
}

/* ----------------------------------------------------------------- intro -- */

.intro {
  max-width: var(--measure);
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.intro__lede {
  font-size: clamp(1.35rem, 1.1rem + 1.4vw, 2rem);
  letter-spacing: -0.01em;
}

.intro__body { max-width: var(--measure); }

/* ---------------------------------------------------------------- blocks -- */

.blocks {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap);
  flex: 1;
  align-content: stretch;
}

@media (min-width: 768px) {
  .blocks { grid-template-columns: 1fr 1fr; }
}

.block {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  min-height: clamp(200px, 34vh, 360px);
  padding: clamp(1.1rem, 3vw, 1.75rem);
  border: none;
  border-radius: var(--radius-block);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  /* background-color is in here so the block re-tints on the same timing as the
     body text. Fading one without the other flashes low contrast mid-swap. */
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

/* Ring drawn inside the block, where the contrast is known. */
.block:focus-visible { outline-offset: -8px; }

.block--case {
  background: var(--block-case);
  color: var(--block-case-ink);
  --focus-ring: var(--block-case-ink);
}

.block--products {
  background: var(--block-products);
  color: var(--block-products-ink);
  --focus-ring: var(--block-products-ink);
}

.block__title {
  font-size: clamp(1.25rem, 1rem + 1.2vw, 1.75rem);
  font-weight: bold;
}

.block:hover { transform: translateY(-3px); box-shadow: 0 10px 24px rgb(0 0 0 / 0.18); }
.block:active { transform: translateY(-1px); }

/* ----------------------------------------------------------------- pills -- */
/* The 2px ink border is load-bearing: #F5D547 on #3BA5D0 is only 1.94:1 and on
   #A3B93C only 1.52:1, so the fill alone would not meet WCAG 1.4.11 (3:1) for
   a control boundary. The border does. */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.42rem 0.95rem;
  border: 2px solid var(--accent-ink);
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font: inherit;
  font-size: 0.9rem;
  font-weight: bold;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}

.block:hover .pill,
.pill:hover { transform: translateY(-2px); box-shadow: 0 4px 10px rgb(0 0 0 / 0.25); }

.pill:focus-visible { --focus-ring: var(--accent-ink); }

.pill--next { font-size: clamp(0.9rem, 0.85rem + 0.3vw, 1rem); padding: 0.6rem 1.25rem; }

/* -------------------------------------------------------------- icon btn -- */

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: none;
  border-radius: var(--radius-pill);
  background: none;
  color: var(--ink);
  font-size: 1.5rem;
  line-height: 0;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.icon-btn:hover { transform: scale(1.1); }

/* ----------------------------------------------------------- case studies -- */

.view--case {
  display: flex;
  flex-direction: column;
  gap: clamp(1rem, 3vw, 1.5rem);
  padding: clamp(0.85rem, 2.5vw, 1.5rem);
  border-radius: var(--radius-panel);
  background: var(--surface);
  transition: background-color var(--dur) var(--ease);
}

/* Sticky, not scroll-locked: the page itself is the only scroll container. */
.case__bar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.35rem 0;
  background: var(--surface);
  transition: background-color var(--dur) var(--ease);
}

.case-select {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;                 /* so the pill can shrink instead of pushing
                                   the close button off a narrow screen */
  flex: 0 1 auto;
  padding: 0.5rem 0.85rem;
  border-radius: var(--radius-pill);
  background: var(--block-case);
  color: var(--block-case-ink);
  --focus-ring: var(--block-case-ink);
  max-width: min(100%, 34rem);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

.case-select__label { font-weight: bold; white-space: nowrap; }

/* Under ~480px the label goes to assistive tech only and the study name keeps
   the room. The <label> stays associated with the <select> either way. */
@media (max-width: 30em) {
  .case-select__label {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

.case-select__field {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-width: 0;
  flex: 0 1 auto;
}

/* A real <select>: native keyboard handling, native listbox, no ARIA to get
   wrong. Only the closed state is restyled. */
.case-select__input {
  appearance: none;
  -webkit-appearance: none;
  max-width: 100%;
  padding: 0 1.6rem 0 0;
  border: none;
  background: none;
  color: inherit;
  font: inherit;
  text-overflow: ellipsis;
  cursor: pointer;
}
.case-select__input option { color: #000; background: #fff; }

.case-select__caret {
  position: absolute;
  right: 0.25rem;
  font-size: 0.85em;
  pointer-events: none;
}

/* One close component everywhere. The icon is x-circle-fill, so the red disc is
   the button and the X is knocked out to the white backing: red on white is
   5.8:1, and the 2px ink ring keeps the boundary at 3:1 on the lime panel where
   the red alone would only reach 2.7:1. */
.icon-btn--close {
  flex: none;
  width: 1.95rem;
  height: 1.95rem;
  border: 2px solid #0F2417;
  background: #FFFFFF;
  color: #C1272D;
  font-size: 1.95rem;
  --focus-ring: #0F2417;
}
.icon-btn--close .icon { width: 1em; height: 1em; margin: -0.02em; }
.icon-btn--close:hover { color: #9E1F24; }
[data-theme="dark"] .icon-btn--close { --focus-ring: #EAF2EC; }

.case__body {
  display: flex;
  flex-direction: column;
  gap: clamp(1.5rem, 4vw, 2.5rem);
  width: min(100%, 72rem);
  margin-inline: auto;
  padding: clamp(0.25rem, 2vw, 1.5rem) clamp(0.25rem, 2vw, 1rem);
}

.case__head { display: flex; flex-direction: column; gap: 0.75rem; max-width: var(--measure); }
.case__title { font-size: clamp(1.6rem, 1.2rem + 2vw, 2.6rem); letter-spacing: -0.015em; }

/* A credit line, not a heading: smallest size on the page and dimmed by opacity
   rather than a second colour, so it steps back from the summary without
   introducing another value to keep in contrast. 0.7 holds 5.7:1 in light and
   6.7:1 in dark. */
.case__meta {
  margin-top: -0.35rem;
  font-size: 0.85rem;
  letter-spacing: 0.03em;
  opacity: 0.7;
}
.case__summary { font-size: clamp(1.05rem, 1rem + 0.5vw, 1.25rem); max-width: var(--measure); }

.case__section { display: flex; flex-direction: column; gap: 0.9rem; }
.case__section h2 {
  font-size: clamp(1.05rem, 1rem + 0.5vw, 1.25rem);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.case__section p { max-width: var(--measure); }

.case__list {
  margin: 0;
  padding-left: 1.15rem;
  max-width: var(--measure);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.case__foot {
  display: flex;
  justify-content: center;
  padding-bottom: 0.5rem;
}

/* ------------------------------------------------------- media placeholders -- */

.shot { margin: 0.5rem 0 0; display: flex; flex-direction: column; gap: 0.5rem; }

/* A portrait capture at full column width would tower over the prose next to it,
   so it gets a narrower cap and keeps its own ratio. */
.shot--portrait { max-width: min(100%, 30rem); }

.shot__frame {
  position: relative;
  aspect-ratio: var(--ar, 16 / 9);
  display: grid;
  place-items: center;
  overflow: hidden;
  border: 2px dashed var(--ink-muted);
  border-radius: 12px;
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}

.shot__label {
  padding: 0.5rem 0.9rem;
  font-size: 0.85rem;
  font-weight: bold;
  letter-spacing: 0.04em;
  color: var(--ink);
  text-align: center;
}

/* Media stays out of the layout until a real URL replaces the TODO marker.
   Keyed off the hydration class, not [src]: a lazy video is marked live as soon
   as it has a real URL, so it can occupy the frame and show its poster while the
   src is still pending. Hiding it until [src] existed gave it zero height, which
   meant it never intersected the viewport and never loaded at all. */
.shot:not(.shot--live) .shot__img,
.shot:not(.shot--live) .shot__video { display: none; }

.shot--live .shot__frame { border-style: solid; border-color: transparent; background: none; }
.shot--live .shot__label { display: none; }
/* Taken out of flow so the media can never stretch the frame: the frame's height
   comes from --ar alone. Each --ar is set to the asset's true ratio, so `cover`
   fills exactly and crops nothing. */
.shot--live .shot__img,
.shot--live .shot__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.shot figcaption { font-size: 0.9rem; color: var(--ink-muted); max-width: var(--measure); }

/* ----------------------------------------------------------- theme toggle -- */

.theme-dock { display: flex; }

.theme-toggle {
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  line-height: 0;
}

/* Track and knob are fixed colours rather than themed ink: the knob has to stay
   perceivable as a control in both themes (1.4.11), and a light knob on a light
   track — as the dark mockup draws it — does not. */
/* Two square cells side by side, and a knob the exact size of one cell. Every
   dimension comes off --sz and --pad, so the knob lands dead centre on whichever
   icon it covers instead of being eyeballed. */
.theme-toggle__track {
  --sz: 2rem;
  --pad: 0.3rem;
  position: relative;
  display: grid;
  grid-template-columns: var(--sz) var(--sz);
  align-items: center;
  padding: var(--pad);
  border: 2px solid #0F2417;
  border-radius: var(--radius-pill);
  background: #FFFFFF;
}

/* Absolute children are placed against the padding box, so `--pad` puts the
   knob exactly on the first grid cell. */
.theme-toggle__knob {
  position: absolute;
  top: var(--pad);
  left: var(--pad);
  width: var(--sz);
  height: var(--sz);
  border-radius: 50%;
  background: #0F2417;
  transition: transform var(--dur) var(--ease);
}
[data-theme="dark"] .theme-toggle__knob { transform: translateX(var(--sz)); }

.theme-toggle__icon {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: var(--sz);
  height: var(--sz);
  font-size: 1.05rem;
  color: #0F2417;                     /* 11.4:1 on the white track */
  transition: color var(--dur) var(--ease);
}
/* Whichever icon the knob is covering flips to the knob's contrast colour. */
.theme-toggle__icon--sun { color: #BFDCEC; }
[data-theme="dark"] .theme-toggle__icon--sun { color: #0F2417; }
[data-theme="dark"] .theme-toggle__icon--moon { color: #A3B93C; }

.theme-toggle:hover .theme-toggle__track { box-shadow: 0 3px 10px rgb(0 0 0 / 0.2); }

/* ----------------------------------------------------------------- modal -- */

.modal {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: grid;
  place-items: center;
  padding: var(--pad-page);
}

.modal__scrim {
  position: absolute;
  inset: 0;
  background: rgb(15 36 23 / 0.55);
}

.modal__panel {
  position: relative;
  width: min(1100px, 100%);
  max-height: 100%;
  overflow-y: auto;              /* the only nested scroller on the page */
  overscroll-behavior: contain;
  padding: 0 clamp(1rem, 3vw, 1.75rem) clamp(1rem, 3vw, 1.75rem);
  border-radius: var(--radius-panel);
  background: var(--panel);
  color: var(--panel-ink);
  --focus-ring: var(--panel-ink);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

/* Sticky rather than absolute: the panel is the scroller, so an absolutely
   positioned close button scrolls off the top with the content. */
.modal__bar {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  justify-content: flex-end;
  padding: clamp(1rem, 3vw, 1.75rem) 0 0.75rem;
  background: var(--panel);
  transition: background-color var(--dur) var(--ease);
}

.cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap);
}
@media (min-width: 768px) {
  /* stretch, not start: the two cards read as a pair, so they share a height
     and their action rows line up. */
  .cards { grid-template-columns: 1fr 1fr; align-items: stretch; }
}

.card {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  padding: clamp(0.9rem, 2.5vw, 1.25rem);
  border-radius: var(--radius-panel);
  background: var(--block-products);
  color: var(--block-products-ink);
  --focus-ring: var(--block-products-ink);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.card:hover { transform: translateY(-3px); box-shadow: 0 10px 24px rgb(0 0 0 / 0.25); }

.card__title { font-size: clamp(1.15rem, 1rem + 0.8vw, 1.4rem); font-weight: bold; }
.card__text { font-size: 0.95rem; }
.card__more { font-size: 0.95rem; }
.card__actions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: auto; padding-top: 0.25rem; }

/* Scoped to the placeholder state: this rule sits after the .shot--live block,
   so unscoped it would keep the dashed frame and tint on a hydrated card. */
.card .shot:not(.shot--live) .shot__frame {
  border-color: var(--block-products-ink);
  background: color-mix(in srgb, var(--block-products-ink) 12%, transparent);
}
.card .shot__label { color: var(--block-products-ink); }

/* The two product shots are different shapes (1.24 and 2.02), so in the cards
   they share one frame and letterbox onto a mat instead of each keeping its own
   height. `contain` here rather than `cover` — matching the frames must not cost
   either screenshot a crop. */
.card .shot { margin-top: 0; }
.card .shot__frame {
  aspect-ratio: 16 / 10;
  background: color-mix(in srgb, var(--block-products-ink) 10%, transparent);
  border-radius: 10px;
}
.card .shot--live .shot__img,
.card .shot--live .shot__video { object-fit: contain; }

/* -------------------------------------------------------------- lightbox -- */

/* Sits on the frame rather than covering it, so a video showing its own
   controls stays usable. The whole frame is clickable too — see scripts.js. */
.shot__expand {
  position: absolute;
  right: 0.5rem;
  bottom: 0.5rem;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  padding: 0;
  border: 2px solid var(--accent-ink);
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 0.95rem;
  line-height: 0;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
/* Revealed on hover or focus on pointer devices; always visible on touch,
   where there is no hover to reveal it. */
.shot__frame:hover .shot__expand,
.shot__expand:focus-visible { opacity: 1; }
.shot__expand:hover { transform: scale(1.08); }
@media (hover: none) {
  .shot__expand { opacity: 1; }
}

.shot__frame--tappable { cursor: zoom-in; }

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: clamp(0.75rem, 3vw, 2rem);
}

/* Darker than the dialog scrim. At 0.55 over the light theme the caption only
   reached 4.20:1; this takes it to 9.6:1, and an enlarged image wants the room
   around it quiet anyway. */
.lightbox .modal__scrim { background: rgb(15 36 23 / 0.85); }

.lightbox__panel {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  max-width: 100%;
  max-height: 100%;
  --focus-ring: #EAF2EC;
}

.lightbox__stage {
  display: flex;
  min-height: 0;
  max-width: 100%;
}

.lightbox__stage :is(img, video) {
  max-width: min(96vw, 1600px);
  max-height: 80vh;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: 10px;
  background: #000;
}

.lightbox__caption {
  max-width: 60ch;
  color: #EAF2EC;                     /* 15.4:1 on the scrim over any theme */
  font-size: 0.95rem;
  text-align: center;
  text-shadow: 0 1px 3px rgb(0 0 0 / 0.6);
}

.lightbox__close {
  position: absolute;
  top: -0.75rem;
  right: -0.75rem;
  z-index: 2;
}

@media (max-width: 30em) {
  .lightbox__close { top: -0.5rem; right: -0.25rem; }
  .lightbox__stage :is(img, video) { max-height: 70vh; }
}

/* ------------------------------------------------------------- responsive -- */

/* On small screens the header gets out of the way while a case study or the
   products modal is open, and comes back on dismiss. */
@media (max-width: 767px) {
  body.is-immersive .site-header { display: none; }
  body.is-immersive { grid-template-rows: 1fr auto; }
  .case__body { padding-inline: 0.25rem; }
  .modal { padding: 0.75rem; }
}

@media (min-width: 1200px) {
  .view--case { padding: clamp(1.25rem, 2vw, 2rem); }
}

/* ---------------------------------------------------------------- motion -- */

@media (prefers-reduced-motion: no-preference) {
  [data-stagger] {
    animation: block-in 180ms var(--ease) both;
  }
  [data-stagger="1"] { animation-delay: 0ms; }
  [data-stagger="2"] { animation-delay: 90ms; }   /* 90 + 180 = 270ms total */

  .modal__panel { animation: panel-in 180ms var(--ease) both; }

  @keyframes block-in {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
  }
  @keyframes panel-in {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .block:hover, .card:hover, .pill:hover, .block:hover .pill, .icon-btn:hover { transform: none; }
  [data-stagger] { opacity: 1; }
}

@media (prefers-contrast: more) {
  .shot__frame { border-style: solid; }
  .site-nav__link { border-bottom-color: currentColor; }
}
