/* =============================================================
   PAGES — inner-page heroes, service pages, case study, blog
   ============================================================= */

/* -------------------------------------------------------------
   PAGE HERO — shorter than the home hero, same language
   ------------------------------------------------------------- */
.page-hero {
  position: relative;
  padding-top: clamp(8rem, 20vh, 12rem);
  padding-bottom: var(--section-y);
  overflow: hidden;
  isolation: isolate;
}
.page-hero__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(100% 80% at 80% 0%, var(--page-accent-glow, rgba(255,195,0,0.10)), transparent 60%),
    linear-gradient(180deg, var(--page-accent, #141414) 0%, var(--tk-black) 78%);
}
.page-hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
  gap: var(--s-6) var(--s-8);
  align-items: end;
}
@media (max-width: 900px) { .page-hero__inner { grid-template-columns: 1fr; } }

/* Breadcrumb */
.crumbs {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-bottom: var(--s-5);
}
/* Inline links in small print still need a thumb-sized hit area. */
.crumbs a { padding-block: 4px; }
.crumbs a:hover { color: var(--accent); }
.crumbs span[aria-hidden] { opacity: 0.5; }

/* -------------------------------------------------------------
   SERVICE PAGES
   Each page sets --page-accent on <body> so the hero, rules, and
   pull quotes inherit one dark tone without duplicating CSS.
   ------------------------------------------------------------- */
body[data-service="content"]     { --page-accent: var(--svc-content); }
body[data-service="ads"]         { --page-accent: var(--svc-ads); }
body[data-service="real-estate"] { --page-accent: var(--svc-realestate); }
body[data-service="commercial"]  { --page-accent: var(--svc-commercial); }
body[data-service="dfy"]         { --page-accent: var(--svc-dfy); }
body[data-service="ai"]          { --page-accent: var(--svc-ai); }

.svc-panel {
  background: var(--page-accent, var(--bg-surface));
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  padding: var(--s-7);
}
@media (max-width: 640px) { .svc-panel { padding: var(--s-5); } }

/* Included / not-included list */
.check-list { display: flex; flex-direction: column; gap: var(--s-3); }
.check-list li {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-3);
  align-items: start;
  padding-block: var(--s-2);
  border-bottom: 1px solid var(--rule-faint);
}
.check-list li:last-child { border-bottom: none; }
.check-list .glyph { padding-top: 0.35em; }
.check-list strong { display: block; font-weight: 600; }
.check-list span.d { color: var(--text-secondary); font-size: var(--fs-sm); }

/* "Who it's for" chips */
.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chip {
  padding: 0.45rem 0.95rem;
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}

/* -------------------------------------------------------------
   CASE STUDY
   ------------------------------------------------------------- */
/* The wrapper fixes the ratio, so the image has to cover rather than
   stretch to fill it. Without this any asset that is not exactly 16:9
   is distorted instead of cropped. */
.cs-hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.cs-hero__media {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--rule);
  background: var(--bg-surface);
  margin-top: var(--s-7);
}

/* Results first — the reference leads with the number, not the story. */
.results-band {
  display: grid;
  /* min-content, not a fixed floor: an equal share of the row is narrower
     than a six figure number at this size, and the band clips its
     overflow, so the digits were being cut off mid-number. Each column
     now takes at least what its own figure needs. auto-fit cannot be used
     with an intrinsic minimum, so the count comes from the data. */
  grid-template-columns: repeat(var(--result-cols, 4), minmax(min-content, 1fr));
  gap: 1px;
  /* A class, not an inline style: an inline custom property outranks
     every selector, so the breakpoints below could never collapse it. */
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.result {
  background: var(--bg-surface);
  padding: var(--s-6) var(--s-5);
}
.result__val {
  font-family: var(--font-display);
  font-weight: 800;
  /* Capped so four of the longest figures still sit in one row. */
  font-size: clamp(1.5rem, 2.9vw, 2.5rem);
  white-space: nowrap;
  line-height: 1;
  letter-spacing: var(--ls-tight);
  color: var(--accent);
}
.result__val[data-todo] {
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-weight: 400;
}
.results-band--2 { --result-cols: 2; }
.results-band--3 { --result-cols: 3; }
.results-band--4 { --result-cols: 4; }
.results-band--5, .results-band--6 { --result-cols: 3; }
@media (max-width: 900px) { .results-band { --result-cols: 2; } }
@media (max-width: 460px) { .results-band { --result-cols: 1; } }
.result__lbl {
  margin-top: var(--s-3);
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

.cs-section { display: grid; grid-template-columns: minmax(0,0.5fr) minmax(0,2fr); gap: var(--s-6) var(--s-8); padding-block: var(--s-7); border-top: 1px solid var(--rule); }
@media (max-width: 860px) { .cs-section { grid-template-columns: 1fr; gap: var(--s-4); } }
.cs-section__label { font-size: var(--fs-xs); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--accent); }
.cs-section__body p + p { margin-top: var(--s-4); }
.cs-section__body p { color: var(--text-secondary); max-width: 46rem; }

.pull-quote {
  padding: var(--s-7) 0;
  border-block: 1px solid var(--rule);
  margin-block: var(--s-7);
}
.pull-quote blockquote {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: var(--ls-tight);
  max-width: 46rem;
}
.pull-quote figcaption { margin-top: var(--s-5); font-size: var(--fs-sm); color: var(--text-muted); }

/* Next project */
.next-project {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-5);
  flex-wrap: wrap;
  padding: var(--s-7) 0;
}
.next-project__title {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-weight: 700;
  letter-spacing: var(--ls-mega);
  transition: color var(--dur-fast) var(--ease-out);
}
.next-project:hover .next-project__title { color: var(--accent); }

/* -------------------------------------------------------------
   BLOG
   ------------------------------------------------------------- */
/* Journal list: three across on desktop, and the tag filter bar is gone, so
   the grid is the only thing carrying rhythm here. */
.post-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(var(--s-5), 3vw, var(--s-6));
}
@media (max-width: 1000px) { .post-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .post-grid { grid-template-columns: 1fr; } }

.post-card {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  transition: transform var(--dur-mid) var(--ease-out);
}
.post-card:hover { transform: translateY(-6px); }

/* ---- generated covers ----
   Posts have no artwork. Each card gets a stable hue from its slug plus the
   tag set large, so an unillustrated journal still reads as designed rather
   than unfinished. A real `image` in the post front matter overrides this. */
