/* ─────────────────────────────────────────────────────────────
   home.css — Homepage V3 "Olivetti" technical-drawing aesthetic
   Figma node 139:3285. Cream paper plate with rust-red labels and
   deep-brown text. Reuses tokens.css fonts (DM Serif Display + Inter)
   but introduces Olivetti-specific colors scoped to body.home.
   Responsive: collapses every 2/3-col layout to single column at
   ≤768px and scales the giant display headlines down.
   ───────────────────────────────────────────────────────────── */

body.home,
body.about {
  /* Olivetti palette — paper colors now reference tokens.css so the
     home matches the case-study background exactly (was #f0ebe0,
     now var(--color-bg) = #F5F0EB). The About page rides the same
     tokens + parchment grid background (about.css depends on these
     vars and assumes the grid is already painted). */
  --ho-paper:   var(--color-bg);            /* #F5F0EB */
  --ho-paper-2: var(--color-bg-elevated);   /* #EEE8E2, method section plate */
  --ho-rust:    #7a2e1f;   /* eyebrows, labels, figure numbers */
  --ho-ink:     #3a2317;   /* headlines, body */
  --ho-ink-2:   #5a4738;   /* secondary body */
  --ho-cta:     #37281c;   /* dark CTA button */
  --ho-line:    rgba(58, 35, 23, 0.18);  /* hairline rules (soft) */
  --ho-line-2:  rgba(58, 35, 23, 0.45);  /* table grid (firmer) */

  /* Two monospace stacks:
     --ho-mono       = Space Mono — page-level technical labels
                       (eyebrows, status box, ruler steps, colophon, etc.)
     --ho-mono-card  = JetBrains Mono Medium — interactive/card labels
                       (project tab, eyebrow date, VIEW PROJECT, IMAGE.JPG) */
  --ho-mono:      'Space Mono', ui-monospace, 'Cascadia Mono', SFMono-Regular,
                  Menlo, Consolas, 'Courier New', monospace;
  --ho-mono-card: 'JetBrains Mono', ui-monospace, 'Cascadia Mono', SFMono-Regular,
                  Menlo, Consolas, monospace;

  /* Single-tier technical grid à la Nudge: evenly-spaced 1px lines on
     100px squares, applied to body so it spans the full viewport width
     (not capped by #main's 1440px). Sections that previously set their
     own paper bg are transparent so this shows through; project cards
     and the ho-method plate render their own surfaces on top. */
  background-color: var(--ho-paper);
  background-image:
    linear-gradient(to right,  rgba(58, 35, 23, 0.04) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(58, 35, 23, 0.04) 1px, transparent 1px);
  background-size: 100px 100px;
  background-position: 0 0;
  color: var(--ho-ink);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Page max-width — Figma desktop frame (139:3285) is 1440px wide, so
   cap the homepage there. Mobile/tablet shrink naturally — max-width
   only kicks in above 1440. The grid lives on body so it extends past
   this 1440 cap on ultra-wide displays. */
body.home #main {
  max-width: 1440px;
  margin-inline: auto;
}

/* Section inner — 64px page-edge gutters per Figma. */
.ho-section-inner {
  padding-inline: 64px;
}

/* ── Eyebrow label (PLATE · FIG. 1.0 · …) ────────────────── */

.ho-eyebrow {
  display: inline-block;
  font-family: var(--ho-mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--ho-rust);
  text-transform: uppercase;
}

/* ── Display headlines — DM Serif Display ────────────────── */

.ho-display {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--ho-ink);
  letter-spacing: -0.015em;
  line-height: 0.95;
  margin: 0;
}

.ho-display--xl { font-size: clamp(2.75rem, 8.2vw, 7.25rem); }   /* 44 → 116 px */
.ho-display--lg { font-size: 40px; }
.ho-display--md { font-size: clamp(1.75rem, 3.4vw, 2.375rem); line-height: 1.05; }  /* 28 → 38 px */

/* Italic accent — second line of each big headline (rust red, italic).
   Mirrors Figma's per-character style on hero / works / method / contact
   headlines. <em> is the semantic wrapper. */
.ho-italic {
  font-style: normal;
  color: var(--ho-rust);
  font-weight: 400;
}

/* Hero headline second-line override — slate-blue per latest Figma direction.
   Method and contact headlines keep the rust accent. */
#hero-headline .ho-italic { color: #5D839B; }

/* ── 2. Hero ─────────────────────────────────────────────── */

.ho-hero {
  padding-block: 56px;
  background: transparent;
  min-height: 754px;          /* match Figma 139:3287 frame height */
  display: flex;
  flex-direction: column;
  justify-content: center;    /* primaryAxisAlignItems: CENTER */
}

/* Inner stack: vertical, gap 24, centered both axes — matches Figma
   hero-v3 layout (P:CENTER C:CENTER). Headline → intro → measurements. */
.ho-hero .ho-section-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  text-align: center;
  max-width: 880px;
  margin-inline: auto;
}

/* Headline override — cap the hero headline at 70px so the measurements
   row underneath isn't crowded. */
.ho-hero .ho-display { font-size: clamp(2.5rem, 6.7vw, 70px); line-height: 0.95; font-weight: 300; }

.ho-hero-intro {
  font-size: 20px;
  line-height: 1.4;
  color: #7B776E;
  max-width: 580px;           /* Figma intro frame width */
  /* margin: 0 auto so the block centers when narrower than its parent
     content area; previously `margin: 0` zeroed out auto and left the
     block flush-left whenever flex couldn't override it. */
  margin: 0 auto;
}

/* ── Key measurements (now part of hero) ─────────────────── */
/* Figma 273:51412 measurements-row: HORIZONTAL, gap 24, 3 boxed cards.
   Each card (e.g. 273:51413): 16px radius, 1px rust border, value block
   (rust text, padded 24) sitting above a rust-filled label band (white
   text, padded 8 8 8 24). Cards are self-contained — no shared divider. */

.ho-measure-row {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  width: 100%;
  max-width: 756px;
}

.ho-measure {
  display: flex;
  flex-direction: column;
  text-align: left;
  border: 1px solid var(--ho-rust);
  border-radius: var(--radius-medium);
  overflow: hidden;           /* clip rust band's bottom corners */
  background: transparent;
}

.ho-measure-value {
  display: block;
  font-family: var(--font-display);
  font-size: 48px;
  font-weight: 400;
  color: var(--ho-rust);      /* Figma rgb(122,46,31) — was ink */
  line-height: 1;             /* Figma's 72px lh creates excess box; visual height of "7+" is glyph height */
  letter-spacing: -0.02em;
  padding: 24px;
}

.ho-measure-label {
  display: block;
  font-family: var(--ho-mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-white);
  background: var(--ho-rust);
  padding: 8px 8px 8px 24px;  /* Figma asymmetric: extra left for label start */
  margin-top: auto;           /* push band to bottom if card is taller than content */
}

