/* ==========================================================================
   PROJECT-DETAIL.CSS
   Shared design system for the 4 case-study pages
   (nextgen-ai.html, pinghub.html, pao.html, tooyenplus.html).

   Why this file exists: those 4 pages were each built with their own
   embedded <style> block, copy-pasted from one another and then tweaked
   per project. Diffing all 4 showed ~90% of each block was byte-identical
   (tokens, hero/factbox/TOC/lightbox/gallery/lesson-card/etc.) — this file
   is that shared ~90%, so it's now written once instead of 4 times.

   How each page still uses it:
     <link rel="stylesheet" href="../styles/main.css">           (site chrome)
     <link rel="stylesheet" href="../styles/project-detail.css"> (this file)
     <style> ... </style>                                        (page-only overrides)
   <main class="case-study case-study--{project}"> — the bare "case-study"
   class is what this file's selectors target; the "--{project}" class is
   kept only so each page's local <style> block still has something to
   scope its own overrides to, and so the existing per-page <script> block
   (unchanged, not covered by this refactor) keeps working as-is.

   Included here: any component whose CSS was identical (or differed only
   in a value that a clear majority of the 4 pages shared) across 2+ files.
   Left OUT (still local to one page's own <style> block): components used
   by exactly one page — PingHub's persona cards, PAO's permission table,
   TooYen+'s process stepper/iteration-compare, NextGen AI's plain-list —
   since there's no actual duplication to remove for those.
   ========================================================================== */

/* ---------------------------------------------------
   DESIGN TOKENS
   Byte-identical across all 4 pages before this refactor.
--------------------------------------------------- */
:root {
  --ty-bg: #FBF7EE;
  --ty-surface: #FFFFFF;
  --ty-border: #EBE0D0;
  --ty-text: #3A231C;
  --ty-text-sub: #6E5A51;
  --ty-text-disabled: #AA9A90;
  --ty-accent: #D65B3E;
  --ty-accent-dark: #B8492F;
  --ty-accent-2: #E5A754;
  --ty-shadow-soft: 0 24px 48px -28px rgba(58, 35, 28, 0.35);
  --ty-shadow-card: 0 10px 24px -16px rgba(58, 35, 28, 0.25);
  --ty-radius-sm: 10px;
  --ty-radius-md: 20px;
  --ty-radius-lg: 32px;
  --ty-font-display: "Fraunces", Georgia, serif;
  --ty-font-body: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
  --ty-font-mono: "JetBrains Mono", "SFMono-Regular", Consolas, monospace;
  --ty-maxw: 1120px;
  --ty-edge: clamp(20px, 5vw, 64px);
}

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* ---------------------------------------------------
   BASE — was ".case-study--{project} { ... }" x4, identical
   each time apart from the class name. Scoped under the new
   shared ".case-study" class every page's <main> now also carries.
--------------------------------------------------- */
.case-study {
  background: var(--ty-bg);
  color: var(--ty-text);
  font-family: var(--ty-font-body);
  font-size: 16px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}
.case-study img { max-width: 100%; display: block; }
.case-study a { color: inherit; text-decoration: none; }
.case-study h1, .case-study h2, .case-study h3, .case-study h4 {
  font-family: var(--ty-font-display); margin: 0; font-weight: 600;
}
.case-study p { margin: 0 0 1em; color: var(--ty-text-sub); }
.case-study ul { margin: 0; padding: 0; list-style: none; }
.case-study section { position: relative; }
.case-study ::selection { background: var(--ty-accent-2); color: var(--ty-text); }

.wrap {
  max-width: var(--ty-maxw);
  margin: 0 auto;
  padding-left: var(--ty-edge);
  padding-right: var(--ty-edge);
}

.eyebrow {
  font-family: var(--ty-font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ty-text-sub);
}

/* Signature: price-tag label */
.tag-label {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--ty-accent-2);
  color: var(--ty-text);
  font-family: var(--ty-font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 7px 16px 7px 22px;
  clip-path: polygon(14px 0, 100% 0, 100% 100%, 14px 100%, 0 50%);
  margin-bottom: 20px;
}
.tag-label::before {
  content: '';
  position: absolute;
  left: 7px; top: 50%;
  width: 4px; height: 4px;
  background: var(--ty-bg);
  border-radius: 50%;
  transform: translateY(-50%);
}

