/* ==========================================================================
   Hero — layered rebuild of Figma node 931:2939 (Jas-Draft / 首屏)
   Design artboard 1512 × 769. Everything positioned in design coords via a
   container-query-scaled scene, so text stays selectable (slogan/labels are
   real HTML) and each layer can be animated independently.
   Layer z-order (bottom→top):
     bg → glass fill → image/video content → svg lines (rays + rect borders)
     → svg main-points → copy labels → slogan
   The wireframe LINES sit ABOVE the content so every frame border (incl. the
   frame-3 video) is drawn on top of its panel; only the nodes + copy tags rise
   above the lines. The glass FILL stays below the content.
   Loaded last; supersedes the legacy .hero* rules in styles.css/responsive.css.
   ========================================================================== */

.hero-scene {
  position: relative;
  width: 100%;
  background: #f3f3f1;
  overflow: hidden;
  line-height: 0;
}

/* Content stage: 1100px grid (matching .section-shell), centred, keeps the
   1512×769 design ratio. The paper .hero-bg bleeds full-bleed outside it. */
.hero-content {
  position: relative;
  z-index: 2;
  width: min(1100px, calc(100% - 48px));
  margin: 0 auto;
  aspect-ratio: 1120.5 / 769;
  container-type: inline-size;   /* enables cqw units for text scaling */
  line-height: 0;
}

.hero-bg, .hero-content > *, .hero-tilt-group > * { position: absolute; }

/* The constructed diagram behaves as one shallow physical plane. Copy, blue
   labels and paper texture stay locked to the viewport while this group tilts. */
.hero-tilt-group {
  --hero-tilt-x: 0deg;
  --hero-tilt-y: 0deg;
  inset: 0;
  z-index: 2;
  /* Exact origin node: 233.5 / 1512, 676.5 / 769. The ray source remains
     pinned while the whole constructed system opens in depth around it. */
  transform-origin: 2.454% 87.971%;
  transform: perspective(1320px) rotateX(var(--hero-tilt-x)) rotateY(var(--hero-tilt-y)) scale(1.008);
  transform-style: preserve-3d;
  will-change: transform;
  pointer-events: none;
}
.hero-tilt-group .card-hit { pointer-events: auto; }

/* --- background texture ------------------------------------------------- */
.hero-bg {
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 1;
}

