/* global React */
const { useState, useEffect, useRef } = React;
class ErrorBoundary extends React.Component {
constructor(props) { super(props); this.state = { hasError: false }; }
static getDerivedStateFromError() { return { hasError: true }; }
componentDidCatch(err) { try { console.error('[ErrorBoundary]', err); } catch (_) {} }
render() {
if (this.state.hasError) {
return (
);
}
return this.props.children;
}
}
// ── 404 TABLE 標誌 ──────────────────────
const Mark = ({ size = 32, color = 'currentColor' }) => (
);
// ── Top bar ─────────────────────────────
const TopBar = ({ onIndustryPage = false }) => {
const [menuOpen, setMenuOpen] = useState(false);
const close = () => setMenuOpen(false);
// Sub-pages (industry.html etc) don't render Pricing / Instructor / FAQ — route those nav links back to the homepage.
const prefix = onIndustryPage ? '/' : '';
return (
404 TABLE
立即報名 →
);
};
// ── Hero ────────────────────────────────
const HERO_VARIANTS = {
default: {
h1: <>2 小時會議記錄,
現在 10 分鐘搞定。>,
lede: <>老闆又錄了 5 分鐘語音。
客戶又發了 80 字沒標點的 LINE。
合約又是 100 頁、10 分鐘後要結論。
—— 你是秘書,不是讀心術師。>,
},
line: {
h1: <>老闆的 LINE,
你秒懂秒回。>,
lede: <>一行沒標點的字,背後是 10 個待辦。
你不是看不懂,是沒有工具把它攤平。
—— 現在有了。>,
},
overtime: {
h1: <>準時下班,
不是妄想。>,
lede: <>每天最後一個走,不是因為最勤快。
是因為沒有工具把「老闆要的」變成「5 分鐘能交的」。
—— 這堂課就是那個工具。>,
},
};
const Hero = () => {
const [variant, setVariant] = useState(window.TWEAKS?.heroVariant || 'default');
useEffect(() => {
const handler = (e) => setVariant(e.detail?.heroVariant || 'default');
window.addEventListener('tweaks-change', handler);
return () => window.removeEventListener('tweaks-change', handler);
}, []);
const { h1, lede } = HERO_VARIANTS[variant] || HERO_VARIANTS.default;
return (
職能實驗室 / EST. 2026
500元課程陣列 — 秘書/行政 — 第 01 季
2026 春季班 / 招生中
{h1}
);
};
// ── Marquee ─────────────────────────────
const Marquee = () => {
const items = [
'老闆錄音祕笈', '會議 10 分鐘交件', '名片秒掃歸檔術', '100 張 15 分鐘',
'老闆 LINE 翻譯機', '雙語接待一小時套裝', '合約地雷掃描術', '5 分鐘看完 100 頁',
'月底對帳信量產術', '80 封 10 分鐘', '老闆記憶還原術', '差勤對帳魔法',
'報價單百變工廠', '300 人請柬不出錯', '跨時區會議終結 10 輪信',
];
const makeRow = (rep) => items.map((x, i) => (
{x}●
));
return (
{makeRow('a')}{makeRow('b')}{makeRow('c')}
);
};
// ── Pain stories ────────────────────────
const PainStories = () => {
const stories = [
{ tag: 'SCENE 01 / 14:32', t: '「會議紀錄今天下班前要。」', q: '老闆一邊講一邊走出會議室。你還在打第二點,他已經到第七點了。', ts: 'TUE 14:32:08' },
{ tag: 'SCENE 02 / 09:15', t: '「那個女客戶的電話呢?」', q: '科技業、姓陳、去年 Q3 — 然後就沒了。你抽屜翻遍,名片找不到。', ts: 'WED 09:15:44' },
{ tag: 'SCENE 03 / 23:48', t: '「明天要看合約有沒有雷。」', q: '110 頁 PDF 在你信箱躺著,老闆 10 點開會。你已經到家。', ts: 'SUN 23:48:21' },
];
return (
{stories.map((s, i) => (
))}
);
};
// ── Courses ─────────────────────────────
const Courses = () => {
const [open, setOpen] = useState(0);
return (
0320 堂課陣列
每一堂解一個那一幕。
離開教室 — 成果沒帶回家。
{(window.COURSES || []).map((c, i) => (
setOpen(open === i ? -1 : i)}
>
/{c.n}
{c.title}
{c.pain}
{c.timer}搞定
+
))}
);
};
// ── Pricing ─────────────────────────────
const Pricing = () => (
挑一個你最痛的「那一幕」,1 小時當場做完,離開教室就能用。
- 任選 1 堂,1 小時實作
- 不帶作業回家
- 附 3 套提示詞模板
- 同系列累積購買享折抵
);
// ── Industries ──────────────────────────
const IndustriesSection = () => (
0430 個產業 / 30 種老闆
你的產業,
有你產業的地獄。
30 產業 × 3 價位 = 90 個專屬入口。
);
// ── Metrics ─────────────────────────────
const Metrics = () => (
120×
效率提升
2 小時的會議紀錄,10 分鐘交件 — 學員實測中位數。
96%
回購率
第一堂上完的學員中,96% 一個月內報第二堂。
3套
提示詞模板
每堂課帶走 3 套個人化提示詞,下班就能用。
0頁
作業
離開教室時成果已經做出來,絕不帶作業回家。
);
// ── Proof ───────────────────────────────
const Proof = () => (
"
上完《老闆錄音祕笈》那天回辦公室,老闆 5 點半丟給我 90 分鐘的會議錄音。
—— 我 6 點 12 分把三版交件甩在他桌上,準時下班。
▍ 林小姐 / 製造業總經理特助 / 年資 8 年
「下班前交件」
從口號變日常。
我們的設計原則只有一條:離開教室時,今天的工作已經交了。不是 demo,是你今天回去就能用、能交、能準時下班的工作流。
);
// ── Instructor ──────────────────────────
const Instructor = () => (
陳知行
創辦人 / 前 上市公司董事長特助
12 年董事長特助資歷。看過 5 個老闆、3 種產業、無數次「現在馬上要」的崩潰瞬間。設計這 20 堂課,是把自己 12 年踩過的坑,變成 2 小時就能走完的捷徑。
);
// ── FAQ ─────────────────────────────────
const FAQ = () => {
const faqs = [
{ q: '我完全不會寫程式、不會用 AI,跟得上嗎?', a: '課程全部以「滑手機等級」為設計基準。沒有程式、沒有指令列、沒有 API。你會的,就是手機錄音、複製貼上、按一個按鈕。我們對自己的要求是:教室裡最不熟科技的那一位,也要在課程結束時當場交出成果。' },
{ q: '課程是線上還是實體?', a: '主推實體面授(台北 / 台中 / 高雄),人數上限 12 人,講師現場逐一檢查每位學員的成果。線上同步直播班另開,但作業檢核機制不同 — 報名前我們會跟你確認。' },
{ q: '為什麼 500 元這麼便宜?是不是有後續推銷?', a: '500 元是「試水溫」階。我們的邏輯是:你上得爽就會自己回來,所以第一堂便宜得跟一杯精品咖啡一樣。不會在課堂上推銷,但你會收到下一堂的訊息 —— 因為那時候你已經想再上了。' },
{ q: '我用的不是 ChatGPT,是 Claude / Gemini / 其他,可以嗎?', a: '可以。所有提示詞模板我們提供「跨平台版本」,並現場示範同一份提示詞在三大平台上的差異。你帶哪個來,我們調哪個。' },
{ q: '我們公司可以包班嗎?', a: '可以。最低 8 人成班,會依照貴公司流程客製教材,並提供 90 天落地陪跑。請透過報名表單或來信洽詢。' },
];
const [open, setOpen] = useState(0);
return (
{faqs.map((f, i) => (
setOpen(open === i ? -1 : i)}>
Q.{String(i + 1).padStart(2, '0')} — {f.q}
+
{f.a}
))}
);
};
const SHEET_URL = 'https://script.google.com/macros/s/AKfycbzvmN838Uji5413-Of7oT8RNhTdFmLtdBlL_0uMuZmotzoT0YeYAwUmItpdqGASQDlE_A/exec';
// ── Signup ──────────────────────────────
const Signup = ({ source = 'main' }) => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [fields, setFields] = useState({ name: '', email: '', company: '', title: '', industry: '' });
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
setError('');
// 1) 寫一份 lead 到 Google Sheets(不等回應,no-cors fire-and-forget)
fetch(SHEET_URL, {
method: 'POST',
mode: 'no-cors',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...fields, source, intent: 'paid_checkout' }),
}).catch(() => {});
// 2) 觸發 Meta Pixel Lead(廣告優化用,付款前就觸發)
if (window.fbq) window.fbq('track', 'Lead', { content_name: '404 TABLE 1堂課' });
if (window.gtag) window.gtag('event', 'generate_lead');
// 3) 跳 Recur Hosted Checkout
try {
if (!window.RecurCheckout) throw new Error('金流尚未載入,請重新整理頁面');
const recur = window.RecurCheckout.init({ publishableKey: window.RECUR_PK });
await recur.redirectToCheckout({
productId: window.RECUR_PID,
customerEmail: fields.email,
successUrl: window.location.origin + '/success.html?session_id={CHECKOUT_SESSION_ID}',
cancelUrl: window.location.origin + '/#signup',
metadata: {
name: fields.name,
company: fields.company,
title: fields.title,
industry: fields.industry,
source,
},
});
} catch (err) {
setError(err.message || '結帳失敗,請稍後再試');
setLoading(false);
}
};
const set = (k) => (e) => setFields(f => ({ ...f, [k]: e.target.value }));
return (
停止原地加班。
從一堂 500 開始。
填表後直接付款 NT$500,我們會在 24 小時內聯繫你確認上課場次(北中高實體 / 線上同步)。沒有電話騷擾。
);
};
// ── Footer ──────────────────────────────
const Foot = ({ onIndustryPage = false }) => {
const prefix = onIndustryPage ? '/' : '';
return (
);
};
// ── App ─────────────────────────────────
const App = () => (
);
Object.assign(window, {
App, Mark, TopBar, Hero, Marquee, PainStories, Courses,
Pricing, IndustriesSection, Metrics, Proof, Instructor, FAQ, Signup, Foot,
ErrorBoundary,
});