@media (max-width: 1024px) {
  .ho-hero { min-height: 620px; padding-block: 48px; }
}

@media (max-width: 768px) {
  /* Mobile hero — Figma 349:55098 (iPhone 16, 393×852). Hero fills the
     full viewport with content clustered upper-middle: 24% breathing
     room above the knot, 26% below the intro. svh accounts for mobile
     browser chrome so the layout doesn't shift when the URL bar
     collapses on scroll; vh is the fallback. */
  .ho-hero {
    min-height: 100vh;
    min-height: 100svh;
    padding-block: calc(24svh - 20px) 26svh;
    justify-content: flex-start;
  }
  .ho-hero .ho-section-inner {
    /* 24px gap is the headline → intro gap from Figma. Knot → headline
       gets +9px extra via the knot's margin-bottom below (effective 33px). */
    gap: 24px;
    max-width: 334px;
    /* Override the global mobile .ho-section-inner padding-inline so the
       334px max-width is the actual content area (matches Figma 334px
       text frame width) — children at 334px no longer overflow. */
    padding-inline: 0;
  }
  /* Knot box — 275×280 per Figma. Fits inside the 286px content area
     (.ho-section-inner is 334px with 24px padding-inline = 286px content),
     so margin:auto centers cleanly without negative-free-space artifacts.
     +9px bottom margin on top of the parent's 24px gap = 33px knot→headline. */
  .ho-hero-detail {
    width: 275px;
    height: 280px;
    margin: 0 auto 9px;
  }
  /* Headline + intro typography per Figma 349:55095 / 349:55097. */
  .ho-hero .ho-display {
    font-size: 32px;
    line-height: 1;
  }
  .ho-hero-intro {
    font-size: 14px;
    line-height: 19px;
    max-width: 334px;
  }
  .ho-measure-row { grid-template-columns: 1fr; gap: 16px; max-width: 320px; }
  .ho-measure-value { font-size: 40px; padding: 20px 20px 16px; }
  .ho-measure-label { padding: 8px 8px 8px 20px; }
  .ho-works { padding-top: 0; }
  .ho-project--01 { margin-top: 0; }
}

/* ── Card 01 "scale up while entering" desktop behavior ──────
   Card 01 lives in the works section with cards 02/03 (the stack
   context that made the stack work in the first place). It just
   carries an extra scale transform driven by --c01s, which JS ramps
   from 0.45 → 1.0 as the user scrolls toward its natural pin
   position. transform-origin: center top makes it grow downward
   from a centered top edge — at scale 1 the layout box is exactly
   where the next card (02) will pin, so the stack handoff is
   pixel-clean. Same gate as the sticky-stack rule. */
@media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
  /* Hero sized so its bottom sits ≈244px above the viewport bottom on a
     1024-tall viewport — matches the case-study peek in Figma 288:52658.
     Text is bottom-aligned with a 130px padding-bottom so the gap from
     the intro to the card preview is exactly 130px (Figma spec).
     Mobile/reduced-motion keep the centered layout above. */
  .ho-hero {
    min-height: 100vh;
    justify-content: flex-start;
    /* Figma 288:52250 — knot top at 21% of hero height, bottom space 28%.
       Top trimmed by 32px to pull the knot + headline + intro stack up. */
    padding-block: calc(21vh - 32px) 28vh;
  }

  /* Stretch the inner stack to fill the hero's content area so the
     scroll-cue can be pushed to the bottom of that area (just above
     the peeking folder) via margin-top: auto. */
  .ho-hero .ho-section-inner {
    flex: 1 1 auto;
    align-self: stretch;
  }

  /* Push the CASE STUDIES cue down to the bottom of the inner stack —
     it sits between the intro paragraph and the peeking folder. */
  .ho-hero .ho-scroll-cue {
    margin-top: auto;
    /* Nudge the cue further down toward the peeking folder. Translate
       avoids changing layout / hero min-height. Tune to taste. */
    transform: translateY(64px);
  }

  /* Drop the works section top-padding now that the header is gone,
     so the 130px gap above is the exact distance to the case study. */
  .ho-works { padding-top: 0; }

  /* (Removed) Card 01's scale-from-peek transform. The hero used to
     end with a small folder peeking above its bottom edge; scrolling
     past the hero would scale and lift the folder into its pinned
     position. Now removed — folders just live at full size in their
     natural flow and stack via sticky as the user scrolls. */
}

/* ── 4. Selected works ───────────────────────────────────── */

.ho-works {
  padding-block: 56px;
  background: transparent;
}

.ho-works-header {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 32px;
  align-items: start;
  margin-bottom: 36px;
}

.ho-works-header-text { display: flex; flex-direction: column; gap: 14px; }

.ho-works-index {
  font-family: var(--ho-mono);
  font-size: 11px;
  line-height: 20px;          /* match Figma 20px line-box */
  color: var(--ho-rust);
  white-space: pre;
  margin: 0;
}

/* ── Project cards (Figma 223:49753 / 223:49825 / 223:49897) ──────
   Each card is a folder-tab + 2-col body (text 576px / image 704px)
   on a custom pastel background. Project 01 has an inline radar SVG;
   02/03 are placeholders with an IMAGE.JPG corner badge. */

.ho-project {
  --proj-bg: #e0f0e6;          /* default; modifiers override */
  --proj-i: 0;                 /* index, set per-card via inline style */
  margin-top: 100px;           /* spacing to section title (collapses with
                                  .ho-works-header margin-bottom) and to
                                  the previous card */
}