/* ---------------------------------------------------
   ON-THIS-PAGE SIDE TOC
--------------------------------------------------- */
/* left (not right) so its position tracks the content column's actual
   right edge — .wrap centers at 50% and is capped at --ty-maxw, so its
   right edge sits at "50% + maxw/2" once the viewport is wide enough to
   stop clamping .wrap to 100%. A fixed `right: 20px` instead put the toc
   a constant distance from the *viewport* edge regardless of where the
   content edge actually was, which is what let it drift on top of hero
   text at ~1000–1300px viewports (content edge and toc both live in that
   same 20px-from-edge zone there). */
.toc-side {
  position: fixed;
  top: 50%;
  left: calc(50% + (var(--ty-maxw) / 2) + 32px);
  transform: translateY(-50%);
  z-index: 90;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.toc-side a {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  padding: 6px 4px;
  font-family: var(--ty-font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ty-text-disabled);
  transition: color 0.2s ease;
}
.toc-side a::before {
  content: '';
  flex-shrink: 0;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--ty-text-disabled);
  transition: background 0.2s ease, transform 0.2s ease;
}
.toc-side a:hover { color: var(--ty-text); }
.toc-side a.is-active { color: var(--ty-accent); }
.toc-side a.is-active::before { background: var(--ty-accent); transform: scale(1.5); }
/* Below this, the gutter to the right of the (now maxed-out at --ty-maxw)
   content column is narrower than the toc's own ~95px + the 32px gap
   above needs, so it would start overlapping content again — hide it
   instead of letting it collide, same as the pre-existing mobile case.
   Below 1401px, .scroll-progress (styles/main.css) is what shows instead
   as the wayfinding/progress cue — keep the two breakpoints in sync. */
@media (max-width: 1400px) {
  .toc-side { display: none; }
}

/* ---------------------------------------------------
   HERO
--------------------------------------------------- */
.case-study .hero { padding: 56px 0 88px; overflow: hidden; }
/* Image-left / text-right on every page (order:-1 on .hero__art wins
   regardless of each page's own DOM order — pinghub/pao/tooyenplus all
   write text first, image second in HTML, same as before this changed;
   only NextGen AI's markup already had the image first). Column ratio
   (0.95fr/1.05fr, image slightly narrower than text) also matches
   NextGen AI now, previously the shared default here (1.1fr/0.9fr) was
   only used by pinghub/pao/tooyenplus while NextGen AI overrode it. */
.hero__grid {
  display: grid;
  grid-template-columns: 0.95fr 1.05fr;
  gap: 56px;
  align-items: center;
}
.hero__art { order: -1; }
@media (max-width: 860px) {
  .hero__grid { grid-template-columns: 1fr; gap: 48px; }
}
/* Common value across pinghub/pao/tooyenplus (3 of 4 pages); NextGen AI's
   longer title needs a smaller clamp, overridden locally on that page. */
.hero h1 { font-size: clamp(2.4rem, 6vw, 4rem); line-height: 1.02; letter-spacing: -0.01em; }
.hero h1 .accent { color: var(--ty-accent); }

/* Descriptor line under the hero h1 — currently used by NextGen AI and
   PingHub; kept here (not page-local) since it's a reusable pattern, not
   project-specific content. */
.hero__subtitle {
  font-family: var(--ty-font-display);
  font-size: clamp(1.05rem, 2vw, 1.3rem);
  font-weight: 500;
  color: var(--ty-text-sub);
  margin: 10px 0 0;
  max-width: 42ch;
}
/* Common value across pinghub/pao/tooyenplus; NextGen AI overrides to 48ch
   locally (its tagline sentence runs longer). */
.hero__tagline { font-size: 1.1rem; max-width: 46ch; margin-top: 18px; }

/* Common max-width across pinghub/pao/tooyenplus; NextGen AI overrides to
   460px locally (its Role/Status fact values wrap across more text). */
.factbox {
  margin-top: 24px;
  border-top: 2px solid var(--ty-text);
  width: 100%;
  max-width: 100%;
}
.factbox__head {
  font-family: var(--ty-font-display);
  font-weight: 700;
  font-size: 1.05rem;
  padding: 12px 18px;
  border-bottom: 6px solid var(--ty-text);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.factbox__head span { font-family: var(--ty-font-mono); font-size: 0.65rem; color: var(--ty-text-sub); font-weight: 500; }
.fact-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 9px 18px;
  border-bottom: 1px dashed var(--ty-border);
  font-size: 0.86rem;
}
.fact-row:last-child { border-bottom: none; }
.fact-row .label { white-space: nowrap; color: var(--ty-text-sub); font-family: var(--ty-font-mono); font-size: 0.72rem; letter-spacing: 0.04em; text-transform: uppercase; }
.fact-row .leader { flex: 1; border-bottom: 2px dotted var(--ty-border); margin-bottom: 5px; }
.fact-row .value { white-space: nowrap; font-weight: 600; text-align: right; }
/* Rows whose value is a phrase, not a short label — allowed to wrap so the
   factbox can't overflow its frame. Used by NextGen AI and PingHub. */
