/* 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 (
404

頁面載入時發生錯誤。請 { e.preventDefault(); location.reload(); }} style={{ borderBottom: '1px solid currentColor' }}>重新整理

); } return this.props.children; } } // ── 404 TABLE 標誌 ────────────────────── const Mark = ({ size = 32, color = 'currentColor' }) => ( {/* Left 4: diagonal stroke + crossbar + right bar */} {/* Table: top + two legs */} {/* Right 4: left bar + crossbar + diagonal stroke */} ); // ── 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}

{lede}

20
堂課
500
元 / 堂
1 hr
不帶作業
挑一堂試試 — NTD 500 看方案 →
); }; // ── 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 (
02那一天的那一幕

不是能力不夠,
時間不夠。

{stories.map((s, i) => (
{s.tag}

{s.t}

{s.q}
▍ {s.ts}
))}
); }; // ── 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}搞定
+
/{c.n}
痛點
{c.pain}
解法
{c.solve}
帶走
{c.take}
))}
); }; // ── Pricing ───────────────────────────── const Pricing = () => (
單堂體驗
500NTD / 堂
挑一堂報名 →
挑一個你最痛的「那一幕」,1 小時當場做完,離開教室就能用。
  • 任選 1 堂,1 小時實作
  • 不帶作業回家
  • 附 3 套提示詞模板
  • 同系列累積購買享折抵
); // ── Industries ────────────────────────── const IndustriesSection = () => (
0430 個產業 / 30 種老闆

你的產業
有你產業的地獄

30 產業 × 3 價位 = 90 個專屬入口。

{(window.INDUSTRIES || []).map((ind, i) => (
/{String(i + 1).padStart(2, '0')}
{ind.zh}
→ 進入
))}
); // ── Metrics ───────────────────────────── const Metrics = () => (
120×
效率提升
2 小時的會議紀錄,10 分鐘交件 — 學員實測中位數。
96%
回購率
第一堂上完的學員中,96% 一個月內報第二堂。
3
提示詞模板
每堂課帶走 3 套個人化提示詞,下班就能用。
0
作業
離開教室時成果已經做出來,絕不帶作業回家。
); // ── Proof ─────────────────────────────── const Proof = () => (
05學員回響

他們不再加班。

"
上完《老闆錄音祕笈》那天回辦公室,老闆 5 點半丟給我 90 分鐘的會議錄音。
—— 我 6 點 12 分把三版交件甩在他桌上,準時下班。
▍ 林小姐 / 製造業總經理特助 / 年資 8 年

「下班前交件」
從口號變日常。

我們的設計原則只有一條:離開教室時,今天的工作已經交了。不是 demo,是你今天回去就能用、能交、能準時下班的工作流。

); // ── Instructor ────────────────────────── const Instructor = () => (
06講師

設計者本身,
當過那個秘書。

陳知行 — 404 TABLE 創辦人

知行

創辦人 / 前 上市公司董事長特助

12 年董事長特助資歷。看過 5 個老闆、3 種產業、無數次「現在馬上要」的崩潰瞬間。設計這 20 堂課,是把自己 12 年踩過的坑,變成 2 小時就能走完的捷徑。

12yr
特助實戰
5
老闆服侍
2,400+
學員結業
); // ── 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 (
07常見問題

已經在猶豫了。

{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 小時內聯繫你確認上課場次(北中高實體 / 線上同步)。沒有電話騷擾。

{error &&

{error}

}

送出後跳轉至 Recur(PAYUNi)安全付款頁。完成付款後 24 小時內專人聯繫排課。付款受 PAYUNi 統一金流保護,PAYUNi 將寄送電子收據至報名 Email。

); }; // ── 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, });