.ho-project--01 { --proj-bg: #e0f0e6; }   /* mint */
.ho-project--02 { --proj-bg: #d9cfe8; }   /* lavender */
.ho-project--03 { --proj-bg: #e8d8cf; }   /* peach */

/* ── Folder tab on top-left of each card ─────────────────── */

/* Tab silhouette is drawn as inline SVG inside .ho-project-tab so the
   slanted right edge (Figma 223:49756 path: 0,0 → 188,0 → 220,22.4 →
   220,56 → 0,56) can carry a 1px stroke. The element itself is just
   a positioning shell for the icon + label. */
.ho-project-tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  /* Logo → label gap per Figma (frames 567:4315 / 483:5765 / 567:4322). */
  gap: 8px;
  padding: 0 32px 0 24px;
  width: 220px;
  height: 56px;
  margin-bottom: -1px;         /* overlap card top border so they merge */
  z-index: 1;
  /* Anchor reset — tabs are <a> elements now (link to case studies). */
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  pointer-events: auto;        /* tab stays clickable even when its parent
                                  article opts out of pointer events */
}

/* Underline only the label on hover so the tab feels like a text link.
   Tab body + icon stay still. */
.ho-project-tab:hover .ho-project-tab-label {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.ho-project-tab-shape {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;           /* let the centered stroke spill 0.5px */
}

.ho-project-tab-fill {
  fill: var(--proj-bg);
  stroke: none;
}

.ho-project-tab-stroke {
  fill: none;
  stroke: #000;
  stroke-width: 1;
}

/* Lift the icon + label above the SVG */
.ho-project-tab > .ho-project-tab-icon,
.ho-project-tab > .ho-project-tab-label,
.ho-project-tab > .ho-project-tab-logo {
  position: relative;
  z-index: 1;
}

/* Tab icon. All three tabs use 16×16 line icons from the Huge icon
   set (focus / widgets / users 02), exported as SVG per Figma frames
   567:4315, 483:5765, 567:4322. Vertical alignment with the 13px
   label is handled by align-items:center on the parent tab. */
.ho-project-tab-logo {
  display: block;
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
}

.ho-project-tab-icon {
  display: inline-flex;
  align-items: flex-end;
  gap: 1px;
  height: 12px;
}
.ho-project-tab-icon span {
  width: 5px;
  background: #1c1c1c;
}
.ho-project-tab-icon span:nth-child(1) { height: 4px; }
.ho-project-tab-icon span:nth-child(2) { height: 8px; }
.ho-project-tab-icon span:nth-child(3) { height: 12px; }

.ho-project-tab-label {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  color: #1c1c1c;
  letter-spacing: 0;
}

/* ── Card body (text + image side-by-side) ───────────────── */

.ho-project-body {
  /* Impact compression — brief scaleY squash when the flap lands. Pulse
     is parabolic ((p-0.9)·(1-p)) clamped ≥ 0, so it's only non-zero in
     the impact window p ∈ [0.9, 1.0], peaking ~0.5% squash (~3px on a
     600px body) at p = 0.95. transform-origin: top so the body
     compresses downward, away from the flap that just landed. */
  --ho-impact: max(0, calc((var(--ho-flap-progress, 0) - 0.9) * (1 - var(--ho-flap-progress, 0))));
  transform: scaleY(calc(1 - var(--ho-impact) * 8));
  transform-origin: top center;
  display: grid;
  /* Figma 223:49762 layout — text col 576px, image col 704px (1fr fills
     the remaining space), 8px gap, 24px right padding (text col is flush
     left, image slot ends 24px from card edge). */
  grid-template-columns: 576px 1fr;
  gap: 8px;
  padding: 24px 24px 24px 0;
  background: var(--proj-bg);
  border: 1px solid #000;             /* Figma 223:49762 strokeWeight: 1, all sides */
  border-top-right-radius: 12px;
  border-bottom-left-radius: 12px;
  border-bottom-right-radius: 12px;
  /* tab sits on top-left of this border with margin-bottom: -1 so
     the tab's bottom edge overlaps the card's top border seamlessly */
  min-height: 540px;
}

/* ── Text column (left) ──────────────────────────────────── */

.ho-project-text {
  display: flex;
  flex-direction: column;
  padding: 56px 32px 56px 56px;
}

.ho-project-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  color: #1c1c1c;
  letter-spacing: 0;
  margin-bottom: 28px;
}
.ho-project-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #1c1c1c;
  flex-shrink: 0;
}

.ho-project-title {
  font-family: var(--font-display);
  font-size: 40px;
  font-weight: 400;
  color: #1c1c1c;
  line-height: 1.05;
  letter-spacing: -0.01em;
  margin: 0 0 24px;
}

.ho-project-desc {
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.55;
  color: #1c1c1c;
  margin: 0;
  max-width: 460px;
}

.ho-project-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  color: #1c1c1c;
  letter-spacing: 0;
  text-decoration: none;
  margin-top: auto;          /* push to bottom of column */
  padding-top: 32px;
  transition: transform 180ms ease;
}
.ho-project-link:hover { transform: translateX(2px); }

/* ── Image column (right) ────────────────────────────────── */

/* Image slot (the right grid cell, 704px wide × 700px tall on desktop)
   per Figma 223:49771 / 261:50627 / 261:50848. The slot itself is
   transparent — the visual is centered inside an inner FRAME (594×592
   on desktop, with rounded corners + subtle bg + grid pattern). */
.ho-project-image {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 540px;
  overflow: hidden;
}


/* Inner frame around each visualization — Figma 223:49772 / 261:50628
   / 261:50853. 592×592 with cornerRadius 9.87 (≈10px). Inset 56px
   left/right and 54px top/bottom from slot edges (slot is 704×700, so
   704-112 = 592 and 700-108 = 592). The frame holds the bg + grid +
   1px stroke. Stroke color is per-project so it tones with the palette
   (mint highlight on Card 1, faint ink hairline on Cards 2/3). */
.ho-project-image-frame {
  position: relative;
  width: 100%;
  max-width: 592px;
  aspect-ratio: 1;
  max-height: 100%;
  background: rgba(28, 28, 28, 0.04);
  border: 1px solid #1c1c1c;            /* visible dark frame around visualization */
  border-radius: 10px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.ho-project--02 .ho-project-image-frame,
.ho-project--03 .ho-project-image-frame {
  background: rgba(28, 28, 28, 0.08);
  /* border-color inherits the unified dark stroke from the base rule */
}

/* IMAGE.JPG corner badge — Figma puts it at (572, 24) within the 704×700
   slot, i.e. 24px from the slot's top and right edges. */
.ho-project-image-label {
  position: absolute;
  top: 24px;
  right: 24px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px 5px 8px;
  background: #3cc5f5;
  font-family: var(--ho-mono-card);
  font-size: 12px;
  font-weight: 500;
  color: #1c1c1c;
}
.ho-project-image-tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 20px;
  background: #1c1c1c;
  color: #3cc5f5;
  font-size: 7px;
  border-radius: 2px;
}

/* Visualizations fill the inner frame (.ho-project-image-frame). The
   frame carries the size + aspect ratio; the SVG just stretches into
   it and preserveAspectRatio (default xMidYMid meet) keeps the drawing
   centered + 1:1 inside. */
.ho-project-radar,
.ho-project-pstar,
.ho-project-trust {
  display: block;
  width: 100%;
  height: 100%;
  position: relative;
  z-index: 1;
}

/* Faint blueprint grid behind the radar (Project 01), protocol
   star (Project 02), and trust network (Project 03). Drawn on the
   inner frame (clipped to its rounded corners) so the pattern only
   shows inside the framed visual area. */
.ho-project-image--radar .ho-project-image-frame::before,
.ho-project-image--pstar .ho-project-image-frame::before,
.ho-project-image--trust .ho-project-image-frame::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(59, 36, 25, 0.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(59, 36, 25, 0.045) 1px, transparent 1px);
  background-size: 84px 84px;
  background-position: center;
  opacity: 0.55;
  pointer-events: none;
}

/* ── Project 03 trust network ────────────────────────────
   Color palette from Figma 250:50336:
     • ink           #3b2316  (lines, glyphs, captions)
     • accent        #854e31  (terracotta — packet, ripples, star ratings)
     • inner-fill    #e6d8d0  (cream-pink — body of inner node circles)
   All animations honour prefers-reduced-motion. */