.fact-row .value.is-long { white-space: normal; }

/* PingHub-only prototype link, styled consistently with the tag system —
   kept here since it reuses shared tokens, not because 2+ pages use it. */
.proto-link {
  display: inline-block;
  margin-top: 18px;
  font-family: var(--ty-font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.03em;
  color: var(--ty-accent);
  border-bottom: 1px solid var(--ty-accent);
  padding-bottom: 2px;
}
.proto-link:hover { color: var(--ty-accent-dark); border-color: var(--ty-accent-dark); }

/* Wraps .hero__art + .factbox as ONE column-2 grid item, stacked
   vertically. Without this, a bare .factbox sitting as a 3rd direct
   child of .hero__grid (which only defines 2 columns) falls through to
   a new implicit row under column 1 instead of sitting beside/under the
   image — that's what was producing the empty gap next to the phone
   shot. Stacking factbox under the image inside one column also means
   its width now matches the image's column width exactly. */
.hero__side { display: flex; flex-direction: column; gap: 24px; }
.hero__art { position: relative; display: flex; justify-content: center; }
/* Common (non-rotated, 260px) version across nextgen-ai/pinghub/pao;
   TooYen+ overrides locally (rotated 240px phone + its own blob/magnet). */
.hero__phone {
  position: relative;
  z-index: 1;
  /* width: 260px; */
  /* border-radius: 24px;
  box-shadow: var(--ty-shadow-soft); */
  /* border: 6px solid var(--ty-surface); */
  overflow: hidden;
  /* background: var(--ty-surface); */
}

/* ---------------------------------------------------
   SECTION generic
--------------------------------------------------- */
.case-study .section { padding: 80px 0; border-top: 1px solid var(--ty-border); }
.section__head { max-width: 62ch; margin-bottom: 44px; }
.section__head h2 { font-size: clamp(1.6rem, 3.4vw, 2.4rem); line-height: 1.12; }
.section__lede { font-size: 1.05rem; margin-top: 14px; }
/* Used by nextgen-ai/pinghub/pao; TooYen+ uses inline styles for its own
   ad-hoc sub-headings instead, so this rule is simply unused there. */
.subhead { font-size: 1.15rem; margin: 0 0 12px; }

.case-study [data-reveal] { opacity: 0; transform: translateY(18px); transition: opacity 0.6s ease, transform 0.6s ease; }
.case-study [data-reveal].in-view { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .case-study [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* Intro copy */
.story__intro { max-width: 68ch; font-size: 1.02rem; margin-bottom: 40px; }
.story__intro strong { color: var(--ty-text); }

/* Consistent across every page that uses either box (all 4 for research-note;
   nextgen-ai + tooyenplus for honesty-note). */
.research-note, .honesty-note {
  margin-top: 32px;
  display: flex; gap: 14px; align-items: flex-start;
  border-radius: var(--ty-radius-sm);
  padding: 16px 20px;
  max-width: 68ch;
}
.research-note {
  background: var(--ty-surface);
  border: 1px dashed var(--ty-text-disabled);
}
.research-note svg { flex-shrink: 0; width: 20px; height: 20px; color: var(--ty-accent); margin-top: 2px; }
.research-note p { margin: 0; font-size: 0.9rem; }
.honesty-note {
  border: 1px dashed var(--ty-accent);
  background: #fff;
}
.honesty-note svg { width: 20px; height: 20px; color: var(--ty-accent); flex-shrink: 0; margin-top: 2px; }
.honesty-note p { margin: 0; font-size: 0.92rem; }

/* Role */
.role__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; }
@media (max-width: 860px) { .role__grid { grid-template-columns: 1fr; } }
.scope-list { display: flex; flex-direction: column; gap: 10px; }
.scope-list li { display: flex; align-items: flex-start; gap: 12px; font-size: 0.95rem; color: var(--ty-text); }
.scope-list li strong { color: var(--ty-text); }
.scope-list svg { flex-shrink: 0; width: 18px; height: 18px; color: var(--ty-accent); margin-top: 3px; }
.responsibility { border-left: 3px solid var(--ty-accent-2); padding-left: 18px; margin-bottom: 20px; }
.responsibility:last-child { margin-bottom: 0; }
.responsibility h4 { font-size: 0.98rem; font-family: var(--ty-font-body); font-weight: 700; margin-bottom: 4px; }
.responsibility p { font-size: 0.9rem; margin: 0; }

/* Numbered decision / round label — base (in-card) variant. NextGen AI's
   Research/Impact sections use this label standalone (no card), which
   needs extra display/margin — added locally on that page. */
.pain-card__num { font-family: var(--ty-font-mono); font-size: 0.72rem; color: var(--ty-accent); font-weight: 700; letter-spacing: 0.06em; }

/* Used by NextGen AI (Research/Solution/Impact) and PAO (Solution). */
.solution-block { margin-bottom: 44px; }
.solution-block:last-child { margin-bottom: 0; }
.solution-block h3 { font-size: 1.2rem; margin-bottom: 12px; }

/* 3-card decision/insight grid — used by PingHub, PAO, TooYen+. PAO's
   2-column variant (--2) travels with the base since it's additive. */
.pain-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.pain-grid--2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 860px) { .pain-grid, .pain-grid--2 { grid-template-columns: 1fr; } }
.pain-card {
  background: var(--ty-surface);
  border: 1px solid var(--ty-border);
  border-radius: var(--ty-radius-md);
  padding: 26px 24px 24px;
  box-shadow: var(--ty-shadow-card);
}
.pain-card h3 { font-size: 1.1rem; margin: 8px 0 10px; line-height: 1.3; }
.pain-card p { margin: 0; font-size: 0.92rem; }
.pain-card p:not(:last-child) { margin-bottom: 10px; }
.pain-card p strong { color: var(--ty-text); }

/* Wide diagram figures (reuse .shot / lightbox). Grid-based, borderless
   layout: repeat(auto-fit, minmax(280px, 480px)) wraps to as many columns
   as fit a row and stretches them evenly to fill it (up to 480px each), so
   a row never needs horizontal scrolling regardless of how many shots it
   holds. The 480px cap (was 1fr, uncapped) is what keeps a row with only 1
   or 2 shots from stretching them across the entire row width — a single
   shot alone in a row no longer blows up to ~1120px; justify-content:
   center keeps that capped row from hugging the left edge with empty
   space beside it. Pages with real mobile screenshots (e.g. PingHub's
   #screens) override this locally with their own, narrower cap. */
.shot-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 480px));
  justify-content: center;
  gap: 24px;
}
.shot-row--wide { margin-top: 20px; }

