o2o-negosium-original/landing/app/components/sections/hero-dataflow.tsx
Haewon Kam f214fccc49 [feat] landing: 히어로 배경을 캔버스에서 루프 영상으로 교체
배경이 설명을 지던 구조를 걷어냈다. 예전 캔버스는 negotium 파이프라인을 직접
그렸지만(입자 240개·6단계 서사·무리 라벨), 이제 배경은 배경으로만 쓴다.
설명은 카피와 아래 섹션이 맡는다.

영상: hero_veo31 0~5초를 크로스페이드 루프로. 그냥 자르면 끝에서 처음으로 튀어서
끝 0.8초를 앞 0.8초와 겹쳐 이음새를 없앴다(결과 4.25초, 첫↔끝 프레임 RMS 18.6/255).
1280x720 h264 631K + 포스터 58K. webm 은 뺐다 — VP9 이 1.0M 으로 h264 보다 커서
둘을 다 실을 이유가 없다.

- CLUSTER_ANCHORS 라벨 제거. 좌표 소스가 사라진 라벨은 화면비마다 어긋나기만 하고
  아무것도 설명하지 못한다.
- DataFlowPhase 를 orbit-ring 으로 이관 후 data-flow-canvas.tsx(500줄) 삭제.
  단계 이름을 실제로 쓰는 건 강화학습 섹션의 순환 링뿐이다.
- 영상은 가로 화면에서만 튼다. 16:9 를 세로에 object-cover 로 깔면 가로가 62% 잘리고
  남은 38% 가 1.7배로 확대된다(848x1220 실측) — 입자가 거대한 보케로 뭉개져 배경이
  아니라 노이즈가 된다. 세로에서는 무대 그라데이션만 남긴다. 모바일 대역폭도 아낀다.
- prefers-reduced-motion 도 같은 경로로 영상을 감춘다. 포스터가 뒤를 받친다.
- 스크림은 색 하나(stage-deep)로 알파만 움직인다. 중간 stop 에서 색을 바꿨더니
  그 지점이 가로선으로 읽혔다.
- 밴드 개념이 사라져 히어로 레이아웃을 카피 중앙 정렬로 되돌렸다. 직전 커밋의
  --band-top 기준 높이 계산은 더 이상 필요 없다.

검증: 1440x860 크롭 7%, 375x812 영상 숨김·제목 헤더 여유 152px, 콘솔 에러 없음.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 10:24:21 +09:00

94 lines
5.1 KiB
TypeScript

import { ArrowRight } from "lucide-react"
import { Button } from "@/components/ui/button"
import { CTA_GAP, DISPLAY_LEAD_GAP } from "@/components/ui/section-heading"
import { Typography } from "@/components/ui/typography"
/**
* 다크 무대 히어로 — 배경 루프 영상.
*
* 예전에는 캔버스가 negotium 파이프라인을 직접 그렸고(입자 240개·6단계 서사·무리 라벨),
* 배경이 곧 설명이었다. 지금은 배경을 배경으로만 쓴다 — 설명은 카피와 아래 섹션이 맡는다.
* 그래서 무리 라벨(CLUSTER_ANCHORS)도 함께 걷어냈다. 좌표 소스가 사라진 라벨은
* 화면비가 바뀔 때마다 어긋나기만 하고 아무것도 설명하지 못한다.
*
* 배경이 의미를 지지 않으므로 카피 가독성이 1순위다. 그래서:
* - 영상 위에 스크림을 깐다. 예전 캔버스는 하단 밴드에만 입자가 있어 상단이 비었지만,
* 이 영상은 화면 전체에 밝은 요소가 흩어져 흰 글자가 묻힌다.
* - 카피는 화면 중앙 정렬. 밴드 개념이 없으니 상하로 나눌 이유도 없다.
*/
export function HeroDataFlow() {
return (
<section
data-stage-hero
className="stage-bg relative min-h-screen text-on-stage flex flex-col overflow-hidden"
>
{/* 영상은 가로 화면에서만 튼다.
16:9 소스를 세로 화면에 object-cover 로 깔면 가로가 62% 잘리고 남은 38% 가
1.7배로 확대된다(848x1220 실측) — 데이터 입자가 거대한 보케로 뭉개져서
배경이 아니라 노이즈가 된다. 캔버스는 뷰포트에 맞춰 그렸으니 없던 문제다.
세로에서는 무대 그라데이션(stage-bg)만 남긴다. 그게 브랜드 배경이라 빈 화면이 아니다.
prefers-reduced-motion 에서도 같은 이유로 영상을 감춘다 — 포스터가 뒤를 받친다. */}
<div
aria-hidden
className="absolute inset-0 hidden landscape:block motion-reduce:hidden bg-cover bg-center"
style={{ backgroundImage: "url(/gifs/hero_loop.jpg)" }}
>
<video
className="w-full h-full object-cover"
src="/gifs/hero_loop.mp4"
poster="/gifs/hero_loop.jpg"
autoPlay
muted
loop
playsInline
preload="metadata"
/>
</div>
{/* 카피 가독성용 스크림. 색은 하나(stage-deep)로 두고 알파만 움직인다 —
중간 stop 에서 색이 바뀌면 그 지점이 가로선으로 읽힌다(stage/70 을 끼웠을 때 발생). */}
<div
aria-hidden
className="absolute inset-0 pointer-events-none bg-linear-to-b from-stage-deep/90 via-stage-deep/60 to-stage-deep/95"
/>
{/* 아래 라이트 섹션과의 경계를 부드럽게 */}
<div aria-hidden className="absolute inset-x-0 bottom-0 h-32 bg-linear-to-b from-transparent to-stage pointer-events-none" />
{/* pt-[104px] 는 fixed 헤더 회피용 바닥. 그 아래 남은 공간에서 수직 중앙 정렬한다. */}
<div className="relative z-10 w-full flex-1 pt-[104px] pb-16 flex items-center">
<div className="max-w-4xl mx-auto px-6 w-full text-center">
{/* 히어로 카피에는 진입 애니메이션을 걸지 않는다.
여기가 LCP 요소라 opacity:0 으로 시작하면 하이드레이션 전까지 첫 화면이 비고,
광고 유입처럼 JS 가 느린 환경에서 그대로 손실이 된다. 모션은 배경 영상이 맡는다. */}
{/* 2톤 헤드라인의 약한 쪽은 별도 회색이 아니라 같은 흰색의 투명도로 낮춘다.
네이비 위에 라벤더 회색을 얹으면 화면에 색상각이 하나 더 생겨 탁해진다. */}
<Typography variant="stageDisplay">
<span className="text-white"> 1:1로,</span>
<br />
<span className="text-on-stage-soft/60"> .</span>
</Typography>
{/* 히어로는 h1 이어야 해서 SectionHeading(h2 고정)을 못 쓴다. 대신 간격만은
같은 상수를 물려 최종 CTA 와 리듬이 어긋나지 않게 한다. */}
<p className={`${DISPLAY_LEAD_GAP} text-base sm:text-lg text-on-stage-soft leading-relaxed break-keep max-w-2xl mx-auto`}>
·· . ,
.
</p>
<div className={`${CTA_GAP} flex flex-col sm:flex-row gap-3 justify-center`}>
<Button href="#how-it-works" variant="stage" size="stageRound" className="group">
<span> </span>
<ArrowRight className="w-4 h-4 transition-transform group-hover:translate-x-0.5" />
</Button>
<Button href="#contact-section" variant="stageGhost" size="stageRound">
</Button>
</div>
</div>
</div>
</section>
)
}