/* (.ho-project-trust shares sizing with .ho-project-radar / .ho-project-pstar
   in the rule above — fills its 1:1 inner frame.) */

.ho-trust-boundary {
  fill: none;
  stroke: #3b2316;
  stroke-width: 0.8;
  stroke-dasharray: 7 9;
  opacity: 0.25;
  vector-effect: non-scaling-stroke;
}

.ho-trust-glow {
  transform-box: fill-box;
  transform-origin: center;
  animation: ho-trust-center-pulse 3s ease-in-out infinite;
}

@keyframes ho-trust-center-pulse {
  0%, 100% { opacity: 0.11; transform: scale(0.92); }
  50%      { opacity: 0.18; transform: scale(1.08); }
}

.ho-trust-solid-edge {
  stroke: #3b2316;
  stroke-width: 0.8;
  fill: none;
  opacity: 0.9;
  vector-effect: non-scaling-stroke;
}

.ho-trust-dashed-edge {
  stroke: #3b2316;
  stroke-width: 0.55;
  stroke-dasharray: 3 4;
  fill: none;
  opacity: 0.34;
  vector-effect: non-scaling-stroke;
}

.ho-trust-node-ring,
.ho-trust-glyph-line {
  fill: none;
  stroke: #3b2316;
  stroke-width: 0.9;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.ho-trust-inner-fill {
  fill: #e6d8d0;
  stroke: #854e31;
  stroke-width: 0.7;
  vector-effect: non-scaling-stroke;
}

.ho-trust-outer-node {
  opacity: 0.5;
  transition: opacity 0.2s ease-out;
}
.ho-trust-outer-node.is-received { opacity: 1; }

.ho-trust-star {
  fill: #854e31;
  stroke: none;
}
.ho-trust-star-outline {
  fill: none;
  stroke: #854e31;
  stroke-width: 0.75;
  vector-effect: non-scaling-stroke;
}

.ho-trust-verified-dot,
.ho-trust-packet {
  fill: #854e31;
}

.ho-trust-ripple {
  fill: none;
  stroke: #854e31;
  stroke-width: 1;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  vector-effect: non-scaling-stroke;
}
.ho-trust-ripple.is-active {
  animation: ho-trust-ripple-out 0.6s ease-out forwards;
}

@keyframes ho-trust-ripple-out {
  0%   { opacity: 0.9; transform: scale(1);    }
  100% { opacity: 0;   transform: scale(2.05); }
}

.ho-trust-caption {
  fill: #3b2316;
  opacity: 0.55;
  font-family: 'Space Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 6px;
  text-anchor: middle;
}

@media (prefers-reduced-motion: reduce) {
  .ho-trust-glow,
  .ho-trust-ripple,
  .ho-trust-ripple.is-active { animation: none; transform: none; }
}

/* Vertex dot scales when its protocol fires (JS toggles data-active).
   transform-box: fill-box anchors scale to the dot's painted center
   regardless of its absolute position in the SVG. */
.ho-pstar-dot {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 320ms cubic-bezier(0.16, 1, 0.3, 1);
}
.ho-pstar-vertex[data-active] .ho-pstar-dot {
  transform: scale(1.4);
}

/* Spoke active state — matches vertex active for the duration of
   one packet (JS handles the toggle). */
.ho-pstar-spokes line {
  transition: stroke-opacity 240ms ease, stroke-width 240ms ease;
}
.ho-pstar-spokes line[data-active] {
  stroke-opacity: 0.55;
  stroke-width: 0.9;
}

@media (prefers-reduced-motion: reduce) {
  .ho-pstar-dot,
  .ho-pstar-vertex[data-active] .ho-pstar-dot,
  .ho-pstar-spokes line { transition: none; transform: none; }
}

/* ── Radar animations ───────────────────────────────────────
   Three layers play continuously (CSS) plus a JS-driven node
   activation cycle (see js/home.js). All animations stop when
   the user prefers reduced motion. */

/* Sonar beam — JS drives rotation per-frame so the sweep can slow when
   crossing detected nodes. Trail beams (cloned in JS) lag behind at
   stepped angles + lower opacities, making the sweep feel like it's
   actually scanning rather than spinning at a constant rate.
   Reduce-motion users see a static beam (JS short-circuits). */
.ho-radar-sweep,
.ho-radar-sweep-trail {
  transform-origin: 300px 300px;
}


/* Active node — JS toggles `data-active` on each <g class="ho-radar-node">
   in sequence. transform-box: fill-box anchors the scale to the dot's
   own center regardless of its absolute position in the SVG.
   The eased-quint curve (0.16, 1, 0.3, 1) gives a "soft as silk"
   pop-in that decelerates gracefully instead of snapping. The
   complementary fade-out uses the same curve over a slightly longer
   duration so the dot eases back to rest. */
.ho-radar-dot {
  transform-box: fill-box;
  transform-origin: center;
  transition:
    transform 900ms cubic-bezier(0.16, 1, 0.3, 1),
    filter   900ms cubic-bezier(0.16, 1, 0.3, 1);
  /* Default state: no halo. */
  filter: drop-shadow(0 0 0 rgba(162, 206, 179, 0));
}
.ho-radar-node[data-active] .ho-radar-dot {
  transform: scale(1.4);
  /* Halo uses the brighter dot shade #A2CEB3 so it reads on both
     darker (#7E9386) and brighter (#A2CEB3) base dots. */
  filter: drop-shadow(0 0 6px rgba(162, 206, 179, 0.65));
}

/* Per-node "ping" ring — JS injects a fresh circle on each
   activation, the animation fires once, JS removes it. */
.ho-radar-ping {
  transform-box: fill-box;
  transform-origin: center;
  animation: ho-radar-ping 1400ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
  pointer-events: none;
}

@keyframes ho-radar-ping {
  0%   { transform: scale(1);   opacity: 0.7; }
  60%  {                        opacity: 0.4; }
  100% { transform: scale(3.4); opacity: 0;   }
}

@media (prefers-reduced-motion: reduce) {
  .ho-radar-sweep,
  .ho-radar-pulse,
  .ho-radar-ping { animation: none; }
  .ho-radar-pulse { display: none; }
  .ho-radar-node[data-active] .ho-radar-dot { transform: none; }
}

/* ── Sticky-stack: scroll-driven folder behaviour (desktop only) ──
   Per Figma 261:50473 / 261:50541 / 261:50686, the design is a
   simple folder stack: each card sits centered in the viewport
   with ~64px horizontal and ~110px vertical padding, and as the
   user scrolls the next card slides up and covers the previous
   one perfectly (no opacity, no scale, no lift — the new card
   just stacks on top).

   How this works in CSS:
     • Each .ho-project is a 100vh sticky slot pinned at top:0 — that
       gives one viewport of scroll per card before the next pins.
     • Inside each slot, the folder (tab + body) is a fixed-size
       block centered with flex justify-content:center, so the
       folder reads as a single unit with padding all around.
     • Tabs are staggered horizontally (0 / 220 / 440px) so when
       cards stack, all previous tabs remain visible at the top of
       each card alongside the current one.
     • z-index increases per card so later cards visually cover
       earlier ones.
   Placed AFTER the base .ho-project-body / .ho-project-image rules
   so the min-height/height/flex overrides win on cascade order.
   Gated at min-width: 1024 so tablet (768-1023) drops to the regular
   stacked single-column layout (no sticky-stack). */

@media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
  .ho-project {
    position: sticky;
    /* Pin the 75vh slot vertically centered, so the remaining 25vh
       splits 12.5vh above + 12.5vh below. The 25vh of "below" lands
       right where the next card's container starts in flow — that's
       the peek the user sees before scrolling. */
    top: calc((100vh - 75vh) / 2);
    height: 75vh;            /* slot height = peek size; smaller slot →
                                 more of the next folder visible at rest.
                                 (was 85vh, gave only ~15vh peek). */
    margin-top: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    box-sizing: border-box;
    z-index: calc(10 + var(--proj-i));
    /* Phase-4 lift — JS sets --ho-stack-shift on the .ho-stack wrapper
       from 0 → -<empty-space-above-folder> during phase 4, lifting the
       three pinned cards so the tab row reaches viewport top in the
       final closed state (per Frame 39387). Transform doesn't affect
       sticky pin layout, so the box still pins at top:0 — the lift is
       paint-only. */
    transform: translateY(var(--ho-stack-shift, 0px));
    will-change: transform;
    /* The article fills the full 75vh slot but the tab + body only
       occupy part of it. Without this, the topmost article (card 03,
       z=12) swallows clicks in its empty regions, blocking the
       Sastrify and Dimension 4 tabs underneath. Turning off pointer
       events on the article and re-enabling them on the tab + body
       lets clicks reach whichever child is actually under the cursor. */
    pointer-events: none;
  }

  .ho-project-body {
    pointer-events: auto;
  }

  .ho-project-tab {
    margin-left: calc(var(--proj-i) * 220px);
    flex: 0 0 auto;
  }

  /* Folder body height — Figma is 748px (805px folder − 56px tab) on a
     1024px viewport, with ~110px of padding above and below. Clamp so
     it fits with breathing room on shorter viewports too. */
  .ho-project-body {
    flex: 0 0 auto;
    /* 25% smaller than before — was clamp(540px, calc(100vh - 220px), 748px).
       Smaller cards mean each folder consumes less viewport so users see
       there's more below. Visualizations auto-fit the frame. */
    height: clamp(405px, calc((100vh - 220px) * 0.75), 561px);
    min-height: 0;
    /* Width the right column to (body-height − 48px top+bottom padding),
       so the square image-frame fills it horizontally and the right-side
       whitespace matches the 24px top/bottom padding. */
    grid-template-columns: 1fr calc(clamp(405px, calc((100vh - 220px) * 0.75), 561px) - 48px);
  }

  /* The grid image column has its own min-height in the base rule;
     let it follow the body height instead so the folder fits the slot. */
  .ho-project-image {
    min-height: 0;
  }

  /* ── Phase 4: method-section folder-flap closing animation ─────
     The .ho-stack wrapper holds 3 sticky cards + 1 sticky method-flap
     + a 100vh tail. With 5 × 100vh of children, all 4 sticky elements
     pin together for the last 100vh of scroll (the "phase-4 zone"),
     during which JS drives the flap's rotation from edge-on (open,
     pointing down + away) to flat (closed, covering the card body). */

  /* Method-flap slot — sticky 100vh, sibling of the three .ho-project
     cards. z-index above the topmost card (z=12) so when the flap
     rotates to flat (rotateX 0) it covers the card body. The slot
     itself carries the 3D perspective so the rotating child panel
     has real depth (a "swing up" rather than a flat squash). */
  .ho-method.ho-method-flap {
    position: sticky;
    /* Match the cards' sticky geometry (top:12.5vh + 75vh slot) so the
       master flap unsticks at the same scrollY as the cards. If the flap
       slot is taller than the cards', it unsticks earlier — the parent's
       bottom catches up sooner — and the cards behind peek out below it
       as the user scrolls into the footer. */
    top: calc((100vh - 75vh) / 2);
    height: 75vh;
    margin: 0;
    padding: 0;
    background: transparent;
    z-index: 25;
    pointer-events: none;
    /* Higher perspective value = subtler foreshortening, so the
       rotating flap doesn't appear noticeably narrower than the
       cards' folder bodies during the close (the visual was
       creating a misalignment with the case-study tabs above).
       Origin at 50% 100% (bottom center) anchors the vanishing
       point at the hinge, keeping the bottom edge perfectly
       aligned with the cards' folder body bottom throughout the
       rotation; the top still recedes slightly for depth. */
    perspective: 4000px;
    perspective-origin: 50% 100%;
    /* Lift in lockstep with the cards (same --ho-stack-shift), so the
       flap's hinge stays aligned with the cards' folder-body bottom
       throughout phase 4. */
    transform: translateY(var(--ho-stack-shift, 0px));
    will-change: transform;
  }

  /* The actual folder-bottom-flap panel — positioned to cover the
     card body area (just below the tab row → bottom of folder body)
     when fully closed. Rotation pivots around this panel's BOTTOM
     edge (the fold line at the bottom of the cards' folder body),
     which in element-local terms means transform-origin: bottom
     center. The flap naturally lays in its CLOSED position
     (covering cards, face out, rotateX = 0) so the content reads
     right-side-up at rest; JS drives --ho-flap-angle from -100deg
     (open, hanging down + angled away) to 0deg (closed) as the user
     scrolls through the phase-4 zone.

     Position math (geometry mirrors the cards' folder body):
       folder height ≈ 56(tab) + clamp(540, 100vh-220, 748)(body)
       folder top    = (100vh - folderH) / 2
       tab bottom    = folder top + 56
       body bottom   = folder top + folderH    ← this is the hinge

     The panel's height = body height (so when closed it exactly
     overlays the card body). Its BOTTOM sits at body-bottom (the
     hinge), achieved with `bottom: calc(...)` of the empty space
     below the folder = (100vh - folderH) / 2. */
  .ho-method-flap-panel {
    /* Match the resized card body (75% of the original clamp) so the
       closing flap still snaps exactly over the card surface. */
    --ho-folder-h: calc(56px + clamp(405px, calc((100vh - 220px) * 0.75), 561px));
    /* Manila-folder cream from Figma 335:55023 (rgb(232, 224, 207)).
       Lighter and more neutral than the page parchment #F5F0EB so the
       closed folder reads as a distinct object resting on the page. */
    --ho-folder-fill: #E8E0CF;
    position: absolute;
    left: 0;
    right: 0;
    /* Section is 75vh now (matches cards). Panel bottom is the empty
       space below the folder inside the 75vh slot, mirroring the card
       body's flex-centered position so the closed flap snaps exactly
       over the card surface. */
    bottom: calc((75vh - var(--ho-folder-h)) / 2);
    height: clamp(405px, calc((100vh - 220px) * 0.75), 561px);
    box-sizing: border-box;

    background: var(--ho-folder-fill);
    border: 1px solid #000;
    /* Figma 335:55023 has cornerRadius 0 — sharp corners on all four
       sides for the master folder. The case-study folder bodies have
       rounded corners (12px) but the closed master folder is a clean
       rectangle. */
    border-radius: 0;
    /* Padding 56 vertical / 64 horizontal per Figma layout spec. */
    padding: 56px 64px;
    pointer-events: auto;

    /* Section-inner stretches to fill the panel; the ruler gets
       margin-top:auto to anchor at the bottom while the headline rides
       up to the top — see the .ho-method-flap-panel .ho-section-inner
       rule + .ho-ruler push-to-bottom below. */
    display: flex;
    flex-direction: column;
    justify-content: flex-start;

    transform-origin: bottom center;
    transform: rotateX(var(--ho-flap-angle, -100deg));
    transition: none;           /* JS drives this per scroll frame */
    backface-visibility: hidden;
    will-change: transform;
  }

  /* Right-edge tab on the closed master folder (Figma 335:55072).
     Sits above the panel's top edge flush right, like a manila-folder
     tab. Same silhouette family as the case-study tabs (slanted right
     edge merging into the body) but bigger: 457×70 vs 220×56. The
     tab is an absolute child of .ho-method-flap-panel so it inherits
     the panel's rotateX — folds with the front cover as one piece. */
  .ho-method-flap-tab {
    position: absolute;
    top: -56px;                /* sits just above the panel's top edge,
                                   same height as case-study tabs so all
                                   four tabs share a top alignment */
    right: 0;                  /* flush with the panel's right edge */
    width: 457px;
    height: 56px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    box-sizing: border-box;
    margin-bottom: -1px;       /* overlap panel border so they merge */
    backface-visibility: hidden;
    pointer-events: auto;
  }

  .ho-method-flap-tab-shape {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
  }
  .ho-method-flap-tab-fill {
    fill: var(--ho-folder-fill);   /* same as the folder body */
    stroke: none;
  }
  .ho-method-flap-tab-stroke {
    fill: none;
    stroke: #000;
    stroke-width: 1;
  }

  /* Lift the icon + label above the SVG */
  .ho-method-flap-tab > .ho-method-flap-tab-icon,
  .ho-method-flap-tab > .ho-method-flap-tab-label {
    position: relative;
    z-index: 1;
  }

  .ho-method-flap-tab-icon {
    display: inline-flex;
    align-items: flex-end;
    gap: 1px;
    height: 12px;
  }
  .ho-method-flap-tab-icon span {
    width: 5px;
    background: #1c1c1c;
  }
  .ho-method-flap-tab-icon span:nth-child(1) { height: 4px; }
  .ho-method-flap-tab-icon span:nth-child(2) { height: 8px; }
  .ho-method-flap-tab-icon span:nth-child(3) { height: 12px; }

  .ho-method-flap-tab-label {
    font-family: var(--font-body);
    font-size: 13px;
    font-weight: 500;
    color: #1c1c1c;
    letter-spacing: 0;
  }

  /* Method content lives directly inside the flap panel — re-use the
     section-inner padding for horizontal gutters, but trim the outer
     vertical padding since the flap already has its own padding.
     Make it a flex column that fills the panel so the ruler can be
     pushed to the bottom (margin-top:auto) while the headline rides
     up to the top — visually only the title moves. */
  .ho-method-flap-panel .ho-section-inner {
    padding-inline: 0;
    width: 100%;
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
  }

  /* Three auto-margins split the leftover height into three equal
     parts: above the headline, between the headline and the ruler,
     and below the ruler. Result: equal top and bottom padding on
     the title+ruler block, with the title-to-ruler gap staying
     proportionally the same. */
  .ho-method-flap-panel #method-headline {
    margin-top: auto;
  }
  .ho-method-flap-panel .ho-ruler {
    margin-top: auto;
    margin-bottom: auto;
  }

  /* Phase-4 tail spacer: extends the sticky parent so cards 1-3 stay
     pinned during the flap's scroll-driven close. Height = the scroll
     window that maps to flap progress 0→1 in js/home.js. The user exits
     the stack (and the footer slides in) at the exact moment the close
     reaches 100%, so there's no empty scroll past a finished close. */
  .ho-method-flap-tail {
    height: 75vh;
    width: 100%;
    pointer-events: none;
  }
}