/* --- full-scene SVG overlays (grid + points) ---------------------------- */
.hero-svg {
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.hero-grid   { z-index: 2; }   /* glass fill only — below content */
.hero-lines  { z-index: 4; }   /* rays + rect borders — above content/video */
.hero-points { z-index: 5; }
.hero-origin { z-index: 6; }   /* origin marker: outside the stage, full size */

.hero-grid path, .hero-lines path { fill: none; stroke: rgba(0,0,0,.22); stroke-width: 1; vector-effect: non-scaling-stroke; }
.hero-lines .ray  { stroke-linecap: round; }
.hero-lines .rect { stroke: rgba(0,0,0,.22); stroke-width: 1; vector-effect: non-scaling-stroke; }
/* frame-1 glass fill: a single translucent parallelogram so the outline is the
   rect stroke alone (no doubled edge from a bordered PNG). frames 2/3 carry the
   glass tint baked into their content raster, so no fill path is needed there. */
.hero-grid .glass { fill: rgba(210,229,242,.55); stroke: none; }
/* frame-1 glass fill depends on the round: round-2's raster carries its own
   baked blue panel (hide the fill to avoid a doubled tint); round-1's content
   is the line-art bird with NO baked blue, so the translucent fill is shown. */
.hero-scene.is-r2 .hero-grid .glass1 { display: none; }
.hero-scene.is-r1 .hero-grid .glass1 { display: block; }
.hero-points circle { fill: rgba(0,0,0,.42); }

/* --- glass panels + image content --------------------------------------- */
/* Figma exports are skewed parallelograms baked into an opaque raster, so the
   out-of-shape corners carry the artboard colour. multiply drops that flat
   ground (light×bg → bg) while keeping the dark line-art and letting the rays
   read through the glass — matching the transparent look of the source. */
.hero-layer { display: block; mix-blend-mode: multiply; }
.hero-glass { z-index: 3; }
.hero-img   { z-index: 3; }

/* positions (design coords → %). left/top/width computed from node bbox,
   export centred on the shape (export px / 2 = logical size). */
/* frame-1 round-2 card raster (hero-img-1-r2): a full pre-skewed card face
   (blue panel + starburst baked in). Sized so the baked blue panel lands on
   frame 1's parallelogram bbox; object-fit:fill maps it exactly, the star rays
   overflow the panel and the white ground is dropped by multiply. */
/* frame-1 content geometry depends on the round.
   round-2: a full pre-skewed card raster (blue panel + starburst baked in),
   sized so the baked panel lands on frame 1's parallelogram bbox.
   round-1: the small line-art bird, sitting inside the (separately drawn) glass
   panel at its original placement. */
.i1 { object-fit: fill; }
.hero-scene.is-r2 .i1 { left: 21.311%; top: 68.205%; width: 10.869%; height: 19.766%; }
.hero-scene.is-r1 .i1 { left: 23.511%; top: 75.143%; width: 7.229%; height: auto; object-fit: contain; }
.g2 { left: 47.702%; top: 44.629%; width: 10.483%; }
.i2 { left: 46.230%; top: 44.986%; width: 13.655%; }
/* The raster export has opaque white corners outside its skewed card face.
   Clip those corners so a 3D rotation never reveals its rectangular canvas. */
.hero-scene .i2 { clip-path: polygon(0 0,100% 22.66%,100% 100%,0 77.64%); }
.g3 { left: 70.387%; top: 15.740%; width: 17.560%; }

/* frame-3 live video. The BOX is the parallelogram's pre-skew rectangle
   (rect3 bbox: left 1067.5, top 114.5, w 259, h 435 @1512×769); the inner
   <video> is sheared into rect3 by skewY (see below). Box grows from frame 2's
   centre, matching the still it replaces. */
.hero-video-box.v3 {
  left: 76.884%;    /* 1067.5 / 1512 */
  top: 14.889%;     /* 114.5 / 769  */
  width: 23.115%;   /* 259 / 1512   */
  height: 56.567%;  /* 435 / 769    */
  z-index: 3;
  line-height: 0;
}
.hero-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* map the box rectangle onto rect3's parallelogram: vertical left/right edges,
     top/bottom edges sloped down-right by 126px over 259px → skewY 25.94°,
     sheared about the box's top-left so TL stays put. */
  transform: skewY(25.94deg);
  transform-origin: 0 0;
}

/* --- copy labels (real text) --------------------------------------------
   Figma copy1 (931:2941): Source Han Serif CN Bold (→ Noto Serif SC 700),
   15.556px, tracking -0.031em, colour #f0f0f0 on #00a9ff, sharp corners,
   padding 3.958px / 9.894px. A crisp detection tag, not a pill. */
.hero-label {
  z-index: 6;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.262cqw 0.654cqw;   /* 3.958 / 9.894 px @1512 */
  border-radius: 0;             /* Figma: square corners */
  background: #00a9ff;
  font-family: "Noto Serif SC", serif;
  font-weight: 700;
  font-size: 1.029cqw;          /* 15.556px */
  line-height: 1.25;            /* 19.445 / 15.556 */
  letter-spacing: -0.031em;     /* -0.4861 / 15.556 */
  color: #f0f0f0;
  white-space: nowrap;
}
.lbl1 { left: 0.089%; top: 75.42%; }
.lbl2 { left: 17.941%; top: 59.03%; }
.lbl3 { left: 60.326%; top: 89.99%; }
.lbl4 { left: 93.076%; top: 25.36%; }

/* --- slogan (real text) --------------------------------------------------
   Figma sologan (926:2286): Source Han Serif CN Bold (→ Noto Serif SC 700),
   56px / line-height 66px; "everyone" in Signal Blue; sub PingFang SC 24px. */
