o2o-negosium-original/landing/app/components/sections/hero-dataflow.tsx
Haewon Kam d2f1a8f6f4 [fix] landing: 히어로 배경이 탁하고 흐리던 문제 — 스크림 축소 + 재인코딩
탁함: 스크림이 원인이었다. stage-deep(#03060F) 을 90/60/95 알파로 전면에 깔아
가장 옅은 지점에서도 영상이 40% 만 남았다. 그 값은 헤드라인 둘째 줄이 회색(/60)이던
시절 그 글자를 살리려고 넣은 것인데, 헤드라인이 흰색이 된 뒤로는 근거가 없어졌다.
게다가 중앙이 가장 옅어서 — 정작 글자가 있는 자리 — 보호 목적으로도 방향이 틀렸다.

흐림: 1280x720 crf 30 은 전면 배경으로 무르다. 1600x900 crf 24 로 재인코딩.
업스케일 1.19배 → 0.96배(다운스케일)로 바뀌어 선명도가 올라간다. 631K → 1.7M.

스크림을 걷었더니 이번엔 리드 문단(17px)이 밝은 입자 위에서 안 읽혔다. 헤드라인은
흰색 74px 이라 버티지만 본문은 못 버틴다. 전면을 덮으면 영상이 탁하고, 다 걷으면
본문이 안 읽힌다 — 둘 다 만족하려면 덮는 범위를 글자가 있는 자리로 좁혀야 한다.

  세로 그라데이션  50 / 투명 / 45   헤더 nav 와 하단 전환만 보호
  중앙 방사형      85 → 55 → 투명   카피 뒤만 국소적으로

색은 color-mix 로 --color-stage-deep 를 참조한다. 하드코딩하면 토큰이 둘이 된다.

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

128 lines
7.3 KiB
TypeScript

import { useState } from "react"
import { ArrowRight } from "lucide-react"
import { Button } from "@/components/ui/button"
import { DemoRequestModal } from "@/components/ui/demo-request-modal"
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() {
const [demoOpen, setDemoOpen] = useState(false)
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 에서 색이 바뀌면
그 지점이 가로선으로 읽힌다.
알파는 90/60/95 였다가 낮췄다. 그 값은 헤드라인 둘째 줄이 회색(/60)이던 시절
그 글자를 살리려고 깐 것인데, 가장 옅은 지점에서도 영상이 40% 만 남아서
배경 전체가 탁해졌다. 헤드라인이 흰색이 된 지금은 이만큼 덮을 이유가 없다.
구간별로 필요한 만큼만 준다 — 위는 헤더 nav(작은 회색 글자), 가운데는 흰
헤드라인이라 가장 옅게, 아래는 다음 섹션으로 넘어가는 암전(h-32)이 따로 있다. */}
<div
aria-hidden
className="absolute inset-0 pointer-events-none bg-linear-to-b from-stage-deep/50 via-transparent to-stage-deep/45"
/>
{/* 카피 뒤만 국소적으로 누른다.
전면을 균일하게 덮으면 글자는 살지만 영상이 통째로 탁해진다. 반대로 다 걷으면
헤드라인(흰색 74px)은 버텨도 리드 문단(17px)이 밝은 입자 위에서 읽히지 않는다.
둘 다 만족하는 유일한 방법은 덮는 범위를 글자가 있는 자리로 좁히는 것이다.
가장자리는 영상이 그대로 보이고 가운데만 어두워진다. */}
<div
aria-hidden
className="absolute inset-0 pointer-events-none"
style={{
background:
"radial-gradient(58% 42% at 50% 45%," +
" color-mix(in srgb, var(--color-stage-deep) 85%, transparent) 0%," +
" color-mix(in srgb, var(--color-stage-deep) 55%, transparent) 45%," +
" transparent 72%)",
}}
/>
{/* 아래 라이트 섹션과의 경계를 부드럽게 */}
<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톤을 쓰지 않는다. 예전 캔버스 배경은 하단 밴드에만 입자가 있어 헤드라인
뒤가 거의 단색이었고, 그때는 투명도 60% 로 낮춘 둘째 줄도 읽혔다. 배경이 영상으로
바뀌면서 밝은 요소가 글자 뒤로 지나가 그 대비가 무너졌다 — 배경이 움직이는 위에서는
가독성이 조판 장치보다 우선한다. 2톤은 최종 CTA 에 남아 있다(그쪽은 배경이 정적). */}
<Typography variant="stageDisplay">
<span className="text-white">협력사 전원과 1:1로,</span>
<br />
<span className="text-white">동시에 협상합니다.</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>
{/* 2차 CTA 는 상담 신청(#contact-section)과 목적지가 겹쳐 사실상 같은 버튼이었다.
데모 요청으로 갈라서, 고관여 폼(상담 신청) 앞단에 저관여 리드 캡처를 둔다. */}
<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 type="button" variant="stageGhost" size="stageRound" onClick={() => setDemoOpen(true)}>
데모 요청
</Button>
</div>
</div>
</div>
<DemoRequestModal open={demoOpen} onClose={() => setDemoOpen(false)} />
</section>
)
}