/* Borderless: no card fill, no border, no box-shadow on the container —
   images sit directly on the page background instead of looking like
   they're inside a white/cream photo-mat card. The accent glow lives on
   the image itself (on hover), so interactivity still reads without a
   frame. Width is 100% so every shot fills its grid cell responsively;
   aspect-ratio (set per type modifier below) is what keeps row heights
   consistent when a landscape screenshot sits next to a portrait one.

   flex column + margin-top:auto on the figcaption (below) is what keeps
   captions on one baseline across a row even when a row mixes a capped
   .mockup-scroll-frame with a normal-height shot: grid's align-items:
   stretch already makes every figure in the row exactly as tall as the
   tallest one, and the auto margin then pushes each figure's caption
   down to consume whatever space its own media didn't use. */
.shot {
  display: flex;
  flex-direction: column;
  width: 100%;
  cursor: zoom-in;
}
.shot img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: contain;
  border-radius: var(--ty-radius-sm);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
/* Glow spreads on all sides (not just a bottom drop-shadow), so it reads
   as a highlight on the image rather than a card lifting off the page. */
.shot:hover img { transform: translateY(-4px); box-shadow: 0 0 24px rgba(214, 91, 62, 0.45); }
.shot .tag-mini { margin: 0 0 10px; }
.shot figcaption { font-size: 0.76rem; color: var(--ty-text-sub); text-align: center; padding-top: 10px; margin-top: auto; }

/* Type modifiers — pick the one matching what the screenshot actually is.
   Diagrams (canvases, maps, matrices) run close to landscape; desktop UI
   screenshots run wider still; phone screenshots are tall. Only the
   aspect-ratio differs between them now — width comes from .shot above. */