/* ── Responsive ──────────────────────────────────────────── */

@media (max-width: 1023px) {
  .ho-project-body {
    grid-template-columns: 1fr;
    /* Remove the desktop two-column padding so the stacked text/image
       columns sit flush; we'll re-add small padding in the mobile rule. */
    padding: 0;
    gap: 0;
    min-height: auto;
  }
  .ho-project-image {
    min-height: 360px;
    border-top: 1px solid rgba(28, 28, 28, 0.08);
    aspect-ratio: 1;
    padding: 24px;
  }
  .ho-project-image-frame {
    max-width: 100%;
  }
}

@media (max-width: 768px) {
  .ho-project { margin-top: 60px; }
  .ho-project-tab { padding: 12px 28px 12px 20px; }
  .ho-project-tab-label { font-size: 12px; }
  .ho-project-text { padding: 36px 24px 36px 24px; }
  .ho-project-eyebrow { margin-bottom: 20px; font-size: 12px; }
  .ho-project-desc { font-size: 15px; }
  .ho-project-link { padding-top: 24px; font-size: 12px; }
  .ho-project-image { min-height: 280px; padding: 16px; }
  .ho-project-title { font-size: 28px; line-height: 1.1; margin-bottom: 18px; }
  .ho-project-image-label { top: 16px; right: 16px; font-size: 11px; }
}