.post-cover {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  overflow: hidden;
  background:
    radial-gradient(120% 120% at 78% 12%, hsl(var(--cover-h) 62% 22% / 0.85), transparent 62%),
    linear-gradient(152deg, var(--bg-elevated), var(--bg-surface));
  transition: transform var(--dur-slow) var(--ease-out);
}
.post-card:hover .post-cover { transform: scale(1.04); }
.post-cover__grid {
  position: absolute;
  inset: -1px;
  background-image:
    linear-gradient(to right, var(--rule) 1px, transparent 1px),
    linear-gradient(to bottom, var(--rule) 1px, transparent 1px);
  background-size: 34px 34px;
  opacity: 0.5;
}
.post-cover__mark {
  position: relative;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.4rem, 3.4vw, 2.1rem);
  letter-spacing: var(--ls-mega);
  text-transform: uppercase;
  color: var(--accent);
  mix-blend-mode: screen;
  padding-inline: var(--s-4);
  text-align: center;
}
/* Post hero reuses the same cover at article scale. */
.cs-hero__media--cover { position: relative; aspect-ratio: 21 / 9; min-height: 14rem; }
/* 21:9 is a cinema ratio. On a phone it collapses to a letterbox strip
   barely taller than the label sitting in it, which is why article covers
   read as broken rather than as images. */
@media (max-width: 760px) {
  .cs-hero__media--cover { aspect-ratio: 4 / 3; min-height: 0; }
}
.cs-hero__media--cover .post-cover__mark { font-size: clamp(1.8rem, 5vw, 3.4rem); }
.post-card__media {
  position: relative;
  aspect-ratio: 3 / 2;
  border-radius: var(--r-md);
  overflow: hidden;
  border: 1px solid var(--rule);
  background: var(--bg-surface);
}
.post-card__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out);
}
.post-card:hover .post-card__media img { transform: scale(1.04); }
.post-card__meta {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  font-size: var(--fs-xs);
  color: var(--text-muted);
  letter-spacing: 0.04em;
}
.post-card__tag { color: var(--accent); text-transform: uppercase; letter-spacing: var(--ls-wide); }
.post-card__title {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: 700;
  line-height: 1.25;
  transition: color var(--dur-fast) var(--ease-out);
}
.post-card:hover .post-card__title { color: var(--accent); }

/* Article body */
.prose { max-width: var(--maxw-prose); }
.prose > * + * { margin-top: var(--s-5); }
.prose p { color: var(--text-secondary); }
.prose h2 { font-size: var(--fs-h3); margin-top: var(--s-7); }
.prose h3 { font-size: var(--fs-h4); margin-top: var(--s-6); }
.prose ul, .prose ol { display: flex; flex-direction: column; gap: var(--s-2); padding-left: var(--s-5); }
.prose ul li { list-style: disc; color: var(--text-secondary); }
.prose ol li { list-style: decimal; color: var(--text-secondary); }
.prose a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.prose blockquote {
  border-left: 2px solid var(--accent);
  padding-left: var(--s-5);
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: 600;
  line-height: 1.35;
  color: var(--text-primary);
}
.prose code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: var(--bg-surface);
  padding: 0.15em 0.4em;
  border-radius: var(--r-sm);
}
.prose img { border-radius: var(--r-md); }
.prose hr { border: none; border-top: 1px solid var(--rule); margin-block: var(--s-7); }

/* Loading / empty states for CMS-driven lists */
.state {
  padding: var(--s-8) 0;
  text-align: center;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

/* -------------------------------------------------------------
   CONTACT
   ------------------------------------------------------------- */
.contact-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
  gap: var(--s-8);
  align-items: start;
}
@media (max-width: 900px) { .contact-grid { grid-template-columns: 1fr; } }

.contact-form { display: flex; flex-direction: column; gap: var(--s-5); }
.contact-form__row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); }
@media (max-width: 560px) { .contact-form__row { grid-template-columns: 1fr; } }

.contact-aside { display: flex; flex-direction: column; gap: var(--s-6); }
.contact-block { padding-bottom: var(--s-5); border-bottom: 1px solid var(--rule); }
.contact-block h3 {
  font-size: var(--fs-xs);
  font-family: var(--font-body);
  font-weight: 500;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--s-3);
}
.contact-block a {
  font-family: var(--font-display); font-size: var(--fs-h4); font-weight: 600;
  display: inline-block;
  /* The email and the handle are the two things a phone visitor taps. */
  padding-block: 3px;
}
.contact-block a:hover { color: var(--accent); }
.contact-block p { color: var(--text-secondary); font-size: var(--fs-sm); }

/* -------------------------------------------------------------
   404
   ------------------------------------------------------------- */
.notfound {
  min-height: 80svh;
  display: grid;
  place-content: center;
  text-align: center;
  gap: var(--s-5);
}

/* =============================================================
   PROJECTS
   ============================================================= */
/* One project per row. Four across made each one a thumbnail; at full width
   the work is the thing you actually look at, and the rows alternate so the
   eye has to travel rather than scan a column. */
.proj-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(var(--s-6), 7vw, var(--s-8));
}
.proj-tile {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(var(--s-5), 4vw, var(--s-7));
  /* The entrance animation parks the media a few percent off-axis until it
     scrolls into view. Without this the parked state pokes past the viewport
     edge on the rows that enter from the right. */
  overflow: hidden;
}
.proj-tile:nth-child(even) .proj-tile__media { order: 2; }
.proj-tile__media {
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--rule);
  background: var(--bg-surface);
}
.proj-tile__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  /* Was grayscale(1): the desaturated state read as a grey sheet over the
     work. A soft scrim dims it instead, so colour is visible before hover
     and the image simply clears. */
  transition: transform var(--dur-slow) var(--ease-out);
}
.proj-tile__media::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(200deg, rgba(10,10,10,0.55), rgba(10,10,10,0.15));
  transition: opacity var(--dur-slow) var(--ease-out);
  pointer-events: none;
}
.proj-tile:hover .proj-tile__media::after { opacity: 0; }
.proj-tile:hover .proj-tile__media img { transform: scale(1.05); }

.proj-tile__body { display: flex; flex-direction: column; gap: var(--s-3); }

@media (max-width: 860px) {
  .proj-tile { grid-template-columns: 1fr; gap: var(--s-4); }
  .proj-tile:nth-child(even) .proj-tile__media { order: 0; }
  .proj-tile__media { aspect-ratio: 16 / 11; }
}
.proj-tile__num {
  position: absolute;
  z-index: 2;
  top: var(--s-4); left: var(--s-4);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--accent);
  background: rgba(10,10,10,0.7);
  padding: 3px 8px;
  border-radius: var(--r-pill);
}
.proj-tile__title { font-family: var(--font-display); font-size: var(--fs-h2); font-weight: 700; line-height: var(--lh-tight); }
.proj-tile:hover .proj-tile__title { color: var(--accent); }
.proj-tile__lede { font-size: var(--fs-base); color: var(--text-secondary); max-width: 44ch; }
.proj-tile__go { font-size: var(--fs-sm); color: var(--accent); }