.shot--diagram img { aspect-ratio: 4 / 3; }
.shot--desktop img { aspect-ratio: 16 / 10; }
.shot--mobile img { aspect-ratio: 9 / 18.5; }

/* Scrollable viewport for unusually long screenshots (full-page captures,
   long lists) — caps the image at a fixed height and scrolls internally
   instead of forcing the whole page to scroll past one giant image.
   Must be placed after the type modifiers above: same specificity as
   `.shot img`/`.shot--desktop img` etc., so source order is what lets it
   win and cancel their aspect-ratio/object-fit for the wrapped image.
   The <figcaption> stays OUTSIDE this frame (a sibling, not a child) so
   captions across a grid row still land on the same baseline. */
.mockup-scroll-frame {
  max-height: 420px;
  overflow-y: auto;
  border-radius: var(--ty-radius-sm);
  scrollbar-width: thin;
  scrollbar-color: var(--ty-accent) var(--ty-border);
}
.mockup-scroll-frame img { aspect-ratio: auto; object-fit: unset; }
.mockup-scroll-frame::-webkit-scrollbar { width: 6px; }
.mockup-scroll-frame::-webkit-scrollbar-track { background: var(--ty-border); border-radius: 4px; }
.mockup-scroll-frame::-webkit-scrollbar-thumb { background: var(--ty-accent); border-radius: 4px; }

/* Feature cards — used by NextGen AI (Solution) and TooYen+ (Solution).
   NextGen AI's --4 modifier and TooYen+'s icon badge are both additive,
   so both travel together here without conflicting. */
.feature-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.feature-grid--4 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 860px) { .feature-grid, .feature-grid--4 { grid-template-columns: 1fr; } }
.feature-card { background: var(--ty-surface); border-radius: var(--ty-radius-md); border: 1px solid var(--ty-border); padding: 26px 24px; }
.feature-card__icon {
  width: 44px; height: 44px;
  border-radius: 12px;
  background: var(--ty-accent-2);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 16px;
}
.feature-card__icon svg { width: 22px; height: 22px; color: var(--ty-text); }
.feature-card h3 { font-size: 1.05rem; margin-bottom: 8px; }
.feature-card p { font-size: 0.9rem; margin: 0; }

/* Screens gallery — identical across all 4 pages */
.screen-group { margin-bottom: 52px; }
.screen-group:last-child { margin-bottom: 0; }
.screen-group__head { display: flex; align-items: baseline; justify-content: space-between; gap: 20px; margin-bottom: 18px; flex-wrap: wrap; }
.screen-group__head h3 { font-size: 1.15rem; }
.screen-group__head p { font-size: 0.88rem; margin: 0; max-width: 48ch; color: var(--ty-text-sub); }