/* ── 5. Method (mobile / fallback) ──────────────────────────
   Default rendering on tablet, mobile, and reduce-motion: the method
   reads as a normal section below the works. No "panel" sitting behind
   the three folders — the section is fully transparent so it sits
   directly on the parchment like every other section. The desktop
   sticky-stack (folder bottom flap) overrides this in the @media block
   below. */

.ho-method {
  padding-block: 56px;
  background: transparent;
}

.ho-method .ho-display { margin-top: 20px; margin-bottom: 28px; font-weight: 400; }

/* Ruler structure:
     [number 36px rust]
     ─┬───────────────┬─  continuous ruler line (1px) + 16px drop tick
     [name 12px ink]
     [desc 10px rust] */
.ho-ruler {
  --ho-ruler-line-y: 70px;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  position: relative;
}

.ho-ruler::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: var(--ho-ruler-line-y);
  height: 1px;
  background: var(--ho-rust);
}

.ho-ruler-step {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding-left: 4px;
}

/* Vertical drop tick — centered on the ruler line (8px above + 8px below). */
.ho-ruler-step::after {
  content: "";
  position: absolute;
  left: 0;
  top: calc(var(--ho-ruler-line-y) - 8px);
  width: 1px;
  height: 16px;
  background: var(--ho-rust);
}