/* ---- project detail ---- */
.project-hero {
  position: relative;
  padding-top: clamp(7rem, 17vh, 10rem);
  padding-bottom: var(--s-7);
  text-align: center;
  overflow: hidden;
}
.project-hero__bg {
  position: absolute; inset: 0; z-index: -1;
  background: radial-gradient(80% 60% at 50% 0%, rgba(255,195,0,0.08), transparent 65%);
}
.go-back {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 0.5rem 1.1rem;
  margin-bottom: var(--s-6);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.go-back:hover { border-color: var(--accent); color: var(--accent); }
.project-hero__title { margin-bottom: var(--s-4); }
.project-hero__lede { max-width: 46rem; margin-inline: auto; }

.project-top {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: var(--s-5);
  /* stretch, not start: the shot then matches the combined height of the
     meta card and the properties card rather than running past them. */
  align-items: stretch;
}
@media (max-width: 900px) { .project-top { grid-template-columns: 1fr; } }

.project-shot {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--rule);
  background: var(--bg-surface);
  min-height: 22rem;
}
@media (max-width: 900px) { .project-shot { aspect-ratio: 4 / 3; min-height: 0; } }
.project-shot img { width: 100%; height: 100%; object-fit: cover; }

.project-side { display: flex; flex-direction: column; gap: var(--s-4); }
.meta-card {
  display: flex;
  flex-direction: column;
  gap: var(--s-6);
  padding: var(--s-6);
  background: var(--bg-surface);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
}
.meta-list { display: flex; flex-direction: column; }
.meta-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding-block: var(--s-4);
  border-bottom: 1px solid var(--rule);
}
.meta-row:last-child { border-bottom: none; }
.meta-row dt {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}
.meta-row dd { font-size: var(--fs-sm); font-weight: 600; text-align: right; }

/* On a phone the values here — a scope note, a list of services — are far
   longer than the card is wide. Held in two columns they squeeze the label
   into a two-line sliver and leave the value in a ragged strip beside it.
   Stacked, both get the full width. */
@media (max-width: 760px) {
  .meta-row {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--s-2);
  }
  .meta-row dd { text-align: left; }
}

/* Simple geometric glyphs — no icon font, no external request. */
.meta-ic {
  width: 15px; height: 15px;
  flex: none;
  border: 1.5px solid var(--text-muted);
  border-radius: 3px;
}
.meta-ic--user     { border-radius: 50% 50% 3px 3px; }
.meta-ic--clock    { border-radius: 50%; }
.meta-ic--target   { border-radius: 50%; border-width: 3px; }
.meta-ic--calendar { border-top-width: 4px; }

.meta-nav { display: flex; justify-content: space-between; gap: var(--s-3); }
.meta-nav__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 0.55rem 1rem;
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.meta-nav__btn:hover { border-color: var(--accent); color: var(--accent); }

/* ---- properties ticker ----
   The names are long and the card is narrow, so the list scrolls vertically
   behind a soft mask instead of wrapping into an unreadable block. */
.properties {
  padding: var(--s-6);
  background: var(--bg-surface);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
}
.properties__title {
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--s-4);
}
.properties__viewport {
  position: relative;
  height: 11.5rem;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 14%, #000 86%, transparent);
          mask-image: linear-gradient(180deg, transparent, #000 14%, #000 86%, transparent);
}
.properties__track {
  /* One list holding the items twice. Spacing is padding on each item, not
     a flex gap, so half the track height is exactly one full pass and the
     -50% loop point is seamless. */
  animation: property-roll 26s linear infinite;
}
.properties__viewport:hover .properties__track { animation-play-state: paused; }
.property {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-bottom: var(--s-4);
}
.property__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-base);
  line-height: 1.15;
}
.property__loc {
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}
/* Two identical tracks stack, so -50% lands exactly on the copy. */
@keyframes property-roll {
  from { transform: translateY(0); }
  to   { transform: translateY(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  html:not(.motion-on) .properties__viewport { height: auto; overflow: visible; -webkit-mask-image: none; mask-image: none; }
  html:not(.motion-on) .properties__track { animation: none; }
  html:not(.motion-on) .property[aria-hidden="true"] { display: none; }
}

.platforms {
  padding: var(--s-6);
  background: var(--bg-surface);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
}
.platforms__title {
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--s-4);
}
.platforms__list { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.platform {
  display: grid;
  place-content: center;
  width: 44px; height: 44px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--tk-black);
  font-size: 0;
  transition: transform var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
/* First letter stands in for a logo mark — keeps the row icon-free. */
.platform span::first-letter { font-size: 15px; font-weight: 700; }
.platform span { font-size: 15px; font-weight: 700; overflow: hidden; width: 1ch; white-space: nowrap; }
.platform:hover { transform: translateY(-2px); }
.platform--off { background: var(--bg-elevated); color: var(--text-muted); }

/* ---- reels ---- */
.reels { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-5); }
@media (max-width: 800px) { .reels { grid-template-columns: 1fr; max-width: 24rem; margin-inline: auto; } }
.reel { display: flex; flex-direction: column; gap: var(--s-3); }
.reel video, .reel .media-todo {
  position: relative;
  aspect-ratio: 9 / 16;
  width: 100%;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--rule);
  background: var(--bg-surface);
  object-fit: cover;
}
.reel figcaption { font-size: var(--fs-xs); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--text-muted); }

/* ---- photo gallery ---- */
.photo-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s-3);
}
@media (max-width: 1100px) { .photo-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .photo-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.photo {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: var(--r-md);
  overflow: hidden;
  border: 1px solid var(--rule);
  background: var(--bg-surface);
}
.photo img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out);
}
.photo:hover img { transform: scale(1.06); }

/* -------------------------------------------------------------
   MASONRY GALLERY
   Multi-column rather than a uniform grid: 71% of these photos are
   vertical 9:16 and a square grid centre-cropped every one of them.
   Columns let each frame keep its own aspect ratio.
   ------------------------------------------------------------- */
.masonry {
  column-count: 4;
  column-gap: var(--s-4);
}
@media (max-width: 1100px) { .masonry { column-count: 3; } }
/* Two columns all the way down, not one. At a single column the retail
   page ran to fifty screen-heights on a phone; two halves that, and a
   thumbnail is a target to tap into the lightbox, not something you read.
   The gap tightens so the pair still gets useful width at 320px. */