/* Before/After pill tags — used by PAO and TooYen+ */
.tag-mini {
  display: inline-block;
  font-family: var(--ty-font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 4px 12px;
  border-radius: 100px;
  margin-bottom: 10px;
  font-weight: 700;
}
.tag-mini--before { background: var(--ty-border); color: var(--ty-text-sub);  }
.tag-mini--after { background: var(--ty-accent); color: #fff; }

/* Checklist — common value across PAO/TooYen+ (surface bg, 18px top margin);
   PingHub overrides locally (its checklist sits inside an already-white
   .iteration-notes card, so it needs the cream --ty-bg for contrast, and
   less top margin since the card supplies its own spacing). */
.checklist { display: flex; flex-direction: column; gap: 12px; margin-top: 18px; }
.checklist li {
  display: flex; gap: 10px; align-items: flex-start;
  font-size: 0.92rem;
  background: var(--ty-surface);
  border: 1px solid var(--ty-border);
  border-radius: var(--ty-radius-sm);
  padding: 12px 14px;
}
.checklist svg { width: 17px; height: 17px; color: var(--ty-accent); flex-shrink: 0; margin-top: 2px; }

/* 2-card before/after side-by-side layout — used by PingHub (Testing),
   PAO (Process), TooYen+ (Iteration). Common margin-bottom across
   PingHub/PAO; TooYen+ overrides locally (needs more room before the
   honesty-note that follows). */
.iteration-notes { display: grid; grid-template-rows: 1fr 1fr; gap: 24px; margin-bottom: 8px; }
@media (max-width: 760px) { .iteration-notes { grid-template-rows: 1fr; } }
.iteration-notes > div { background: var(--ty-surface); border: 1px solid var(--ty-border); border-radius: var(--ty-radius-sm); padding: 20px 22px; }
.iteration-notes h4 { font-size: 0.95rem; margin-bottom: 8px; }
.iteration-notes p { font-size: 0.9rem; margin: 0; }


/* Reflection 2-column layout — used by PingHub and TooYen+ */
.split-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; }
@media (max-width: 860px) { .split-2 { grid-template-columns: 1fr; gap: 48px; } }

/* Lesson Learned — identical across all 4 pages */
.lesson-card { padding: 20px 0; border-bottom: 1px solid var(--ty-border); }
.lesson-card:first-child { padding-top: 0; }
.lesson-card:last-child { border-bottom: none; padding-bottom: 0; }
.lesson-card .tag-label { margin-bottom: 10px; }
.lesson-card p { font-size: 0.94rem; margin: 0; }

/* Floating "back to top" button — fixed bottom-right, identical across all
   4 pages. Hidden until the reader has scrolled past the hero (toggled via
   .is-visible by the page's own scroll listener). */
.back-to-top {
  position: fixed;
  right: clamp(16px, 4vw, 32px);
  bottom: clamp(16px, 4vw, 32px);
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--ty-accent);
  color: #fff;
  box-shadow: var(--ty-shadow-card);
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px);
  transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s ease, background 0.2s ease, box-shadow 0.2s ease;
}
.back-to-top.is-visible { opacity: 1; visibility: visible; transform: translateY(0); }
.back-to-top:hover { background: var(--ty-accent-dark); box-shadow: var(--ty-shadow-soft); }
.back-to-top svg { width: 20px; height: 20px; stroke: #fff; }
@media (max-width: 600px) {
  .back-to-top { width: 42px; height: 42px; }
  .back-to-top svg { width: 18px; height: 18px; }
}

/* "Explore more" / other case studies — identical across all 4 pages.
   Sits after the closing note, before the real Contact footer. */
.explore-more { border-top: 1px solid var(--ty-border); padding: 72px 0; }
.explore-more__head { margin-bottom: 32px; }
.explore-more__head h2 { font-size: clamp(1.5rem, 2.6vw, 2rem); margin-top: 6px; }
.explore-more__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; }
@media (max-width: 800px) { .explore-more__grid { grid-template-columns: 1fr; } }
.explore-card { display: flex; flex-direction: column; gap: 14px; text-decoration: none; }
.explore-card__img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--ty-radius-md);
  border: 1px solid var(--ty-border);
  box-shadow: var(--ty-shadow-card);
  background: var(--ty-surface);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.explore-card:hover .explore-card__img { transform: translateY(-4px); box-shadow: var(--ty-shadow-soft); }
.explore-card__title {
  font-family: var(--ty-font-display);
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--ty-text);
  transition: color 0.2s ease;
}
.explore-card:hover .explore-card__title { color: var(--ty-accent); }

/* Lightbox — identical across all 4 pages. overflow-y + align-items:
   flex-start (instead of center) + margin:auto on the frame let the
   lightbox scroll once a zoomed image is taller than the viewport, while
   still centering anything that fits without scrolling. */
.ty-lightbox {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(58, 35, 28, 0.82);
  display: none;
  align-items: flex-start;
  justify-content: center;
  overflow-y: auto;
  padding: 40px 20px;
  font-family: var(--ty-font-body);
}
.ty-lightbox.is-open { display: flex; }
.ty-lightbox__frame { display: flex; flex-direction: column; align-items: center; margin: auto; }
.ty-lightbox img { max-width: min(560px, 90vw); max-height: 82vh; border-radius: var(--ty-radius-sm); box-shadow: 0 30px 60px rgba(0,0,0,0.4); cursor: zoom-in; }
/* Deep zoom — click the lightbox image to toggle full natural size; the
   lightbox's own overflow-y:auto (above) is what lets you scroll/pan it. */
.ty-lightbox img.is-zoomed { max-width: none; max-height: none; cursor: zoom-out; }
.ty-lightbox figcaption { color: #fff; text-align: center; font-size: 0.85rem; margin-top: 14px; opacity: 0.85; }
/* fixed, not absolute: .ty-lightbox itself scrolls now (for zoomed images),
   and a merely-absolute close button would scroll away with that content. */
.ty-lightbox__close { position: fixed; top: 24px; right: 28px; background: none; border: none; cursor: pointer; color: #fff; z-index: 1001; }
.ty-lightbox__close svg { width: 26px; height: 26px; }