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 = () => {
|
export const SiteLayout: React.FC = () => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
const isHome = location.pathname === '/'
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (location.hash) {
|
if (location.hash) {
|
||||||
@@ -21,11 +22,11 @@ export const SiteLayout: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-[var(--tf-bg)] text-[var(--tf-text)]">
|
<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)]">
|
<main id="main" className="bg-[var(--tf-bg)]">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
{isHome ? null : <Footer />}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,360 +1,127 @@
|
|||||||
import React from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
import { FlipBook } from '../components/FlipBook'
|
||||||
|
|
||||||
type HomeSectionProps = {
|
type Entry = {
|
||||||
id: string
|
to: string
|
||||||
children: React.ReactNode
|
label: string
|
||||||
variant?: 'default' | 'alt'
|
desc: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const HomeContainer: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
const SentencePage: 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' }) => {
|
|
||||||
return (
|
return (
|
||||||
<section
|
<div className="flex h-full items-center">
|
||||||
id={id}
|
<p className="max-w-[24ch] text-[clamp(22px,2.6vw,32px)] leading-[1.45] tracking-tight text-[var(--tf-text)]">
|
||||||
aria-labelledby={`${id}-title`}
|
{children}
|
||||||
className={[
|
</p>
|
||||||
'py-[120px]',
|
</div>
|
||||||
variant === 'alt' ? 'bg-[var(--tf-bg-alt)]' : 'bg-[var(--tf-bg)]',
|
|
||||||
].join(' ')}
|
|
||||||
>
|
|
||||||
<HomeContainer>{children}</HomeContainer>
|
|
||||||
</section>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const SectionWorldView: React.FC = () => {
|
const LastPage: React.FC<{ entries: Entry[] }> = ({ entries }) => {
|
||||||
return (
|
return (
|
||||||
<HomeSection id="SectionWorldView">
|
<div className="flex h-full flex-col justify-between">
|
||||||
<header className="max-w-3xl">
|
<div>
|
||||||
<h1
|
<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)]">
|
||||||
id="SectionWorldView-title"
|
Navigation
|
||||||
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>
|
</div>
|
||||||
</div>
|
<h2 className="mt-4 font-[var(--tf-font-serif)] text-[clamp(22px,2.2vw,28px)] leading-[1.25] tracking-tight">
|
||||||
</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>
|
</h2>
|
||||||
<p className="mt-10 text-[clamp(16px,1.4vw,18px)] leading-[1.85] text-[var(--tf-text-subtle)]">
|
<p className="mt-4 text-sm leading-[1.7] text-[var(--tf-text-muted)]">
|
||||||
当合规被前置并系统化,它决定的不是“能否避免风险”,
|
你可以把这页当作目录:继续探索 Why / How / System / Discovery / Solutions / Proof。
|
||||||
<br />
|
|
||||||
而是企业能否更快进入市场、建立信任,并实现可复制的增长。
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</HomeSection>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const SectionMethodology: React.FC = () => {
|
<div className="mt-6 grid gap-3 sm:grid-cols-2">
|
||||||
return (
|
{entries.map((entry) => (
|
||||||
<HomeSection id="SectionMethodology" variant="alt">
|
<Link
|
||||||
<article className="max-w-4xl">
|
key={entry.to}
|
||||||
<h2
|
to={entry.to}
|
||||||
id="SectionMethodology-title"
|
className={[
|
||||||
className="font-medium text-[clamp(36px,3.2vw,40px)] leading-[1.15] tracking-tight text-[var(--tf-text)]"
|
'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)]',
|
||||||
</h2>
|
].join(' ')}
|
||||||
|
>
|
||||||
<div className="mt-10 space-y-6 text-[clamp(16px,1.4vw,18px)] leading-[1.85] text-[var(--tf-text-muted)]">
|
<div className="flex items-center justify-between gap-3">
|
||||||
<p>
|
<div className="text-sm font-[var(--tf-font-ui)] font-semibold tracking-tight text-[var(--tf-text)]">
|
||||||
这一判断,来自长期服务中国企业全球化过程中的真实实践,
|
{entry.label}
|
||||||
也来自高度复杂、强监管与多变量环境下的系统性经验。
|
|
||||||
</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>
|
</div>
|
||||||
<p className="mt-5 text-[clamp(16px,1.4vw,18px)] leading-[1.8] text-[var(--tf-text-muted)]">
|
<span className="text-sm text-[var(--tf-text-subtle)] group-hover:text-[var(--tf-accent-strong)]">→</span>
|
||||||
{module.desc}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
<p className="mt-2 text-xs leading-[1.6] text-[var(--tf-text-muted)]">{entry.desc}</p>
|
||||||
</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>
|
|
||||||
</Link>
|
</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>
|
</div>
|
||||||
</HomeSection>
|
|
||||||
|
<div className="pt-3 text-xs text-[var(--tf-text-subtle)]">
|
||||||
|
Tip: 键盘 → 翻到最后页更快。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Home: React.FC = () => {
|
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 (
|
return (
|
||||||
<div>
|
<div className="relative overflow-hidden">
|
||||||
<SectionWorldView />
|
<div
|
||||||
<SectionDecisionUncertainty />
|
aria-hidden="true"
|
||||||
<SectionCoreThesis />
|
className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top,rgba(30,91,176,0.12),transparent_55%)]"
|
||||||
<SectionMethodology />
|
/>
|
||||||
<SectionSystemOverview />
|
<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)]">
|
||||||
<SectionDiscoveryIntro />
|
<FlipBook title="TuringFlow" sheets={sheets} className="w-full" />
|
||||||
<SectionMeasurableOutcome />
|
</div>
|
||||||
<SectionTrustProof />
|
|
||||||
<SectionSoftCTA />
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user