@media (max-width: 760px)  { .masonry { column-count: 2; } }
@media (max-width: 420px)  { .masonry { column-gap: var(--s-3); } }

.shot {
  break-inside: avoid;
  margin: 0 0 var(--s-4);
  border-radius: var(--r-md);
  overflow: hidden;
  border: 1px solid var(--rule);
  background: var(--bg-surface);
  cursor: zoom-in;
  position: relative;
}
.shot img {
  width: 100%;
  height: auto;
  /* Reserving the ratio stops the columns reflowing as images arrive. */
  aspect-ratio: var(--ar);
  object-fit: cover;
  display: block;
  transition: transform var(--dur-slow) var(--ease-out),
              filter var(--dur-slow) var(--ease-out);
}
.shot::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.17);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
  pointer-events: none;
}
.shot:hover img { transform: scale(1.04); }
.shot:hover::after { opacity: 1; }

/* -------------------------------------------------------------
   LIGHTBOX
   ------------------------------------------------------------- */
.lb {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: none;
  place-content: center;
  background: rgba(6, 6, 6, 0.94);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  padding: var(--s-5);
}
.lb.is-open { display: grid; }
.lb__img {
  max-width: min(92vw, 1400px);
  max-height: 86vh;
  max-height: 86dvh;
  width: auto; height: auto;
  border-radius: var(--r-md);
  object-fit: contain;
}
.lb__btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 48px; height: 48px;
  display: grid;
  place-content: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid var(--rule-strong);
  color: var(--text-primary);
  font-size: 1.1rem;
  transition: background var(--dur-fast) var(--ease-out);
}
.lb__btn:hover { background: var(--accent); color: var(--tk-black); border-color: var(--accent); }
.lb__prev { left: var(--s-5); }
.lb__next { right: var(--s-5); }
.lb__close { top: var(--s-5); right: var(--s-5); transform: none; }
.lb__count {
  position: absolute;
  bottom: var(--s-5); left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
@media (max-width: 640px) {
  .lb__btn { width: 40px; height: 40px; }
  .lb__prev { left: var(--s-3); } .lb__next { right: var(--s-3); }
}

/* -------------------------------------------------------------
   CASE STUDIES
   Each study is a band with a sticky rail carrying the number and
   sector, and a main column that reads in order: situation, build,
   result. The rail means you always know which study you are in
   even when a long one scrolls past its own heading.
   ------------------------------------------------------------- */
.legend {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-5);
  margin-top: var(--s-6);
}
@media (max-width: 820px) { .legend { grid-template-columns: 1fr; } }
.legend__item {
  padding: var(--s-5);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--bg-surface);
}
.legend__num {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--accent);
}
.legend__title {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: 700;
  margin-block: var(--s-2) var(--s-3);
}
.legend__body { font-size: var(--fs-sm); color: var(--text-secondary); }

.study__layout {
  display: grid;
  grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  gap: clamp(var(--s-5), 5vw, var(--s-8));
  align-items: start;
}
@media (max-width: 940px) { .study__layout { grid-template-columns: 1fr; gap: var(--s-5); } }

.study__rail {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  position: sticky;
  top: 7rem;
}
@media (max-width: 940px) { .study__rail { position: static; flex-direction: row; align-items: baseline; gap: var(--s-4); } }
.study__num {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(3rem, 7vw, 5.5rem);
  line-height: 0.8;
  letter-spacing: var(--ls-mega);
  color: var(--text-ghost);
}
.study__eyebrow {
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}
.study__flag {
  align-self: start;
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--r-pill);
  padding: 3px 10px;
}

.study__main { display: flex; flex-direction: column; gap: var(--s-5); }
.study__headline { max-width: 18ch; }
.study__lede { max-width: 58ch; }

.acts { display: flex; flex-direction: column; gap: 0; margin-top: var(--s-3); }
.act {
  position: relative;
  padding: var(--s-5) 0 var(--s-5) var(--s-6);
  border-top: 1px solid var(--rule);
}
.act:last-child { border-bottom: 1px solid var(--rule); }
/* The connecting spine makes the three acts read as one sequence. */
.act::before {
  content: '';
  position: absolute;
  left: 6px; top: 0; bottom: 0;
  width: 1px;
  background: var(--rule);
}
.act:first-child::before { top: 50%; }
.act:last-child::before { bottom: 50%; }
.act__step {
  position: absolute;
  left: 0; top: calc(var(--s-5) + 0.35em);
  width: 13px; height: 13px;
  border-radius: 50%;
  background: var(--bg-base);
  border: 1px solid var(--accent);
  font-size: 0;
}
.act__title {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: 700;
  margin-bottom: var(--s-2);
}
.act__body { color: var(--text-secondary); max-width: 62ch; }

.study__results { margin-top: var(--s-3); }
.study__note {
  margin-top: var(--s-4);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
/* An unfilled metric says so rather than showing a zero that reads as a result. */
.stat__await {
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 500;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}
.study__link { align-self: start; }
.study--in-progress .study__main { opacity: 0.9; }


/* -------------------------------------------------------------
   REPORT
   The case-study results panel: a grid of headline figures, with an
   optional set of rate bars beneath for studies reporting change
   rather than counts. Bars scale against the largest magnitude in
   their own set, computed at build time.
   ------------------------------------------------------------- */
.report {
  padding: clamp(var(--s-5), 3vw, var(--s-7));
  background: var(--bg-surface);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
}
.report__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-4);
  padding-bottom: var(--s-5);
  border-bottom: 1px solid var(--rule);
}
.report__title { font-family: var(--font-display); font-size: var(--fs-h4); font-weight: 700; }
.report__source { margin-top: 4px; font-size: var(--fs-xs); color: var(--text-muted); }
.report__period {
  flex: none;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--r-pill);
  padding: 4px 12px;
}
.report__sub {
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--s-4);
}