.ho-ruler-num {
  font-family: var(--font-display);
  font-size: 36px;
  color: var(--ho-rust);
  line-height: 1;
}

/* Name's top margin opens the gap that contains the ruler line + drop tick. */
.ho-ruler-name {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--ho-ink);
  margin-top: 36px;
  margin-bottom: 4px;
}

.ho-ruler-desc {
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--ho-rust);
}

/* ─── Mobile: method section becomes a manila folder ───────────
   Below the desktop breakpoint the method section reads as a flat
   folder: a tan body with a tab sticking up from its top-left edge,
   the headline inside the body, and the five method steps as
   stacked cards. The desktop sticky-flap layout (>=1024px) lives
   inside its own min-width block and is unaffected. */

@media (max-width: 1023px) {
  /* Section outer — vertical breathing room only; horizontal gutter
     comes from the parent .ho-section-inner (24px on mobile), so the
     folder lines up edge-for-edge with the case-study cards above. */
  .ho-method {
    padding-block: 48px;
    padding-inline: 0;
  }

  /* Kill the works-section's bottom padding on mobile so the gap from
     the method card to the footer = the method's own 48px padding,
     matching the 48px gap between CHK and the method card above. */
  .ho-works { padding-bottom: 0; }

  /* Reset desktop sticky/3D properties as a safety net. The desktop
     rules live in (min-width:1024) so they shouldn't leak here, but
     this keeps the mobile cascade predictable. */
  .ho-method.ho-method-flap {
    position: static;
    height: auto;
    top: auto;
    transform: none;
    perspective: none;
    z-index: auto;
    pointer-events: auto;
  }

  /* Folder body. 1px black outline matches the case-study folder
     bodies above. No tab on mobile, so all four corners are rounded
     and the body sits in flow without reserving space above. */
  .ho-method-flap-panel {
    position: relative;
    margin-top: 0;
    padding: 28px 24px;
    background: #EBE2D6;
    border: 1px solid #000;
    border-radius: 14px;
    /* Reset desktop absolute positioning + 3D transform. */
    inset: auto;
    height: auto;
    transform: none;
    transition: none;
    display: block;
  }

  /* Tab is desktop-only chrome — hide on mobile. */
  .ho-method-flap-tab { display: none; }

  /* Section-inner inside the folder — strip its default 24px gutter
     so it doesn't double-pad against the folder body's own padding. */
  .ho-method-flap-panel .ho-section-inner {
    width: 100%;
    padding: 0;
    display: block;
  }

  /* Headline — smaller on mobile, sits at the top of the folder body. */
  #method-headline.ho-display--lg {
    margin: 0 0 24px;
    font-size: 30px;
    line-height: 1.05;
    font-weight: 400;
  }

  /* Steps stack vertically as cards — no ruler line, no tick marks. */
  .ho-method-flap-panel .ho-ruler,
  .ho-ruler {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: none;
    gap: 10px;
  }
  .ho-ruler::before,
  .ho-ruler-step::before,
  .ho-ruler-step::after { display: none; content: none; }

  /* Card: two-column grid — large rust numeral on the left, name +
     descriptor stacked on the right. 1px outline mirrors the
     case-study folder bodies above. */
  .ho-ruler-step {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: 14px;
    row-gap: 4px;
    align-items: center;
    padding: 14px 16px;
    background: #E6DDD1;
    border: 1px solid #000;
    border-radius: 10px;
    position: relative;
  }

  .ho-ruler-num {
    grid-column: 1;
    grid-row: 1 / span 2;
    align-self: center;
    font-family: var(--font-display);
    font-size: 28px;
    line-height: 1;
    color: var(--ho-rust);
    margin: 0;
  }

  .ho-ruler-name {
    grid-column: 2;
    grid-row: 1;
    font-family: var(--font-body);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ho-ink);
    margin: 0;
    line-height: 1;
  }

  .ho-ruler-desc {
    grid-column: 2;
    grid-row: 2;
    font-family: var(--font-body);
    font-size: 12px;
    font-style: italic;
    color: var(--ho-rust);
    margin: 0;
    line-height: 1.3;
  }

  /* "Get in touch" CTA — matches the drawer CTA style exactly so the
     two entry points feel like the same affordance: full-width dark-
     brown pill, centered label + arrow with an 8px gap. */
  .ho-method-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    height: 42px;
    margin-top: 16px;
    border-radius: 6px;
    background: #37281c;
    color: #FFFFFF;
    font-family: var(--font-body);
    font-size: 14px;
    font-weight: 500;
    text-decoration: none;
    transition: background-color 180ms ease, transform 180ms ease;
  }
  .ho-method-cta:hover { background: #4a3526; }
  .ho-method-cta:active { transform: translateY(1px); }
}

/* The CTA is mobile-only chrome (desktop method has its own sticky
   flap layout that this would disrupt). */
@media (min-width: 1024px) {
  .ho-method-cta { display: none; }
}

/* ── Mobile: tighter section padding overall ─────────────── */

@media (max-width: 768px) {
  .ho-section-inner { padding-inline: 24px; }
  /* Scale the body grid down so the mobile viewport shows a similar
     column density to desktop (≈8 cols at 400px instead of 4 at 100px),
     and bump the line opacity slightly so the grid reads on the smaller
     surface where each cell takes up more relative real estate. */
  body.home {
    background-image:
      linear-gradient(to right,  rgba(58, 35, 23, 0.06) 1px, transparent 1px),
      linear-gradient(to bottom, rgba(58, 35, 23, 0.06) 1px, transparent 1px);
    background-size: 48px 48px;
  }
}

/* ─────────────────────────────────────────────────────────────────────────
   Hero detail — wireframe-tube trefoil knot above the headline.
   The .ho-hero-detail wrapper is empty in markup; js/hero-detail.js mounts
   a <canvas> + the bottom state label inside it on init.
   ───────────────────────────────────────────────────────────────────────── */

.ho-hero-detail {
  position: relative;
  width: clamp(360px, 44vw, 560px);
  height: clamp(260px, 32vh, 400px);
  /* Negative bottom margin pulls the headline up so it sits tight
     against the bottom of the knot box (4px effective gap with the
     parent's 24px flex gap). */
  margin: 0 auto -24px;
  pointer-events: none;
}

.ho-hero-detail canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* (Removed) Hero exit transform/opacity. The hero used to translate
   up and fade out as the folder rose into its pinned state. With the
   folder peek + scale removed, the hero just scrolls naturally off
   the top — no extra animation needed. */