.hero-slogan { left: 0%; top: 13.004%; width: 53.191%; z-index: 7; line-height: 1; }
.hero-slogan h1 {
  margin: 0;
  font-family: "Noto Serif SC", serif;
  font-weight: 700;
  font-size: 3.70cqw;      /* 56px @1512 */
  line-height: 1.1786;     /* 66 / 56 */
  letter-spacing: 0;
  color: #1a1a1a;
}
.hero-slogan h1 em { font-style: normal; color: #00a9ff; }
.hero-slogan .sub {
  margin: 1.587cqw 0 0;    /* 24px gap */
  font-family: "PingFang SC", "Noto Sans SC", sans-serif;
  font-weight: 400;
  font-size: 1.587cqw;     /* 24px */
  line-height: 1.722;      /* 41.327 / 24 */
  color: rgba(0,0,0,.48);
}
/* line-reveal mask: pad below the baseline so p/y descenders are never
   clipped, and pull the next line back up so the 66px rhythm is unchanged.
   nowrap + fit-content keeps each line unbroken (never 3 lines) while the
   horizontal overflow:hidden only clips the vertical reveal, not the glyphs. */
.hero-line { display: block; width: fit-content; white-space: nowrap; overflow: hidden; padding-bottom: 0.2em; margin-bottom: -0.2em; }
.hero-line > span { display: block; }

/* ==========================================================================
   Entrance animation — sequential chained growth
   The origin node blinks awake first. Then the structure is BUILT OUTWARD one
   frame at a time: frame 1 (+ the ray segments leaving the origin) grows out of
   the origin point; frame 2 (+ its line segments) then grows out of frame 1's
   position; frame 3 grows out of frame 2's. Each frame's group scales up from
   the previous frame's location (its transform-origin), so the whole wireframe
   appears to generate itself left→right. Detection tags flash on (twice) as
   each frame settles. Slogan keeps its original line-reveal. ~2.9s total.

   SVG groups scale in viewBox user units (transform-box: view-box), so their
   transform-origin is a scene coordinate. Content rasters (i1/i2/i3) are NOT
   wrapped — they stay direct children of the scene and keep mix-blend-mode:
   multiply blending against the untouched hero-bg — so each is scaled on its
   own with a transform-origin (as a % of its box) pointing at the same anchor.
   ========================================================================== */

/* per-frame anchors (scene coordinates via transform-box: view-box).
   frame 1 grows from the origin. frames 2/3 build in two parts:
   - the RAYS each shoot out of one of the PREVIOUS frame's four corner nodes
     (transform-origin set inline per path = that node), and
   - the frame body (rect + nodes + content) sweeps out from the previous
     frame's centre. */
.hero-grid .grp1, .f1lines,
.f2ray, .f3ray, .f2rect, .f3rect { transform-box: view-box; }
.hero-grid .grp1, .f1lines { transform-origin: 27.5px 676.5px; }   /* frame 1 body ← origin node */
.f2rect { transform-origin: 289px 600.25px; }  /* frame 2 outline ← frame 1 centre */
.f3rect { transform-origin: 594px 511.25px; }  /* frame 3 outline ← frame 2 centre */
/* .f2ray / .f3ray transform-origin is set inline: each = its own start node.
   corner nodes don't scale with a group any more — each blinks in place. */

/* content rasters: transform-origin (% of each box) points at the same anchor */
.i1 { transform-origin: -173.48% 100%; }      /* → origin (233.5,676.5)        */
.i2 { transform-origin: -149.7% 76.8%; }        /* → frame 1 centre (495,600.25) */
.v3 { transform-origin: -103.28% 91.21%; }      /* → frame 2 centre (800,511.25) */

/* initial hidden states */
.hero-line > span { transform: translateY(120%); }
.hero-slogan .sub { opacity: 0; transform: translateY(0.9cqw); }
.hero-grid .grp1, .hero-img, .v3, .f1lines,
.f2ray, .f3ray, .f2rect, .f3rect { opacity: .12; transform: scale(.04); }
/* every node — the origin and all frame corners — blinks awake in place */
.hero-points circle, .hero-origin .p-origin { opacity: 0; transform-box: fill-box; transform-origin: center; }
.hero-label { opacity: 0; }

@keyframes lineUp { to { transform: translateY(0); } }
@keyframes fadeUp { to { opacity: 1; transform: translateY(0); } }
/* grow a frame out of its anchor: faint speck → full size + opaque */
@keyframes growOut {
  0%   { opacity: .12; transform: scale(.04); }
  55%  { opacity: 1; }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes originBlink {
  0% { opacity: 0; transform: scale(0.4); }
  25% { opacity: 1; transform: scale(1.1); }
  45% { opacity: .25; transform: scale(1); }
  65% { opacity: 1; transform: scale(1.25); }
  100% { opacity: 1; transform: scale(1); }
}
/* copy labels: a single calm fade-in that lands with their frame's nodes */
@keyframes fadeIn { to { opacity: 1; } }

@media (prefers-reduced-motion: no-preference) {
  .hero-line > span { animation: lineUp .78s cubic-bezier(.16,.84,.44,1) both; }
  .hero-line:nth-child(1) > span { animation-delay: .15s; }
  .hero-line:nth-child(2) > span { animation-delay: .30s; }
  .hero-slogan .sub { animation: fadeUp .6s ease both .55s; }

  /* 1) the origin point blinks awake (two pulses) */
  .hero-origin .p-origin { animation: originBlink .9s ease both .30s; }

  /* 2) frame bodies build outward in sequence — outline + glass fill + rays +
        pattern/video all fire on ONE beat out of the previous frame (rays from
        its four nodes, body from its centre). The three beats run on a tight,
        slightly-overlapping cadence (step .38s < duration .48s) so the next ray
        batch starts growing just as the previous one visually lands — the beam
        reads as one continuous emission from the origin outward, no pause. */
  .hero-grid .grp1, .f1lines { animation: growOut .48s cubic-bezier(.22,.9,.3,1) both .55s; }
  .i1 { animation: growOut .48s cubic-bezier(.22,.9,.3,1) both .55s; }

  .f2ray, .f2rect, .i2 { animation: growOut .48s cubic-bezier(.22,.9,.3,1) both .93s; }

  .f3ray, .f3rect, .v3 { animation: growOut .48s cubic-bezier(.22,.9,.3,1) both 1.31s; }

  /* 2b) every corner node blinks twice as its frame lands — same beat as the
         body, same two-pulse signal-lock the origin uses. */
  .hero-points .grp1 circle { animation: originBlink .9s ease both .55s; }
  .hero-points .grp2 circle { animation: originBlink .9s ease both .93s; }
  .hero-points .grp3 circle { animation: originBlink .9s ease both 1.31s; }

  /* 3) copy tags: no blink — each fades in once, alongside its frame's nodes */
  .lbl1, .lbl2 { animation: fadeIn .45s ease both .60s; }
  .lbl3 { animation: fadeIn .45s ease both .98s; }
  .lbl4 { animation: fadeIn .45s ease both 1.36s; }
}

/* reduced motion → final state, no animation */
@media (prefers-reduced-motion: reduce) {
  .hero-line > span { transform: none; }
  .hero-slogan .sub { opacity: 1; transform: none; }
  .hero-grid .grp1, .hero-img, .v3, .f1lines,
  .f2ray, .f3ray, .f2rect, .f3rect { opacity: 1; transform: none; }
  .hero-points circle, .hero-origin .p-origin { opacity: 1; transform: none; }
  .hero-label { opacity: 1; transform: none; }
}

/* ==========================================================================
   Click-to-swap with a dither transition
   Clicking ANY of the three blue panels toggles all three cards' content to
   the other round. The content makes a tiny two-pixel jitter while a light,
   sparse ordered-dither veil flickers over each panel. The content swaps at
   the midpoint, then both effects clear quickly. It should feel like a brief
   signal shake, not a dark dissolve covering the artwork.

   Each veil is a rectangle over its panel's bbox, clipped to the parallelogram
   so only the card face dithers (the crisp frame border + corner nodes, which
   sit on higher layers, stay put on top). The veil is a sparse 8×8 tile in a
   cool pale grey-blue; a short stepped keyframe changes its position while
   the panel content jitters independently.
   ========================================================================== */

/* transparent click catchers over each panel's bbox (rectangular hull of the
   parallelogram). They sit above everything so clicks always land; the whole
   panel reads as interactive. */
.card-hit { z-index: 8; cursor: pointer; background: transparent; }
.hit1 { left: 22.718%; top: 68.21%; width: 6.153%;  height: 19.77%; }
.hit2 { left: 46.184%; top: 44.93%; width: 13.656%; height: 43.04%; }
.hit3 { left: 76.802%; top: 14.89%; width: 23.210%; height: 73.08%; }

/* dither veil: sits at the content layer (z3) but later in the DOM, so it
   paints over the pattern rasters/video while the crisp SVG border (z4), nodes
   (z5) and labels (z6) stay on top. Hidden at rest. */
.dither-veil {
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  background-color: rgba(225, 235, 238, .08);
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='8'%20height='8'%20viewBox='0%200%208%208'%20shape-rendering='crispEdges'%3E%3Cg%20fill='%239ab5be'%3E%3Crect%20x='0'%20y='0'%20width='1'%20height='1'/%3E%3Crect%20x='4'%20y='0'%20width='1'%20height='1'/%3E%3Crect%20x='2'%20y='2'%20width='1'%20height='1'/%3E%3Crect%20x='6'%20y='2'%20width='1'%20height='1'/%3E%3Crect%20x='1'%20y='5'%20width='1'%20height='1'/%3E%3Crect%20x='5'%20y='5'%20width='1'%20height='1'/%3E%3Crect%20x='3'%20y='7'%20width='1'%20height='1'/%3E%3Crect%20x='7'%20y='7'%20width='1'%20height='1'/%3E%3C/g%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 16px 16px;    /* 8-unit tile → sparse 2px pixels */
  image-rendering: pixelated;
}
/* same bbox as the hit areas, clipped to each panel's parallelogram so the
   dither only covers the card face (top edge slopes to ~22.5%, bottom ~77.5%) */
.dv1 { left: 22.718%; top: 68.21%; width: 6.153%;  height: 19.77%; clip-path: polygon(0% 0%, 100% 22.37%, 100% 100%, 0% 76.97%); }
.dv2 { left: 46.184%; top: 44.93%; width: 13.656%; height: 43.04%; clip-path: polygon(0% 0%, 100% 22.66%, 100% 100%, 0% 77.64%); }
.dv3 { left: 76.802%; top: 14.89%; width: 23.210%; height: 73.08%; clip-path: polygon(0% 0%, 100% 22.42%, 100% 100%, 0% 77.40%); }

@keyframes ditherPulse {
  0%   { opacity: 0;   background-position: 0 0; }
  22%  { opacity: .30; background-position: 4px -2px; }
  50%  { opacity: .56; background-position: -5px 4px; }  /* swap here */
  74%  { opacity: .26; background-position: 3px -4px; }
  100% { opacity: 0;   background-position: 0 0; }
}
/* Stepped timing keeps the texture digital, while the low opacity and tiny
   displacement keep the artwork readable throughout the transition. The
   content jitter is driven with Web Animations in app.js so it never replaces
   or restarts the hero's one-time CSS growOut entrance animation. */
.hero-scene.dithering .dither-veil { animation: ditherPulse 460ms steps(3, end) both; }

/* reduced motion: no pulse — content just swaps (JS skips the veil timing) */
@media (prefers-reduced-motion: reduce) {
  .hero-scene.dithering .dither-veil { animation: none; opacity: 0; }
}


@container (max-width: 640px) {
  .hero-slogan h1 { font-size: 3.55cqw; }
}

/* responsive side padding — matches .section-shell breakpoints */
@media (max-width: 800px) {
  .hero-content { width: min(calc(100% - 32px), 1100px); }
}
@media (max-width: 480px) {
  .hero-content { width: calc(100% - 24px); }
}