/* ---- KPI grid ---- */
.kpis {
  display: grid;
  /* min-content, not 0: an equal-width track is narrower than a seven
     digit figure at this size, and the number was being painted over by
     the next cell. Each column now takes at least the width its own
     number needs, and the slack is shared. */
  grid-template-columns: repeat(var(--kpi-cols, 4), minmax(min-content, 1fr));
  gap: 1px;
  background: var(--rule);
}
.kpis--3 { --kpi-cols: 3; }
.kpis--4 { --kpi-cols: 4; }
.kpis--5, .kpis--6 { --kpi-cols: 3; }
@media (max-width: 900px) { .kpis { --kpi-cols: 2; } }
@media (max-width: 460px) { .kpis { --kpi-cols: 1; } }
.kpi {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: var(--s-5) var(--s-4);
  background: var(--bg-surface);
}
.kpi__label {
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}
.kpi__value {
  font-family: var(--font-display);
  font-weight: 800;
  /* Capped so four of the longest figures still fit the panel side by
     side rather than forcing it wider than its container. */
  font-size: clamp(1.5rem, 3.1vw, 2.5rem);
  line-height: 1;
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.kpi--lead .kpi__value { color: var(--accent); }
.kpi__note { font-size: var(--fs-xs); color: var(--text-muted); }

/* ---- rate bars ---- */
.rates { padding-top: var(--s-6); }
.rates__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-4) clamp(var(--s-5), 4vw, var(--s-7));
}
@media (max-width: 760px) { .rates__list { grid-template-columns: 1fr; } }

.rate { display: flex; flex-direction: column; gap: 8px; }
.rate__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3); }
.rate__label { font-size: var(--fs-sm); }
.rate__value { font-family: var(--font-mono); font-size: var(--fs-sm); color: var(--accent); }
.rate__track {
  display: block;
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--bg-elevated);
  overflow: hidden;
}
.rate__fill {
  display: block;
  height: 100%;
  width: var(--pct, 0%);
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--accent), rgba(255,195,0,0.45));
  transform-origin: left;
  transform: scaleX(0);
  transition: transform 950ms var(--ease-out);
}
.rate.is-in .rate__fill { transform: scaleX(1); }
/* A fall in time-on-market is a win, so it reads as a result, not a warning. */
.rate--down .rate__fill { background: linear-gradient(90deg, #7BD88F, rgba(123,216,143,0.4)); }
.rate--down .rate__value { color: #7BD88F; }

.report__note { margin-top: var(--s-5); font-size: var(--fs-xs); color: var(--text-muted); }

/* Bars must never depend on JS to become visible. */
.no-js .rate__fill { transform: scaleX(1); }
@media (prefers-reduced-motion: reduce) {
  html:not(.motion-on) .rate__fill { transform: scaleX(1); transition: none; }
}


/* -------------------------------------------------------------
   AI CONTENT
   A showcase page rather than a gallery: pillars, alternating
   capability rows, a two-column audience split, the process, and
   an explicit statement of where this does and does not belong.
   ------------------------------------------------------------- */
.ai-hero__aside { display: flex; flex-direction: column; gap: var(--s-5); }
.ai-hero__ctas { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.ai-hero__title { max-width: 14ch; }

/* ---- three pillars ---- */
.pillars {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--rule);
  margin-top: var(--s-6);
  border-block: 1px solid var(--rule);
}
@media (max-width: 860px) { .pillars { grid-template-columns: 1fr; } }
.pillar {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  padding: var(--s-6) var(--s-5);
  background: var(--bg-base);
}
.pillar__num {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--accent);
}
.pillar__title { font-family: var(--font-display); font-size: var(--fs-h4); font-weight: 700; }
.pillar__body { color: var(--text-secondary); font-size: var(--fs-sm); }

/* ---- capabilities ---- */
.cap-lede { max-width: 52ch; margin-top: var(--s-4); }
.caps {
  display: flex;
  flex-direction: column;
  gap: clamp(var(--s-6), 6vw, var(--s-8));
  margin-top: var(--s-7);
}
.cap {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(var(--s-5), 4vw, var(--s-7));
  /* The entrance animation parks the media off-axis until it scrolls in. */
  overflow: hidden;
}
/* Alternate sides so the eye travels instead of scanning a column. */
.cap:nth-child(even) .cap__media { order: 2; }
@media (max-width: 860px) {
  .cap { grid-template-columns: 1fr; gap: var(--s-4); }
  .cap:nth-child(even) .cap__media { order: 0; }
}
.cap__media {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--rule);
  background: var(--bg-surface);
}
.cap__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cap__tag {
  position: absolute;
  top: var(--s-4); left: var(--s-4);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--accent);
  background: rgba(10, 10, 10, 0.72);
  border: 1px solid var(--rule);
  padding: 4px 10px;
  border-radius: var(--r-pill);
}
.cap__copy { display: flex; flex-direction: column; gap: var(--s-3); }
.cap__num { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--accent); }
.cap__title {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-weight: 700;
  line-height: var(--lh-tight);
}
.cap__body { color: var(--text-secondary); max-width: 44ch; }

/* ---- audience ---- */
.aud {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(var(--s-5), 4vw, var(--s-7));
  margin-top: var(--s-6);
}
@media (max-width: 860px) { .aud { grid-template-columns: 1fr; } }
.aud__col {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  padding: clamp(var(--s-5), 3vw, var(--s-6));
  background: var(--bg-surface);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
}
.aud__tag {
  align-self: start;
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--r-pill);
  padding: 4px 12px;
}
.aud__headline {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 700;
  line-height: var(--lh-tight);
}
.aud__problem { color: var(--text-secondary); }
.aud__label {
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}
.aud__list { display: flex; flex-direction: column; gap: var(--s-3); }
.aud__list li { display: flex; gap: var(--s-3); color: var(--text-secondary); font-size: var(--fs-sm); }
.aud__list .glyph { color: var(--accent); flex: none; }

/* ---- process ---- */
.flow {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--rule);
  margin-top: var(--s-6);
  border-block: 1px solid var(--rule);
}
@media (max-width: 900px) { .flow { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .flow { grid-template-columns: 1fr; } }
.flow__step {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  padding: var(--s-6) var(--s-5);
  background: var(--bg-base);
}
.flow__num {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--fs-h3);
  color: var(--text-ghost);
  line-height: 1;
}
.flow__title { font-family: var(--font-display); font-size: var(--fs-h4); font-weight: 700; }
.flow__body { color: var(--text-secondary); font-size: var(--fs-sm); }