/* ─────────────────────────────────────────────────────────────────────────
   Hero headline crossfade — two stacked states (complexity → clarity)
   share one grid cell so they overlap pixel-perfect, fading between
   states in sync with the trefoil-knot untie/re-tangle. The .ho-hero-char
   rule below is required: js/hero-detail.js sets per-letter transforms
   and blur, and inline elements ignore transforms — without
   `display: inline-block` the JS silently does nothing visible.
   ───────────────────────────────────────────────────────────────────────── */

#hero-headline {
  display: grid;
  justify-items: center;
}

.ho-hero-title-state {
  grid-area: 1 / 1;
  white-space: nowrap;
  font-style: normal;
}

.ho-hero-title-complexity { color: #a8322a; }
.ho-hero-title-clarity    { color: #2c4d7a; }

.ho-hero-char {
  display: inline-block;
  /* No will-change here. The knot animation does set per-char transform
     /opacity/filter, but only when the knot is running (= scrollY ≤ 8).
     During scroll-induced morph the chars are static, and keeping ~30
     persistent composite layers (one per character × filter promotion)
     was eating compositor budget on Safari, causing the card-01 enlarge
     to feel choppy. The brief promotion at knot-resume is fine. */
  transform-origin: center 60%;
}

.ho-hero-char--space { width: 0.28em; }

/* ─────────────────────────────────────────────────────────────────────────
   Scroll cue — small monospace "CASE STUDIES ↓" label below the intro,
   gently bouncing arrow drawing the eye to the works section. Real
   anchor link for keyboard accessibility. Click does an instant jump
   (no `scroll-behavior: smooth` — that conflicts with the scroll-jack
   rAF tween in home.js, causing every wheel scroll to feel laggy).
   ───────────────────────────────────────────────────────────────────────── */

.ho-scroll-cue {
  /* Inside the .ho-section-inner stack — parent's 24px flex gap plus
     8px margin-top here = 32px from the intro paragraph above.
     Centering inherited from the parent's align-items: center. */
  margin-top: 8px;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;

  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: rgba(58, 35, 23, 0.5);
  text-decoration: none;
  transition: color 200ms ease;
}

.ho-scroll-cue:hover,
.ho-scroll-cue:focus-visible {
  color: rgba(58, 35, 23, 0.85);
}

.ho-scroll-cue-arrow {
  font-size: 18px;
  line-height: 1;
  animation: ho-scroll-cue-bounce 1.6s ease-in-out infinite;
}

@keyframes ho-scroll-cue-bounce {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(6px); }
}

@media (prefers-reduced-motion: reduce) {
  .ho-scroll-cue-arrow { animation: none; }
}

/* ─────────────────────────────────────────────────────────────────────────
   CHK chat — trust propagation diagram (Figma 395:866). Tree-style
   schematic: YOU → FRIEND → junction → 4 people → 4 businesses → drops,
   with a return rail looping back. Pure static SVG; animation layered
   on later.
   ───────────────────────────────────────────────────────────────────────── */
.ho-project-trustnet {
  display: block;
  width: 100%;
  height: 100%;
}

.ho-trustnet-wire,
.ho-trustnet-rail {
  fill: none;
  stroke: rgb(17, 17, 17);
  stroke-width: 1.05;
  stroke-linecap: square;
  stroke-linejoin: miter;
}

.ho-trustnet-node {
  fill: #EDE0D8;
  stroke: rgb(17, 17, 17);
  stroke-width: 1.05;
}

.ho-trustnet-junction {
  fill: rgb(17, 17, 17);
}

.ho-trustnet-business {
  fill: #EDE0D8;
  stroke: rgb(17, 17, 17);
  stroke-width: 1.05;
}

/* Peach tag background behind each text label so the label reads
   cleanly over the wires that pass through it (Figma 458:4234 etc.). */
.ho-trustnet-label-bg {
  fill: rgb(218, 201, 192);
  stroke: none;
}

.ho-trustnet-storefront-icon path {
  fill: none;
  stroke: rgb(17, 17, 17);
  stroke-width: 1.22;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.ho-trustnet-caption,
.ho-trustnet-label {
  font-family: "Space Mono", ui-monospace, monospace;
  font-weight: 400;
  fill: rgb(17, 17, 17);
  letter-spacing: 0.04em;
}

.ho-trustnet-caption {
  font-size: 11px;
}

.ho-trustnet-label {
  font-size: 12px;
}

.ho-trustnet-label--sm {
  font-size: 11px;
}

/* ─────────────────────────────────────────────────────────────────────────
   Trust Network animation layer — ambient CSS pulses + JS-driven packet
   light-up. Mirrors the architecture of the radar (Project 01) and
   protocol star (Project 02).

   Ambient layer runs forever even with no JS: YOU + FRIEND breathe on
   a 3s cycle (slight scale + opacity); the Junction dot has a faster,
   smaller heartbeat at the fork. Reduced-motion disables them via the
   @media block at the bottom.

   Active layer (data-anim="active" on the SVG, set by the IIFE in
   home.js) dims base wire stroke-opacity to ~0.6, then per-packet the
   JS toggles data-active on the wires it traverses; CSS transitions
   stroke-opacity back to fully lit and back. Reduce-motion never sets
   data-anim, so the static state stays exactly as designed.
   ───────────────────────────────────────────────────────────────────────── */

.ho-trustnet-node[data-trust="you"],
.ho-trustnet-node[data-trust="friend"] {
  transform-box: fill-box;
  transform-origin: center;
  animation: ho-trustnet-breathe 3s ease-in-out infinite;
}

.ho-trustnet-junction {
  transform-box: fill-box;
  transform-origin: center;
  animation: ho-trustnet-junction-pulse 1.8s ease-in-out infinite;
}

@keyframes ho-trustnet-breathe {
  0%, 100% { opacity: 1;    transform: scale(1.04); }
  50%      { opacity: 0.85; transform: scale(0.96); }
}

@keyframes ho-trustnet-junction-pulse {
  0%, 100% { transform: scale(1.05); }
  50%      { transform: scale(0.95); }
}

/* When the JS animation is active, dim resting wires so packet light-up
   reads as a contrast bump. Without data-anim, wires stay at full
   opacity (the natural / reduce-motion state). */
.ho-project-trustnet[data-anim="active"] .ho-trustnet-wire,
.ho-project-trustnet[data-anim="active"] .ho-trustnet-rail {
  stroke-opacity: 0.6;
  transition: stroke-opacity 220ms ease-out;
}

.ho-project-trustnet[data-anim="active"] .ho-trustnet-wire[data-active],
.ho-project-trustnet[data-anim="active"] .ho-trustnet-rail[data-active] {
  stroke-opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .ho-trustnet-node[data-trust="you"],
  .ho-trustnet-node[data-trust="friend"],
  .ho-trustnet-junction {
    animation: none;
  }
}
