manual save(2026-01-23 14:38)
This commit is contained in:
328
src/components/FlipBook.tsx
Normal file
328
src/components/FlipBook.tsx
Normal file
@@ -0,0 +1,328 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
type FlipBookSheet = {
|
||||
id: string
|
||||
front: React.ReactNode
|
||||
back?: React.ReactNode
|
||||
}
|
||||
|
||||
type FlipBookProps = {
|
||||
title: string
|
||||
sheets: FlipBookSheet[]
|
||||
className?: string
|
||||
}
|
||||
|
||||
const TRANSITION_MS = 720
|
||||
|
||||
function usePrefersReducedMotion() {
|
||||
const [reduced, setReduced] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const media = window.matchMedia('(prefers-reduced-motion: reduce)')
|
||||
const update = () => setReduced(media.matches)
|
||||
update()
|
||||
|
||||
media.addEventListener('change', update)
|
||||
return () => media.removeEventListener('change', update)
|
||||
}, [])
|
||||
|
||||
return reduced
|
||||
}
|
||||
|
||||
const PageHalf: React.FC<{ side: 'left' | 'right'; children?: React.ReactNode }> = ({ side, children }) => {
|
||||
const isLeft = side === 'left'
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
'absolute inset-y-0 w-1/2 overflow-hidden',
|
||||
isLeft ? 'left-0 rounded-l-[22px]' : 'left-1/2 rounded-r-[22px]',
|
||||
].join(' ')}
|
||||
>
|
||||
<div
|
||||
className={[
|
||||
'h-full',
|
||||
'bg-[color-mix(in_srgb,var(--tf-bg)_55%,white)]',
|
||||
'shadow-[inset_0_1px_0_rgba(255,255,255,0.65)]',
|
||||
isLeft
|
||||
? 'shadow-[inset_-18px_0_40px_rgba(0,0,0,0.06),inset_0_1px_0_rgba(255,255,255,0.65)]'
|
||||
: 'shadow-[inset_18px_0_40px_rgba(0,0,0,0.06),inset_0_1px_0_rgba(255,255,255,0.65)]',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="h-full p-[clamp(18px,2.2vw,28px)]">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const SheetSide: React.FC<{
|
||||
side: 'front' | 'back'
|
||||
contentSide: 'left' | 'right'
|
||||
children?: React.ReactNode
|
||||
}> = ({ side, contentSide, children }) => {
|
||||
const isBack = side === 'back'
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
'absolute inset-0',
|
||||
'[backface-visibility:hidden]',
|
||||
'rounded-[22px]',
|
||||
'bg-transparent',
|
||||
isBack ? '[transform:rotateY(180deg)]' : '',
|
||||
].join(' ')}
|
||||
>
|
||||
<PageHalf side={contentSide}>{children}</PageHalf>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Sheet: React.FC<{
|
||||
sheet: FlipBookSheet
|
||||
index: number
|
||||
sheetCount: number
|
||||
shouldBeFlipped: boolean
|
||||
isTurning: boolean
|
||||
transitionMs: number
|
||||
}> = ({ sheet, index, sheetCount, shouldBeFlipped, isTurning, transitionMs }) => {
|
||||
const zIndex = isTurning ? sheetCount + 10 : sheetCount - index
|
||||
const clipPath = useMemo(() => {
|
||||
if (isTurning) return undefined
|
||||
if (shouldBeFlipped) return 'inset(0 50% 0 0)'
|
||||
return 'inset(0 0 0 50%)'
|
||||
}, [isTurning, shouldBeFlipped])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
'absolute inset-0',
|
||||
'[transform-style:preserve-3d]',
|
||||
'rounded-[22px]',
|
||||
'transition-transform',
|
||||
].join(' ')}
|
||||
style={{
|
||||
zIndex,
|
||||
transitionDuration: `${transitionMs}ms`,
|
||||
transitionTimingFunction: 'cubic-bezier(0.2, 0.9, 0.2, 1)',
|
||||
transformOrigin: '50% 50%',
|
||||
transform: shouldBeFlipped ? 'rotateY(-180deg)' : 'rotateY(0deg)',
|
||||
clipPath,
|
||||
}}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<SheetSide side="front" contentSide="right">
|
||||
{sheet.front}
|
||||
</SheetSide>
|
||||
<SheetSide side="back" contentSide="left">
|
||||
{sheet.back ?? sheet.front}
|
||||
</SheetSide>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const FlipBook: React.FC<FlipBookProps> = ({ title, sheets, className }) => {
|
||||
const reducedMotion = usePrefersReducedMotion()
|
||||
const transitionMs = reducedMotion ? 0 : TRANSITION_MS
|
||||
|
||||
const [currentIndex, setCurrentIndex] = useState(0)
|
||||
const [turning, setTurning] = useState<null | { index: number; direction: 'next' | 'prev' }>(null)
|
||||
|
||||
const busy = Boolean(turning)
|
||||
const canPrev = !busy && currentIndex > 0
|
||||
const canNext = !busy && currentIndex < sheets.length - 1
|
||||
|
||||
const swipeStartX = useRef<number | null>(null)
|
||||
|
||||
const scheduleTurn = useCallback(
|
||||
(action: () => void) => {
|
||||
if (transitionMs === 0) {
|
||||
action()
|
||||
setTurning(null)
|
||||
return
|
||||
}
|
||||
|
||||
window.setTimeout(() => {
|
||||
action()
|
||||
setTurning(null)
|
||||
}, transitionMs)
|
||||
},
|
||||
[transitionMs],
|
||||
)
|
||||
|
||||
const turnToNext = useCallback(() => {
|
||||
if (!canNext) return
|
||||
setTurning({ index: currentIndex, direction: 'next' })
|
||||
scheduleTurn(() => {
|
||||
setCurrentIndex((value) => Math.min(sheets.length - 1, value + 1))
|
||||
})
|
||||
}, [canNext, currentIndex, scheduleTurn, sheets.length])
|
||||
|
||||
const turnToPrev = useCallback(() => {
|
||||
if (!canPrev) return
|
||||
setTurning({ index: currentIndex - 1, direction: 'prev' })
|
||||
scheduleTurn(() => {
|
||||
setCurrentIndex((value) => Math.max(0, value - 1))
|
||||
})
|
||||
}, [canPrev, currentIndex, scheduleTurn])
|
||||
|
||||
useEffect(() => {
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'ArrowRight' || event.key === ' ') {
|
||||
event.preventDefault()
|
||||
turnToNext()
|
||||
}
|
||||
if (event.key === 'ArrowLeft') {
|
||||
event.preventDefault()
|
||||
turnToPrev()
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', onKeyDown)
|
||||
return () => window.removeEventListener('keydown', onKeyDown)
|
||||
}, [turnToNext, turnToPrev])
|
||||
|
||||
const onPointerDown: React.PointerEventHandler<HTMLDivElement> = (event) => {
|
||||
swipeStartX.current = event.clientX
|
||||
}
|
||||
|
||||
const onPointerUp: React.PointerEventHandler<HTMLDivElement> = (event) => {
|
||||
const startX = swipeStartX.current
|
||||
swipeStartX.current = null
|
||||
if (startX === null) return
|
||||
|
||||
const deltaX = event.clientX - startX
|
||||
if (Math.abs(deltaX) < 34) return
|
||||
|
||||
if (deltaX < 0) {
|
||||
turnToNext()
|
||||
return
|
||||
}
|
||||
|
||||
turnToPrev()
|
||||
}
|
||||
|
||||
const progressLabel = useMemo(() => {
|
||||
const page = Math.min(currentIndex + 1, sheets.length)
|
||||
return `${page} / ${sheets.length}`
|
||||
}, [currentIndex, sheets.length])
|
||||
|
||||
const currentSheet = sheets[currentIndex]
|
||||
|
||||
return (
|
||||
<section
|
||||
aria-label={title}
|
||||
className={[
|
||||
'w-full',
|
||||
'max-w-[980px]',
|
||||
'mx-auto',
|
||||
'select-none',
|
||||
className ?? '',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="text-center">
|
||||
<h1 className="font-[var(--tf-font-serif)] text-[clamp(32px,4.2vw,54px)] leading-[1.08] tracking-tight">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="mt-4 text-sm text-[var(--tf-text-subtle)]">点击翻页,或使用键盘 ← / →,移动端可左右滑动。</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 flex items-center justify-center">
|
||||
<div
|
||||
className={[
|
||||
'relative',
|
||||
'w-[min(92vw,940px)]',
|
||||
'h-[min(68vh,560px)]',
|
||||
'[perspective:1800px]',
|
||||
].join(' ')}
|
||||
onPointerDown={onPointerDown}
|
||||
onPointerUp={onPointerUp}
|
||||
>
|
||||
<div
|
||||
className={[
|
||||
'absolute inset-0 rounded-[26px]',
|
||||
'bg-[color-mix(in_srgb,var(--tf-bg)_42%,white)]',
|
||||
'shadow-[0_26px_80px_rgba(0,0,0,0.14)]',
|
||||
'border border-[color-mix(in_srgb,var(--tf-border)_70%,white)]',
|
||||
].join(' ')}
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="absolute inset-y-[22px] left-1/2 w-px bg-[color-mix(in_srgb,var(--tf-border)_80%,white)]"
|
||||
/>
|
||||
|
||||
<div
|
||||
className={[
|
||||
'absolute inset-0 z-[60] transition-opacity',
|
||||
busy ? 'opacity-0 pointer-events-none' : 'opacity-100',
|
||||
].join(' ')}
|
||||
>
|
||||
<PageHalf side="left">{currentSheet?.back ?? currentSheet?.front}</PageHalf>
|
||||
<PageHalf side="right">{currentSheet?.front}</PageHalf>
|
||||
</div>
|
||||
|
||||
{sheets
|
||||
.map((sheet, index) => {
|
||||
const flippedAlready = index < currentIndex
|
||||
const shouldBeFlipped =
|
||||
turning?.index === index
|
||||
? turning.direction === 'next'
|
||||
: flippedAlready
|
||||
|
||||
const isTurning = turning?.index === index
|
||||
|
||||
return (
|
||||
<Sheet
|
||||
key={sheet.id}
|
||||
sheet={sheet}
|
||||
index={index}
|
||||
sheetCount={sheets.length}
|
||||
shouldBeFlipped={shouldBeFlipped}
|
||||
isTurning={isTurning}
|
||||
transitionMs={transitionMs}
|
||||
/>
|
||||
)
|
||||
})
|
||||
.reverse()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex items-center justify-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={turnToPrev}
|
||||
disabled={!canPrev}
|
||||
className={[
|
||||
'inline-flex items-center justify-center rounded-full border px-4 py-2 text-sm transition',
|
||||
'border-[var(--tf-border)] bg-[color-mix(in_srgb,var(--tf-bg)_50%,white)]',
|
||||
canPrev
|
||||
? 'text-[var(--tf-text)] hover:bg-[color-mix(in_srgb,var(--tf-bg)_34%,white)]'
|
||||
: 'text-[var(--tf-text-subtle)] opacity-60 cursor-not-allowed',
|
||||
].join(' ')}
|
||||
aria-label="上一页"
|
||||
>
|
||||
← Prev
|
||||
</button>
|
||||
|
||||
<div className="min-w-[88px] text-center text-xs font-[var(--tf-font-ui)] tracking-wide text-[var(--tf-text-subtle)]">
|
||||
{progressLabel}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={turnToNext}
|
||||
disabled={!canNext}
|
||||
className={[
|
||||
'inline-flex items-center justify-center rounded-full border px-4 py-2 text-sm transition',
|
||||
'border-[var(--tf-border)] bg-[color-mix(in_srgb,var(--tf-bg)_50%,white)]',
|
||||
canNext
|
||||
? 'text-[var(--tf-text)] hover:bg-[color-mix(in_srgb,var(--tf-bg)_34%,white)]'
|
||||
: 'text-[var(--tf-text-subtle)] opacity-60 cursor-not-allowed',
|
||||
].join(' ')}
|
||||
aria-label="下一页"
|
||||
>
|
||||
Next →
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { Footer } from './Footer'
|
||||
|
||||
export const SiteLayout: React.FC = () => {
|
||||
const location = useLocation()
|
||||
const isHome = location.pathname === '/'
|
||||
|
||||
useEffect(() => {
|
||||
if (location.hash) {
|
||||
@@ -21,11 +22,11 @@ export const SiteLayout: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[var(--tf-bg)] text-[var(--tf-text)]">
|
||||
<Header />
|
||||
{isHome ? null : <Header />}
|
||||
<main id="main" className="bg-[var(--tf-bg)]">
|
||||
<Outlet />
|
||||
</main>
|
||||
<Footer />
|
||||
{isHome ? null : <Footer />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,360 +1,127 @@
|
||||
import React from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { FlipBook } from '../components/FlipBook'
|
||||
|
||||
type HomeSectionProps = {
|
||||
id: string
|
||||
children: React.ReactNode
|
||||
variant?: 'default' | 'alt'
|
||||
type Entry = {
|
||||
to: string
|
||||
label: string
|
||||
desc: string
|
||||
}
|
||||
|
||||
const HomeContainer: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
return <div className="mx-auto max-w-[1120px] px-[clamp(24px,6vw,15vw)]">{children}</div>
|
||||
}
|
||||
|
||||
const HomeSection: React.FC<HomeSectionProps> = ({ id, children, variant = 'default' }) => {
|
||||
const SentencePage: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
return (
|
||||
<section
|
||||
id={id}
|
||||
aria-labelledby={`${id}-title`}
|
||||
className={[
|
||||
'py-[120px]',
|
||||
variant === 'alt' ? 'bg-[var(--tf-bg-alt)]' : 'bg-[var(--tf-bg)]',
|
||||
].join(' ')}
|
||||
>
|
||||
<HomeContainer>{children}</HomeContainer>
|
||||
</section>
|
||||
<div className="flex h-full items-center">
|
||||
<p className="max-w-[24ch] text-[clamp(22px,2.6vw,32px)] leading-[1.45] tracking-tight text-[var(--tf-text)]">
|
||||
{children}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const SectionWorldView: React.FC = () => {
|
||||
const LastPage: React.FC<{ entries: Entry[] }> = ({ entries }) => {
|
||||
return (
|
||||
<HomeSection id="SectionWorldView">
|
||||
<header className="max-w-3xl">
|
||||
<h1
|
||||
id="SectionWorldView-title"
|
||||
className="font-[var(--tf-font-serif)] font-medium text-[clamp(56px,5.2vw,64px)] leading-[1.05] tracking-tight text-[var(--tf-text)]"
|
||||
>
|
||||
全球化,正在进入高不确定性时代
|
||||
</h1>
|
||||
<p className="mt-10 text-[clamp(18px,1.6vw,20px)] leading-[1.8] text-[#394B59]">
|
||||
市场、政策、数据与地缘风险同时叠加,
|
||||
<br />
|
||||
任何一次跨境决策的失误,代价都在被放大。
|
||||
</p>
|
||||
<p className="mt-8 text-[clamp(16px,1.4vw,18px)] leading-[1.8] text-[var(--tf-text-subtle)]">
|
||||
对中国企业而言,出海不再只是机会判断,
|
||||
<br />
|
||||
而是一项系统性能力挑战。
|
||||
</p>
|
||||
</header>
|
||||
</HomeSection>
|
||||
)
|
||||
}
|
||||
|
||||
const SectionDecisionUncertainty: React.FC = () => {
|
||||
const questions = [
|
||||
'现在是否适合进入这个国家或区域?',
|
||||
'合规风险会不会在后期集中爆发?',
|
||||
'当前的增长模式,是否能复制到下一个市场?',
|
||||
'如果业务受挫,是战略判断错误,还是信息不足?',
|
||||
'是否存在尚未被识别的关键风险?',
|
||||
]
|
||||
|
||||
return (
|
||||
<HomeSection id="SectionDecisionUncertainty" variant="alt">
|
||||
<div className="max-w-4xl">
|
||||
<h2
|
||||
id="SectionDecisionUncertainty-title"
|
||||
className="font-medium text-[clamp(36px,3.2vw,40px)] leading-[1.15] tracking-tight text-[var(--tf-text)]"
|
||||
>
|
||||
困难的不是走向全球,而是如何在不确定中做出可控决策
|
||||
</h2>
|
||||
|
||||
<div className="mt-12 grid gap-7">
|
||||
{questions.map((question) => (
|
||||
<div key={question} className="border-l border-[var(--tf-border)] pl-6">
|
||||
<p className="text-[clamp(16px,1.4vw,18px)] leading-[1.8] text-[var(--tf-text-muted)]">{question}</p>
|
||||
</div>
|
||||
))}
|
||||
<div className="flex h-full flex-col justify-between">
|
||||
<div>
|
||||
<div className="inline-flex items-center gap-2 rounded-full bg-[var(--tf-accent-soft)] px-3 py-1 text-xs font-[var(--tf-font-ui)] tracking-wide text-[var(--tf-accent-link)]">
|
||||
Navigation
|
||||
</div>
|
||||
</div>
|
||||
</HomeSection>
|
||||
)
|
||||
}
|
||||
|
||||
const SectionCoreThesis: React.FC = () => {
|
||||
return (
|
||||
<HomeSection id="SectionCoreThesis">
|
||||
<div className="mx-auto max-w-4xl text-center">
|
||||
<h2
|
||||
id="SectionCoreThesis-title"
|
||||
className="font-[var(--tf-font-serif)] text-[clamp(44px,4.3vw,56px)] leading-[1.4] tracking-tight text-[var(--tf-text)]"
|
||||
>
|
||||
合规,不是成本。
|
||||
<br />
|
||||
它是增长的
|
||||
<span className="text-[var(--tf-accent-strong)]">结构性杠杆</span>。
|
||||
<h2 className="mt-4 font-[var(--tf-font-serif)] text-[clamp(22px,2.2vw,28px)] leading-[1.25] tracking-tight">
|
||||
从这里进入六个板块
|
||||
</h2>
|
||||
<p className="mt-10 text-[clamp(16px,1.4vw,18px)] leading-[1.85] text-[var(--tf-text-subtle)]">
|
||||
当合规被前置并系统化,它决定的不是“能否避免风险”,
|
||||
<br />
|
||||
而是企业能否更快进入市场、建立信任,并实现可复制的增长。
|
||||
<p className="mt-4 text-sm leading-[1.7] text-[var(--tf-text-muted)]">
|
||||
你可以把这页当作目录:继续探索 Why / How / System / Discovery / Solutions / Proof。
|
||||
</p>
|
||||
</div>
|
||||
</HomeSection>
|
||||
)
|
||||
}
|
||||
|
||||
const SectionMethodology: React.FC = () => {
|
||||
return (
|
||||
<HomeSection id="SectionMethodology" variant="alt">
|
||||
<article className="max-w-4xl">
|
||||
<h2
|
||||
id="SectionMethodology-title"
|
||||
className="font-medium text-[clamp(36px,3.2vw,40px)] leading-[1.15] tracking-tight text-[var(--tf-text)]"
|
||||
>
|
||||
这是在复杂现实中被反复验证的方法论
|
||||
</h2>
|
||||
|
||||
<div className="mt-10 space-y-6 text-[clamp(16px,1.4vw,18px)] leading-[1.85] text-[var(--tf-text-muted)]">
|
||||
<p>
|
||||
这一判断,来自长期服务中国企业全球化过程中的真实实践,
|
||||
也来自高度复杂、强监管与多变量环境下的系统性经验。
|
||||
</p>
|
||||
<p>
|
||||
我们逐渐意识到:
|
||||
<br />
|
||||
合规并非增长的对立面,而是增长的前置条件。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-12">
|
||||
<div className="inline-flex items-baseline gap-3 border-b border-[#EBF2FC] pb-2">
|
||||
<span className="font-[var(--tf-font-serif)] text-[clamp(20px,1.8vw,22px)] text-[var(--tf-accent-strong)]">
|
||||
Compliance as Growth
|
||||
</span>
|
||||
<span className="text-sm font-[var(--tf-font-ui)] text-[var(--tf-text-muted)]">—— 合规即增长</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</HomeSection>
|
||||
)
|
||||
}
|
||||
|
||||
const SectionSystemOverview: React.FC = () => {
|
||||
const modules = [
|
||||
{
|
||||
name: 'G-Comply',
|
||||
desc: '我如何合法进入并持续运营一个海外市场?',
|
||||
},
|
||||
{
|
||||
name: 'G-Growth',
|
||||
desc: '我如何在合规前提下,实现可持续且可复制的增长?',
|
||||
},
|
||||
{
|
||||
name: 'G-Brain',
|
||||
desc: '多个 AI Agent 如何协同,支撑复杂决策?',
|
||||
},
|
||||
{
|
||||
name: 'Discovery',
|
||||
desc: '我如何持续获得高质量、可行动的全球决策信号?',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<HomeSection id="SectionSystemOverview">
|
||||
<div className="max-w-5xl">
|
||||
<h2
|
||||
id="SectionSystemOverview-title"
|
||||
className="font-medium text-[clamp(36px,3.2vw,40px)] leading-[1.15] tracking-tight text-[var(--tf-text)]"
|
||||
>
|
||||
当问题复杂到无法依赖个人经验时,必须用系统解决
|
||||
</h2>
|
||||
|
||||
<p className="mt-10 max-w-3xl text-[clamp(16px,1.4vw,18px)] leading-[1.85] text-[var(--tf-text-muted)]">
|
||||
图灵环流(TuringFlow),致力于将复杂的全球化问题,
|
||||
<br />
|
||||
转化为可执行、可验证、可规模化的系统能力。
|
||||
</p>
|
||||
|
||||
<div className="mt-14 grid gap-5 sm:grid-cols-2">
|
||||
{modules.map((module) => (
|
||||
<div
|
||||
key={module.name}
|
||||
className="group rounded-2xl border border-[var(--tf-border)] bg-[var(--tf-surface)] p-7 transition-colors hover:border-[var(--tf-accent-border)] hover:bg-[var(--tf-accent-soft)]"
|
||||
aria-label={`${module.name} 模块`}
|
||||
>
|
||||
<div className="flex items-baseline justify-between gap-4">
|
||||
<div className="text-lg font-[var(--tf-font-ui)] font-medium text-[var(--tf-text-muted)] group-hover:text-[var(--tf-accent-strong)]">
|
||||
{module.name}
|
||||
</div>
|
||||
<div className="text-xs font-[var(--tf-font-ui)] text-[var(--tf-text-muted)]">Module</div>
|
||||
<div className="mt-6 grid gap-3 sm:grid-cols-2">
|
||||
{entries.map((entry) => (
|
||||
<Link
|
||||
key={entry.to}
|
||||
to={entry.to}
|
||||
className={[
|
||||
'group rounded-2xl border border-[var(--tf-border)] bg-[color-mix(in_srgb,var(--tf-bg)_52%,white)] p-4 transition',
|
||||
'hover:border-[var(--tf-accent-border)] hover:bg-[var(--tf-accent-soft)]',
|
||||
'focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--tf-accent-border)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--tf-bg)]',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="text-sm font-[var(--tf-font-ui)] font-semibold tracking-tight text-[var(--tf-text)]">
|
||||
{entry.label}
|
||||
</div>
|
||||
<p className="mt-5 text-[clamp(16px,1.4vw,18px)] leading-[1.8] text-[var(--tf-text-muted)]">
|
||||
{module.desc}
|
||||
</p>
|
||||
<span className="text-sm text-[var(--tf-text-subtle)] group-hover:text-[var(--tf-accent-strong)]">→</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</HomeSection>
|
||||
)
|
||||
}
|
||||
|
||||
const SectionDiscoveryIntro: React.FC = () => {
|
||||
const tags = ['每日晨报', '政策研究', '行业趋势', '深度报告']
|
||||
|
||||
return (
|
||||
<HomeSection id="SectionDiscoveryIntro" variant="alt">
|
||||
<div className="max-w-4xl">
|
||||
<h2
|
||||
id="SectionDiscoveryIntro-title"
|
||||
className="font-[var(--tf-font-serif)] text-[clamp(36px,3.2vw,40px)] leading-[1.15] tracking-tight text-[var(--tf-text)]"
|
||||
>
|
||||
Discovery,是我们的全球决策雷达
|
||||
</h2>
|
||||
|
||||
<p className="mt-10 text-[clamp(16px,1.4vw,18px)] leading-[1.85] text-[var(--tf-text-muted)]">
|
||||
我们持续追踪全球政策、市场、行业与地缘变化,
|
||||
<br />
|
||||
并将大量碎片化信息,深度加工为可用于决策的判断。
|
||||
</p>
|
||||
|
||||
<div className="mt-12 flex flex-wrap items-center gap-x-3 gap-y-3 text-sm font-[var(--tf-font-ui)]">
|
||||
{tags.map((tag) => (
|
||||
<span
|
||||
key={tag}
|
||||
className="inline-flex items-center rounded-full bg-[var(--tf-accent-soft)] px-3 py-1 text-[var(--tf-accent-link)]"
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</HomeSection>
|
||||
)
|
||||
}
|
||||
|
||||
const SectionMeasurableOutcome: React.FC = () => {
|
||||
const points = [
|
||||
'全球合规与市场进入决策周期显著缩短',
|
||||
'关键风险提前识别与预警',
|
||||
'增长路径的可复制性明显提升',
|
||||
'跨市场决策一致性持续增强',
|
||||
]
|
||||
|
||||
return (
|
||||
<HomeSection id="SectionMeasurableOutcome">
|
||||
<div className="max-w-4xl">
|
||||
<h2
|
||||
id="SectionMeasurableOutcome-title"
|
||||
className="font-medium text-[clamp(36px,3.2vw,40px)] leading-[1.15] tracking-tight text-[var(--tf-text)]"
|
||||
>
|
||||
系统带来的,是可以被度量的确定性
|
||||
</h2>
|
||||
|
||||
<div className="mt-12 grid gap-6">
|
||||
{points.map((point) => (
|
||||
<div key={point} className="flex items-start gap-4">
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="mt-2 h-2.5 w-2.5 rounded-full bg-[var(--tf-border)]"
|
||||
/>
|
||||
<p className="text-[clamp(16px,1.4vw,18px)] leading-[1.8] text-[var(--tf-text-muted)]">{point}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</HomeSection>
|
||||
)
|
||||
}
|
||||
|
||||
const SectionTrustProof: React.FC = () => {
|
||||
const logos = ['Gov', 'Enterprise', 'Consortium', 'ThinkTank', 'Academia', 'Partner']
|
||||
|
||||
return (
|
||||
<HomeSection id="SectionTrustProof" variant="alt">
|
||||
<div className="max-w-5xl">
|
||||
<h2
|
||||
id="SectionTrustProof-title"
|
||||
className="font-medium text-[clamp(36px,3.2vw,40px)] leading-[1.15] tracking-tight text-[var(--tf-text)]"
|
||||
>
|
||||
被用于复杂场景,才有价值
|
||||
</h2>
|
||||
|
||||
<p className="mt-10 max-w-3xl text-[clamp(16px,1.4vw,18px)] leading-[1.85] text-[var(--tf-text-subtle)]">
|
||||
图灵环流的能力,已在政府级项目及多行业实践中得到验证。
|
||||
</p>
|
||||
|
||||
<div className="mt-14 grid gap-x-10 gap-y-6 sm:grid-cols-3">
|
||||
{logos.map((label) => (
|
||||
<div
|
||||
key={label}
|
||||
className="select-none rounded-xl border border-[var(--tf-border)] bg-[var(--tf-surface)] px-6 py-5 text-sm font-[var(--tf-font-ui)] tracking-wide text-[var(--tf-text-subtle)] grayscale transition duration-300 hover:grayscale-0"
|
||||
aria-label="Logo 占位"
|
||||
>
|
||||
{label}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</HomeSection>
|
||||
)
|
||||
}
|
||||
|
||||
const SectionSoftCTA: React.FC = () => {
|
||||
return (
|
||||
<HomeSection id="SectionSoftCTA">
|
||||
<div className="max-w-4xl">
|
||||
<h2
|
||||
id="SectionSoftCTA-title"
|
||||
className="font-[var(--tf-font-serif)] text-[clamp(36px,3.2vw,40px)] leading-[1.15] tracking-tight text-[var(--tf-accent-strong)]"
|
||||
>
|
||||
探讨你的全球化路径
|
||||
</h2>
|
||||
|
||||
<div className="mt-10 grid gap-4">
|
||||
<Link
|
||||
to="/discovery"
|
||||
className="group inline-flex items-center justify-between border-b border-[var(--tf-border)] py-4"
|
||||
>
|
||||
<span className="text-[clamp(16px,1.4vw,18px)] leading-[1.6] text-[var(--tf-text-subtle)]">
|
||||
查看一份真实的全球决策示例
|
||||
</span>
|
||||
<span className="text-sm font-[var(--tf-font-ui)] text-[var(--tf-text-subtle)] group-hover:text-[var(--tf-accent-strong)]">
|
||||
→
|
||||
</span>
|
||||
<p className="mt-2 text-xs leading-[1.6] text-[var(--tf-text-muted)]">{entry.desc}</p>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/contact"
|
||||
className="group inline-flex items-center justify-between border-b border-[var(--tf-border)] py-4"
|
||||
>
|
||||
<span className="text-[clamp(16px,1.4vw,18px)] leading-[1.6] text-[var(--tf-text-subtle)]">
|
||||
与我们交流一个具体市场或业务场景
|
||||
</span>
|
||||
<span className="text-sm font-[var(--tf-font-ui)] text-[var(--tf-text-subtle)] group-hover:text-[var(--tf-accent-strong)]">
|
||||
→
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</HomeSection>
|
||||
|
||||
<div className="pt-3 text-xs text-[var(--tf-text-subtle)]">
|
||||
Tip: 键盘 → 翻到最后页更快。
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const Home: React.FC = () => {
|
||||
const entries = useMemo<Entry[]>(
|
||||
() => [
|
||||
{ to: '/why', label: 'Why', desc: '问题的根源与外部环境。' },
|
||||
{ to: '/how', label: 'How', desc: '方法论与执行路径。' },
|
||||
{ to: '/system', label: 'System', desc: '系统构成与模块协同。' },
|
||||
{ to: '/discovery', label: 'Discovery', desc: '持续获取全球决策信号。' },
|
||||
{ to: '/solutions', label: 'Solutions', desc: '解决方案与落地方式。' },
|
||||
{ to: '/proof', label: 'Proof', desc: '案例与可信度证据。' },
|
||||
],
|
||||
[],
|
||||
)
|
||||
|
||||
const sheets = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'p-1',
|
||||
front: <SentencePage>这不是一个首页。</SentencePage>,
|
||||
},
|
||||
{
|
||||
id: 'p-2',
|
||||
front: <SentencePage>它是一页页翻开的叙事。</SentencePage>,
|
||||
},
|
||||
{
|
||||
id: 'p-3',
|
||||
front: <SentencePage>每一页,都只说一句。</SentencePage>,
|
||||
},
|
||||
{
|
||||
id: 'p-4',
|
||||
front: <SentencePage>把复杂的问题,拆成可读的片段。</SentencePage>,
|
||||
},
|
||||
{
|
||||
id: 'p-5',
|
||||
front: <SentencePage>把模糊的焦虑,变成清晰的判断。</SentencePage>,
|
||||
},
|
||||
{
|
||||
id: 'p-6',
|
||||
front: <SentencePage>把“出海”,从机会变成能力。</SentencePage>,
|
||||
},
|
||||
{
|
||||
id: 'p-7',
|
||||
front: <SentencePage>然后,进入真正的内容。</SentencePage>,
|
||||
},
|
||||
{
|
||||
id: 'p-8',
|
||||
front: <LastPage entries={entries} />,
|
||||
},
|
||||
],
|
||||
[entries],
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SectionWorldView />
|
||||
<SectionDecisionUncertainty />
|
||||
<SectionCoreThesis />
|
||||
<SectionMethodology />
|
||||
<SectionSystemOverview />
|
||||
<SectionDiscoveryIntro />
|
||||
<SectionMeasurableOutcome />
|
||||
<SectionTrustProof />
|
||||
<SectionSoftCTA />
|
||||
<div className="relative overflow-hidden">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top,rgba(30,91,176,0.12),transparent_55%)]"
|
||||
/>
|
||||
<div className="mx-auto flex min-h-[calc(100vh-0px)] max-w-[1200px] items-center px-[clamp(18px,4vw,56px)] py-[clamp(28px,6vh,64px)]">
|
||||
<FlipBook title="TuringFlow" sheets={sheets} className="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user