/* ---- where it works / where it doesn't ---- */
.split {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(var(--s-5), 4vw, var(--s-6));
  margin-top: var(--s-6);
}
@media (max-width: 760px) { .split { grid-template-columns: 1fr; } }
.split__col {
  padding: clamp(var(--s-5), 3vw, var(--s-6));
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
  background: var(--bg-surface);
}
.split__title {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: 700;
  margin-bottom: var(--s-4);
}
.split__list { display: flex; flex-direction: column; gap: var(--s-3); }
.split__list li {
  position: relative;
  padding-left: var(--s-5);
  color: var(--text-secondary);
  font-size: var(--fs-sm);
}
.split__list li::before {
  content: '';
  position: absolute;
  left: 0; top: 0.55em;
  width: 10px; height: 2px;
  background: var(--accent);
}
/* The second column is a boundary, not a benefit, so it reads cooler. */
.split__col--no { background: none; }
.split__col--no .split__list li::before { background: var(--text-muted); }
.split__note {
  margin-top: var(--s-5);
  max-width: 68ch;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

/* A capability tile can hold a clip instead of a still. */
.cap__video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* -------------------------------------------------------------
   VIDEO CAROUSEL
   Centre-focused: the active slide sits forward at full size and
   plays; neighbours step back, scaled and dimmed by their
   distance from centre, which JS writes to --d.
   ------------------------------------------------------------- */
.vf {
  position: relative;
  overflow: hidden;
  padding-block: var(--s-6);
  cursor: grab;
  touch-action: pan-y;
  outline: none;
}
.vf:active { cursor: grabbing; }
.vf:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.vf__track {
  display: flex;
  align-items: center;
  gap: clamp(var(--s-4), 2vw, var(--s-5));
  will-change: transform;
  transition: transform 620ms cubic-bezier(0.22, 1, 0.36, 1);
}

.vf__slide {
  flex: none;
  width: clamp(15rem, 26vw, 22rem);
  /* Distance from centre, set by JS, drives the whole resting state. */
  --d: 3;
  opacity: calc(1 - var(--d) * 0.26);
  transform: scale(calc(1 - var(--d) * 0.06));
  transition: opacity 620ms cubic-bezier(0.22, 1, 0.36, 1),
              transform 620ms cubic-bezier(0.22, 1, 0.36, 1);
  cursor: pointer;
}
.vf__slide.is-active { opacity: 1; transform: scale(1); cursor: default; }

.vf__frame {
  position: relative;
  aspect-ratio: 9 / 16;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--rule);
  background: var(--bg-surface);
}
.vf__video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* A play mark on the resting slides, gone once one is active. */
.vf__play {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  background: rgba(10, 10, 10, 0.35);
  transition: opacity var(--dur-mid) var(--ease-out);
}
.vf__play::after {
  content: '';
  width: 0; height: 0;
  border-left: 16px solid var(--accent);
  border-block: 11px solid transparent;
  margin-left: 4px;
}
.vf__slide.is-active .vf__play { opacity: 0; pointer-events: none; }

.vf__caption {
  margin-top: var(--s-4);
  font-size: var(--fs-sm);
  color: var(--text-muted);
  transition: color var(--dur-mid) var(--ease-out);
}
.vf__slide.is-active .vf__caption { color: var(--text-primary); }

/* ---- controls ---- */
.vf__nav { display: flex; gap: var(--s-3); }
.vf__btn {
  width: 44px; height: 44px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  background: none;
  color: var(--text-primary);
  font-size: var(--fs-base);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              opacity var(--dur-fast) var(--ease-out);
}
.vf__btn:hover:not(:disabled) { background: var(--accent); border-color: var(--accent); color: var(--tk-black); }
.vf__btn:disabled { opacity: 0.3; cursor: not-allowed; }

.vf__dots { display: flex; justify-content: center; gap: 2px; margin-top: var(--s-4); }
/* The dot stays 7px, but the button around it is a 26px target: twelve
   7px hit areas in a row is not something a thumb can use. The dot is
   drawn on ::after so the visual size and the tap size stay independent. */
.vf__dot {
  flex: 0 0 auto;
  width: 26px; height: 26px;
  display: grid;
  place-items: center;
  border: 0;
  padding: 0;
  background: none;
  cursor: pointer;
}
.vf__dot::after {
  content: '';
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--rule);
  transition: background var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.vf__dot.is-on::after { background: var(--accent); transform: scale(1.5); }

@media (max-width: 640px) {
  .vf__slide { width: min(72vw, 17rem); }
}
@media (prefers-reduced-motion: reduce) {
  html:not(.motion-on) .vf__track,
  html:not(.motion-on) .vf__slide { transition: none; }
}

/* -------------------------------------------------------------
   PHONE MOCKUP
   Photorealistic handset carrying a vertical reel: titanium rail,
   physical buttons, layered inner shadows, a glare that tracks the
   cursor, and an ambient glow behind. Tilt is driven by JS writing
   --rx/--ry; everything else is CSS.
   ------------------------------------------------------------- */
.phone {
  position: relative;
  display: grid;
  place-items: center;
  padding: clamp(var(--s-5), 4vw, var(--s-7));
  perspective: 1400px;
}
.phone__glow {
  position: absolute;
  width: 68%; height: 62%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255,195,0,0.22), transparent 68%);
  filter: blur(58px);
  pointer-events: none;
}
/* The stage is exactly the device's box, so the floating cards can be
   positioned against the handset rather than against the whole column,
   which is what pushed one of them off the left edge. */
.phone__stage { position: relative; width: min(17rem, 78%); }

.phone__device {
  position: relative;
  width: 100%;
  aspect-ratio: 9 / 19.5;
  border-radius: 2.6rem;
  /* Brushed titanium rail. */
  background: linear-gradient(145deg, #3a3a3c, #6e6e73 18%, #2c2c2e 46%, #7d7d82 74%, #37373a);
  padding: 5px;
  transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transform-style: preserve-3d;
  transition: transform 420ms cubic-bezier(0.22, 1, 0.36, 1);
  box-shadow:
    0 34px 70px rgba(0,0,0,0.62),
    0 6px 18px rgba(0,0,0,0.45),
    inset 0 0 0 1px rgba(255,255,255,0.10);
}
.phone[data-tilting] .phone__device { transition: transform 90ms linear; }

.phone__frame {
  position: absolute;
  inset: 3px;
  border-radius: 2.45rem;
  box-shadow: inset 0 0 0 2px rgba(0,0,0,0.85), inset 0 0 14px rgba(0,0,0,0.6);
  pointer-events: none;
  z-index: 3;
}
.phone__btn {
  position: absolute;
  width: 2px;
  border-radius: 2px;
  background: linear-gradient(180deg, #5a5a5f, #303033);
}
.phone__btn--silent { left: -2px; top: 16%; height: 4%; }
.phone__btn--up     { left: -2px; top: 24%; height: 8%; }
.phone__btn--down   { left: -2px; top: 34%; height: 8%; }
.phone__btn--power  { right: -2px; top: 27%; height: 11%; }

.phone__screen {
  position: relative;
  width: 100%; height: 100%;
  border-radius: 2.3rem;
  overflow: hidden;
  background: var(--bg-base);
}
.phone__video { width: 100%; height: 100%; object-fit: cover; display: block; }

.phone__notch {
  position: absolute;
  top: 9px; left: 50%;
  transform: translateX(-50%);
  width: 27%; height: 17px;
  border-radius: var(--r-pill);
  background: #000;
  z-index: 2;
}
.phone__home {
  position: absolute;
  bottom: 7px; left: 50%;
  transform: translateX(-50%);
  width: 32%; height: 4px;
  border-radius: var(--r-pill);
  background: rgba(255,255,255,0.75);
  z-index: 2;
}
/* Glare sweeps as the device tilts; JS moves --gx/--gy. */
.phone__glare {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: radial-gradient(
    38% 46% at var(--gx, 50%) var(--gy, 0%),
    rgba(255,255,255,0.22), rgba(255,255,255,0.06) 42%, transparent 68%);
}

.phone__user {
  position: absolute;
  top: 34px; left: 12px;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 5px 11px 5px 5px;
  border-radius: var(--r-pill);
  background: rgba(10,10,10,0.52);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.phone__avatar {
  width: 22px; height: 22px;
  border-radius: 50%;
  background: linear-gradient(140deg, var(--accent), #8a6a00);
  box-shadow: 0 0 0 1.5px rgba(255,255,255,0.9);
}
.phone__handle { font-size: 12px; font-weight: 600; color: #fff; }
.phone__check { width: 13px; height: 13px; fill: var(--accent); }
.phone__check-tick { stroke: var(--tk-black); }

/* aspect-ratio: auto is load-bearing. .project-shot takes a 4/3 ratio
   below 900px, which pins this box to about 251px while the phone mockup
   inside it stands 856px tall. The overflow is visible, so the phone and
   its stat cards were being painted 400px past the section and landing on
   top of the reel carousel below: the phone's own video overlapping the
   carousel's. This variant sizes to its content instead. */
.project-shot--phone {
  border: 0;
  background: none;
  aspect-ratio: auto;
  min-height: 0;
  height: auto;
  overflow: visible;
}

@media (prefers-reduced-motion: reduce) {
  html:not(.motion-on) .phone__device { transition: none; transform: none; }
}

/* -------------------------------------------------------------
   ACCOUNT STATS
   Instagram-insights style cards. The number counts up and the
   sparkline draws column by column when the card arrives.
   ------------------------------------------------------------- */
.igs {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--rule);
  margin-top: var(--s-6);
  border-block: 1px solid var(--rule);
}
@media (max-width: 900px) { .igs { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .igs { grid-template-columns: 1fr; } }
.igs__card {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  padding: var(--s-6) var(--s-5);
  background: var(--bg-base);
  transition: background var(--dur-mid) var(--ease-out);
}
.igs__card:hover { background: var(--bg-surface); }
.igs__label {
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}
.igs__value {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.9rem, 3.6vw, 2.7rem);
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.igs__hint { font-size: var(--fs-xs); color: var(--text-muted); }
.igs__spark {
  display: flex;
  align-items: flex-end;
  gap: 4px;
  height: 34px;
  margin-top: var(--s-3);
}
.igs__spark i {
  flex: 1;
  height: var(--h);
  border-radius: 2px 2px 0 0;
  background: linear-gradient(180deg, var(--accent), rgba(255,195,0,0.22));
  transform-origin: bottom;
  transform: scaleY(0);
  transition: transform 720ms cubic-bezier(0.22, 1, 0.36, 1);
}
.igs__card.is-in .igs__spark i { transform: scaleY(1); }
.igs__card.is-in .igs__spark i:nth-child(1) { transition-delay: 40ms; }
.igs__card.is-in .igs__spark i:nth-child(2) { transition-delay: 90ms; }
.igs__card.is-in .igs__spark i:nth-child(3) { transition-delay: 140ms; }
.igs__card.is-in .igs__spark i:nth-child(4) { transition-delay: 190ms; }
.igs__card.is-in .igs__spark i:nth-child(5) { transition-delay: 240ms; }
.igs__card.is-in .igs__spark i:nth-child(6) { transition-delay: 290ms; }
.igs__card.is-in .igs__spark i:nth-child(7) { transition-delay: 340ms; }
.igs__card.is-in .igs__spark i:nth-child(8) { transition-delay: 390ms; }
.no-js .igs__spark i { transform: scaleY(1); }
@media (prefers-reduced-motion: reduce) { html:not(.motion-on) .igs__spark i { transform: scaleY(1); transition: none; } }

.igs__note { margin-top: var(--s-5); max-width: 62ch; font-size: var(--fs-xs); color: var(--text-muted); }

/* Carousel header holds only the arrows now. */
.vf__head { display: flex; justify-content: flex-end; }

/* A phone hero is the whole column, centred, with the pager beneath it.
   Sitting it in the two-column grid left a tall empty box beside it,
   because the aside here is only the previous/next control. */
.project-top--phone {
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  gap: var(--s-5);
}
.project-top--phone .project-shot--phone { width: 100%; }
.project-top--phone .project-side { width: min(34rem, 100%); }
.project-shot--phone .phone__stage { width: min(19rem, 62vw); }

/* -------------------------------------------------------------
   FLOATING PHONE STATS
   Cards sit around the device, drift gently on their own, and
   enter with a stagger. They live outside .phone__screen so they
   float over the bezel rather than under it.
   ------------------------------------------------------------- */
.pstats {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 4;
}
.pstat {
  position: absolute;
  min-width: 9.5rem;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: var(--s-4);
  border-radius: var(--r-md);
  background: rgba(20, 20, 20, 0.88);
  border: 1px solid var(--rule);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55);
  /* Entrance, then a slow idle drift that never fully settles. */
  opacity: 0;
  transform: translateY(14px) scale(0.96);
  transition: opacity 720ms var(--ease-out), transform 720ms var(--ease-out);
  transition-delay: calc(var(--i) * 140ms);
}
.pstat.is-in {
  opacity: 1;
  transform: none;
  animation: pstat-drift 7s ease-in-out infinite;
  animation-delay: calc(var(--i) * 900ms);
}
@keyframes pstat-drift {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -9px; }
}

.pstat--left   { right: 86%; top: 32%; }
.pstat--right  { left: 86%;  top: 14%; }
.pstat--bottom { right: 58%; bottom: 9%; }
.pstat--lower  { left: 82%;  bottom: 19%; }

.pstat__head { display: flex; align-items: center; gap: 7px; }
.pstat__dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--accent);
}
.pstat__label {
  font-size: var(--fs-xs);
  letter-spacing: 0.02em;
  color: var(--text-muted);
}
/* wrap, so a growth pill drops under its figure rather than being pushed
   out of the card. At two-up on a phone the row is ~145px wide and
   "$157.4K" plus a pill needs more than that. */
.pstat__row { display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap; }
.pstat__value {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.5rem;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.pstat__delta {
  font-size: var(--fs-xs);
  font-weight: 600;
  color: #7BD88F;
  background: rgba(123, 216, 143, 0.14);
  border-radius: var(--r-pill);
  padding: 2px 7px;
  white-space: nowrap;
}
.pstat__spark { width: 100%; height: 26px; margin-top: 2px; }
.pstat__line {
  stroke: #7BD88F;
  /* Length is set from the path itself in JS, so the draw works
     whatever the path is later changed to. */
  stroke-dasharray: var(--len, 200);
  stroke-dashoffset: var(--len, 200);
  transition: stroke-dashoffset 1400ms var(--ease-out);
  transition-delay: 420ms;
}
.pstat.is-in .pstat__line { stroke-dashoffset: 0; }

/* The device needs breathing room for the cards to sit beside it. */
.project-shot--phone .phone { padding-inline: clamp(var(--s-6), 12vw, var(--s-9)); }

@media (max-width: 900px) {
  .pstat { min-width: 8.2rem; padding: var(--s-3); }
  .pstat__value { font-size: 1.2rem; }
  .pstat--left   { right: 90%; top: 34%; }
  .pstat--right  { left: 90%;  top: 14%; }
  .pstat--bottom { right: 66%; bottom: 8%; }
  .pstat--lower  { left: 86%;  bottom: 20%; }
}
/* Below this the cards would cover the reel rather than frame it. */
/* Below this the cards cannot float beside a phone that is already
   most of the screen: they would sit on top of the reel. Hiding them was
   the wrong answer though, because the numbers are the point of the
   mockup. They come out of the stage and sit under the device as a plain
   two-up grid instead. */
@media (max-width: 560px) {
  .project-shot--phone .phone { padding-inline: var(--s-4); }
  .project-shot--phone .phone__stage {
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .phone__device { width: min(17rem, 72vw); }

  .pstats {
    position: static;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-3);
    width: 100%;
    margin-top: var(--s-6);
  }
  .pstat {
    position: static;
    inset: auto;
    min-width: 0;
    /* Visible outright rather than waiting on phone.js's observer. The
       drift and the staggered entrance are gone at this size anyway, so
       the only thing the observer was still deciding was whether the
       numbers appeared at all — and it kept deciding no. */
    opacity: 1;
    transform: none;
    animation: none;
  }
  .pstat.is-in { animation: none; }
}
@media (prefers-reduced-motion: reduce) {
  html:not(.motion-on) .pstat { opacity: 1; transform: none; transition: none; }
  html:not(.motion-on) .pstat.is-in { animation: none; }
  html:not(.motion-on) .pstat__line { transition: none; stroke-dashoffset: 0; }
}

/* -------------------------------------------------------------
   SCROLL SYNC GALLERY
   Every column is taller than the frame that shows it, and each one
   is translated so it starts flush with the top of the frame and ends
   flush with the bottom. A column holding more items is taller, so it
   has further to travel over the same scroll: the speed difference
   falls out of the item counts rather than being hand-tuned, and a
   gap at either end is impossible.
   ------------------------------------------------------------- */
.sg-pin {
  position: relative;
  margin-top: var(--s-7);
  /* Height is set in JS from the longest column, so the fastest column
     travels one pixel per pixel of scroll. Left alone it collapses to
     the gallery's own height, which is the no-script layout. */
}
.sg {
  --sg-ar: 3 / 4;
  --sg-gap: clamp(0.4rem, 0.7vw, 0.75rem);
  --sg-radius: var(--r-md);
  --sg-cols: 5;
  /* Clears the fixed nav, so no media ever passes under it. */
  --sg-top: 5.5rem;
  position: sticky;
  top: var(--sg-top);
  /* dvh, not vh: on mobile Safari 100vh is the viewport with the address
     bar hidden, so a 100vh frame is taller than what you can actually see
     and its bottom row sits behind the browser chrome. dvh tracks the
     visible height. The vh line stays as a fallback for older engines. */
  height: calc(100vh - var(--sg-top));
  height: calc(100dvh - var(--sg-top));
  padding-inline: var(--sg-gap);
}
.sg__frame {
  display: grid;
  grid-template-columns: repeat(var(--sg-cols), minmax(0, 1fr));
  gap: var(--sg-gap);
  height: 100%;
  overflow: hidden;
  align-items: start;
}
.sg__col {
  display: flex;
  flex-direction: column;
  gap: var(--sg-gap);
  /* Set by JS; the fallback keeps the top row aligned with no script. */
  transform: translate3d(0, var(--sg-y, 0px), 0);
  will-change: transform;
}
.sg__item {
  position: relative;
  margin: 0;
  aspect-ratio: var(--sg-ar);
  border-radius: var(--sg-radius);
  overflow: hidden;
  background: var(--bg-surface);
  flex: 0 0 auto;
}
.sg__media {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.sg__tag {
  position: absolute;
  left: 9px;
  bottom: 9px;
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--bg-base);
  background: var(--accent);
  border-radius: var(--r-pill);
  padding: 2px 8px;
}

@media (max-width: 1250px) { .sg { --sg-cols: 4; } }
@media (max-width: 1000px) { .sg { --sg-cols: 3; } }
@media (max-width: 700px)  { .sg { --sg-cols: 2; } }

/* On a phone the pinned run is four and a half screen-heights of scroll
   with the layout held still: on a desktop that reads as deliberate, on a
   handset it reads as a page that has stopped responding. Below the
   two-column breakpoint the gallery is an ordinary grid that scrolls with
   everything else. */
@media (max-width: 700px) {
  .sg-pin { height: auto !important; }
  .sg { position: static; height: auto; }
  .sg__frame { height: auto; overflow: visible; }
  .sg__col { transform: none !important; will-change: auto; }
}

/* Without script the columns never move, so nothing may clip or pin:
   the gallery falls back to a plain set of columns. */
.no-js .sg-pin,
.no-js .sg { position: static; height: auto; }
.no-js .sg__frame { height: auto; overflow: visible; }

@media (prefers-reduced-motion: reduce) {
  html:not(.motion-on) .sg-pin,
  html:not(.motion-on) .sg { position: static; height: auto; }
  html:not(.motion-on) .sg__frame { height: auto; overflow: visible; }
  html:not(.motion-on) .sg__col { transform: none; will-change: auto; }
}
