新增 GA4 / Meta Pixel 追蹤與 Cookie 同意機制
- 追蹤 ID 存 site_settings(空字串即關閉),於 /admin/settings 管理 - Base.astro 僅在用戶同意後載入分析工具,未同意前零請求 - 新增 /privacy 私隱政策頁與「重設 Cookie 偏好」按鈕 - 前台 SSR 頁面(首頁、blog、about、privacy)傳入 gaId / metaPixelId - seed 加入預設空值,Footer 加入私隱政策連結 - 同步更新 AGENTS.md 與 README 說明
This commit is contained in:
+11
-3
@@ -8,7 +8,7 @@
|
||||
|
||||
- 擁有 `src/` 全層,亦直接擁有以下冇獨立 child doc 的部分:
|
||||
- `theme/system.ts` — Chakra system(tokens / semanticTokens / fonts / globalCss),前台顏色同字體的唯一來源。現行設計:editorial 極簡風(暖米白 `#FAF8F4` 底、琥珀金 `brand` accent、墨黑 `ink`、Noto Serif HK 標題),詳見 `docs/superpowers/specs/2026-09-11-editorial-redesign-design.md`。
|
||||
- `layouts/Base.astro` — 前台 SEO head(canonical / OG / Twitter / theme-color / JSON-LD `jsonLd` prop / sitemap link)+ Noto Sans HK(非阻塞載入,`media="print" onload` + `<noscript>` fallback)。
|
||||
- `layouts/Base.astro` — 前台 SEO head(canonical / OG / Twitter / theme-color / JSON-LD `jsonLd` prop / sitemap link)+ Noto Sans HK(非阻塞載入,`media="print" onload` + `<noscript>` fallback)+ 同意後先載入嘅 analytics(`gaId` / `metaPixelId` props,見下面 Analytics & consent)。
|
||||
- `layouts/AdminLayout.astro` — 後台外框,純 CSS、與前台一致的色系。
|
||||
- `config.ts` — build-time 靜態頁用的 `SITE_NAME` / `SITE_TAGLINE`(可管理內容一律放 D1,唔放呢度)。
|
||||
- `middleware.ts` — 保護 `/admin`:設 `locals.isAdmin`,無 `ADMIN_PASSWORD` 回 503,未登入導向 `/admin/login`。
|
||||
@@ -28,8 +28,8 @@
|
||||
### Routes & SSR(`pages/`)
|
||||
|
||||
- **預設靜態**:`astro.config.mjs` 係 `output: "static"`。要讀 D1 或 request-time 資料的頁面/endpoint 必須在檔案內寫 `export const prerender = false`。
|
||||
- 目前 SSR:`index.astro`、`blog/index.astro`、`blog/[slug].astro`、`admin/**`、`media/[...key].ts`、`sitemap.xml.ts`。
|
||||
- 保持靜態:`about.astro`、`robots.txt.ts`。
|
||||
- 目前 SSR:`index.astro`、`about.astro`、`privacy.astro`、`blog/index.astro`、`blog/[slug].astro`、`admin/**`、`media/[...key].ts`、`sitemap.xml.ts`。
|
||||
- 保持靜態:`robots.txt.ts`。
|
||||
- **環境變數**:只用 `getEnv()`,且只可喺 `prerender = false` 的頁面/endpoint 用。
|
||||
- **前台動態頁**:讀 D1 → 傳 props 畀單一 React island(`client:idle`,內容仍 SSR 出 HTML),並設邊緣快取 `Cache-Control: public, s-maxage=60, stale-while-revalidate=300`。
|
||||
- **404**:`blog/[slug].astro` 找唔到文章 → 設 `Astro.response.status = 404` 並 render noindex 頁。
|
||||
@@ -38,6 +38,14 @@
|
||||
- **AI 生成(同步)**:`/admin/ai` 的生成喺 admin POST 內同步 `await` 完成先 redirect。如日後要改做背景處理,可用 `Astro.locals.cfContext`(Cloudflare `ExecutionContext`)嘅 `waitUntil`,唔使改現有流程。
|
||||
- **SEO**:用 `Base.astro`;canonical / OG / sitemap 全部靠 `astro.config.mjs` 的 `site`(現為佔位 `https://example.com`,上線前要改)。所有頁面都要有單一 `h1`,section 標題用 `h2`、項目用 `h3`,唔可以跳級(heading 語意一律用 Chakra `as`,唔可以淨靠字級)。JSON-LD 由 `lib/schema-org.ts` 砌,經 `Base.astro` 嘅 `jsonLd` prop 輸出:首頁 `LocalBusiness` + `FAQPage`、文章 `BlogPosting` + `BreadcrumbList`。
|
||||
|
||||
### Analytics & consent
|
||||
|
||||
- **ID 放 D1**:GA4 / Meta Pixel 的 ID 存 `site_settings`(`ga4_measurement_id` / `meta_pixel_id`,admin `/admin/settings` 可改);**空字串=唔載入**,冇 `enabled` boolean。因為係通用 key-value,**唔使 migration**,只需 `scripts/seed.sql` 有 default。
|
||||
- **注入方式**:`Base.astro` 收 optional props `gaId` / `metaPixelId`;只有 SSR 頁讀到 D1 再傳落去(靜態頁要傳就必須轉 `prerender = false`)。GA4 / Meta script 一律喺 **consent 之後**由 inline JS 動態 append,未同意前唔會有任何追蹤請求或 Cookie(亦**冇** Meta `<noscript>` pixel)。
|
||||
- **Consent**:單一接受/拒絕,存 `localStorage` key `cookie_consent`(`granted` / `denied`);banner 同載入邏輯都喺 `Base.astro`(純 HTML/CSS + `is:inline`,唔用 React)。已同意者每次載入即追蹤。
|
||||
- **`/privacy`**:`pages/privacy.astro`(SSR)記錄 Cookie / GA4 / Meta 用途,並提供「重設 Cookie 偏好」清除 `localStorage`。
|
||||
- 免維護原則:唔使抄參考專案嘅 singleton 表/API/React 動態注入(本站係 MPA,每次載入=一次 pageview)。
|
||||
|
||||
### Admin(`pages/admin/`)
|
||||
|
||||
- 所有 `/admin` 路由**必須** `export const prerender = false`。
|
||||
|
||||
@@ -39,6 +39,7 @@ export function Footer({ settings }: { settings: Settings }) {
|
||||
{ label: "服務流程", href: "#process" },
|
||||
{ label: "常見問題", href: "#faq" },
|
||||
{ label: "Blog", href: "/blog" },
|
||||
{ label: "私隱政策", href: "/privacy" },
|
||||
].map((l) => (
|
||||
<Link
|
||||
key={l.href}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ D1 讀取查詢層(`content.ts`)同後台網站設定欄位定義(`setting
|
||||
- **Slug 去重**:`uniqueSlug(db, base, selfId?)` 撞 slug 就加 `-2`、`-3`…;`selfId` 用喺更新自己時排除自己。admin 文章同 AI 生成都用。
|
||||
- **`Db` 型別**:`DrizzleD1Database<typeof schema>`。
|
||||
- **連結 helper**:`whatsappHref` / `telHref` / `mailHref` / `digits` / `formatDate`;電話/WhatsApp 一律經呢啲 helper,唔好散寫。
|
||||
- **設定欄位**:`settings-fields.ts` 的 `SETTINGS_GROUPS` 係 `/admin/settings` 表單的唯一來源;新增一個 setting key 之後,要同步加落 `scripts/seed.sql`。
|
||||
- **設定欄位**:`settings-fields.ts` 的 `SETTINGS_GROUPS` 係 `/admin/settings` 表單的唯一來源;新增一個 setting key 之後,要同步加落 `scripts/seed.sql`。追蹤分析欄位(`ga4_measurement_id` / `meta_pixel_id`,空字串=關閉)由 `Base.astro` 消費,見父層 `src/AGENTS.md` 的 Analytics & consent。
|
||||
- **圖片欄位**:`FieldType` 有 `"image"`;`hero_image` / `og_image` 用呢個型別,`/admin/settings` 會 render `ImageField`(可上傳去 R2)。
|
||||
- 顯示用文案繁體中文。
|
||||
|
||||
|
||||
@@ -50,6 +50,21 @@ export const SETTINGS_GROUPS: SettingsGroup[] = [
|
||||
{ key: "og_image", label: "分享圖片 (OG image)", type: "image" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "追蹤分析",
|
||||
fields: [
|
||||
{
|
||||
key: "ga4_measurement_id",
|
||||
label: "GA4 評估 ID",
|
||||
placeholder: "G-XXXXXXXXXX",
|
||||
},
|
||||
{
|
||||
key: "meta_pixel_id",
|
||||
label: "Meta Pixel ID",
|
||||
placeholder: "123456789012345",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const ALL_SETTING_KEYS = SETTINGS_GROUPS.flatMap((g) => g.fields.map((f) => f.key));
|
||||
|
||||
@@ -9,6 +9,8 @@ interface Props {
|
||||
publishedTime?: Date | null;
|
||||
image?: string;
|
||||
jsonLd?: object | null | (object | null)[];
|
||||
gaId?: string;
|
||||
metaPixelId?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
@@ -19,8 +21,12 @@ const {
|
||||
publishedTime = null,
|
||||
image,
|
||||
jsonLd = null,
|
||||
gaId = "",
|
||||
metaPixelId = "",
|
||||
} = Astro.props;
|
||||
|
||||
const hasAnalytics = Boolean(gaId || metaPixelId);
|
||||
|
||||
const siteName = SITE_NAME;
|
||||
const origin = (Astro.site ?? Astro.url).toString().replace(/\/$/, "");
|
||||
const canonical = new URL(Astro.url.pathname, origin + "/").toString();
|
||||
@@ -61,6 +67,65 @@ const jsonLdHtml = jsonLdItems.length
|
||||
|
||||
{jsonLdHtml && <script type="application/ld+json" is:inline set:html={jsonLdHtml} />}
|
||||
|
||||
{
|
||||
hasAnalytics && (
|
||||
<script is:inline define:vars={{ gaId, metaPixelId }}>
|
||||
window.__analyticsIds = { gaId, metaPixelId };
|
||||
window.__loadAnalytics = function () {
|
||||
var ids = window.__analyticsIds || {};
|
||||
if (window.__analyticsLoaded) return;
|
||||
window.__analyticsLoaded = true;
|
||||
if (ids.gaId) {
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
window.gtag = function () {
|
||||
window.dataLayer.push(arguments);
|
||||
};
|
||||
window.gtag("js", new Date());
|
||||
window.gtag("config", ids.gaId);
|
||||
var gaScript = document.createElement("script");
|
||||
gaScript.async = true;
|
||||
gaScript.src =
|
||||
"https://www.googletagmanager.com/gtag/js?id=" +
|
||||
encodeURIComponent(ids.gaId);
|
||||
document.head.appendChild(gaScript);
|
||||
}
|
||||
if (ids.metaPixelId) {
|
||||
(function (f, b, e, v, n, t, s) {
|
||||
if (f.fbq) return;
|
||||
n = f.fbq = function () {
|
||||
n.callMethod
|
||||
? n.callMethod.apply(n, arguments)
|
||||
: n.queue.push(arguments);
|
||||
};
|
||||
if (!f._fbq) f._fbq = n;
|
||||
n.push = n;
|
||||
n.loaded = true;
|
||||
n.version = "2.0";
|
||||
n.queue = [];
|
||||
t = b.createElement(e);
|
||||
t.async = true;
|
||||
t.src = v;
|
||||
s = b.getElementsByTagName(e)[0];
|
||||
s.parentNode.insertBefore(t, s);
|
||||
})(
|
||||
window,
|
||||
document,
|
||||
"script",
|
||||
"https://connect.facebook.net/en_US/fbevents.js",
|
||||
);
|
||||
window.fbq("init", ids.metaPixelId);
|
||||
window.fbq("track", "PageView");
|
||||
}
|
||||
};
|
||||
try {
|
||||
if (window.localStorage.getItem("cookie_consent") === "granted") {
|
||||
window.__loadAnalytics();
|
||||
}
|
||||
} catch (e) {}
|
||||
</script>
|
||||
)
|
||||
}
|
||||
|
||||
<link rel="sitemap" href="/sitemap.xml" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
@@ -79,6 +144,58 @@ const jsonLdHtml = jsonLdItems.length
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
{
|
||||
hasAnalytics && (
|
||||
<div id="cookie-consent" class="cookie-consent" hidden>
|
||||
<p class="cookie-consent__text">
|
||||
我哋用 Cookie 分析網站使用情況(Google Analytics 同 Meta),以改善服務。你可以選擇接受或拒絕。
|
||||
</p>
|
||||
<div class="cookie-consent__actions">
|
||||
<a class="cookie-consent__link" href="/privacy">私隱政策</a>
|
||||
<button class="cookie-consent__btn" type="button" data-consent="denied">
|
||||
拒絕
|
||||
</button>
|
||||
<button
|
||||
class="cookie-consent__btn cookie-consent__btn--primary"
|
||||
type="button"
|
||||
data-consent="granted"
|
||||
>
|
||||
接受
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
hasAnalytics && (
|
||||
<script is:inline>
|
||||
(function () {
|
||||
var el = document.getElementById("cookie-consent");
|
||||
if (!el) return;
|
||||
var choice = null;
|
||||
try {
|
||||
choice = window.localStorage.getItem("cookie_consent");
|
||||
} catch (e) {}
|
||||
if (!choice) el.hidden = false;
|
||||
el.addEventListener("click", function (event) {
|
||||
var target = event.target;
|
||||
var btn = target && target.closest
|
||||
? target.closest("[data-consent]")
|
||||
: null;
|
||||
if (!btn) return;
|
||||
var value = btn.getAttribute("data-consent");
|
||||
try {
|
||||
window.localStorage.setItem("cookie_consent", value);
|
||||
} catch (e) {}
|
||||
if (value === "granted" && typeof window.__loadAnalytics === "function") {
|
||||
window.__loadAnalytics();
|
||||
}
|
||||
el.hidden = true;
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
)
|
||||
}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -90,4 +207,60 @@ const jsonLdHtml = jsonLdItems.length
|
||||
font-family: "Noto Sans HK", "PingFang HK", "Microsoft JhengHei", system-ui, -apple-system, sans-serif;
|
||||
background: #faf8f4;
|
||||
}
|
||||
|
||||
.cookie-consent[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cookie-consent {
|
||||
position: fixed;
|
||||
inset: auto 0 0 0;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px 24px;
|
||||
padding: 16px 24px;
|
||||
background: #faf8f4;
|
||||
border-top: 1px solid #e5dfd6;
|
||||
}
|
||||
|
||||
.cookie-consent__text {
|
||||
margin: 0;
|
||||
flex: 1 1 320px;
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
color: #4a453f;
|
||||
}
|
||||
|
||||
.cookie-consent__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.cookie-consent__link {
|
||||
font-size: 13px;
|
||||
color: #6e6862;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.cookie-consent__btn {
|
||||
padding: 8px 20px;
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #1a1917;
|
||||
background: transparent;
|
||||
border: 1px solid #1a1917;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cookie-consent__btn--primary {
|
||||
color: #fff;
|
||||
background: #b45309;
|
||||
border-color: #b45309;
|
||||
}
|
||||
</style>
|
||||
|
||||
+14
-1
@@ -1,9 +1,22 @@
|
||||
---
|
||||
import Base from "../layouts/Base.astro";
|
||||
import { SITE_NAME } from "../config";
|
||||
import { getSettings } from "../data/content";
|
||||
import { getDb } from "../lib/db";
|
||||
import { getEnv } from "../lib/env";
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
const settings = await getSettings(getDb(getEnv().DB));
|
||||
---
|
||||
|
||||
<Base title="關於我哋" description={`${SITE_NAME} — 香港村屋太陽能一站式服務。`} noindex={true}>
|
||||
<Base
|
||||
title="關於我哋"
|
||||
description={`${SITE_NAME} — 香港村屋太陽能一站式服務。`}
|
||||
noindex={true}
|
||||
gaId={settings.ga4_measurement_id}
|
||||
metaPixelId={settings.meta_pixel_id}
|
||||
>
|
||||
<div style="max-width:640px;margin:15vh auto;padding:0 24px;text-align:center;font-family:'Noto Sans HK',sans-serif;">
|
||||
<h1 style="font-size:28px;font-weight:800;color:#1A1917;">我哋嘅服務已整合到主頁</h1>
|
||||
<p style="color:#6E6862;line-height:1.9;">請返去主頁睇晒我哋嘅村屋太陽能服務、案例同流程。</p>
|
||||
|
||||
@@ -41,6 +41,8 @@ const jsonLd = post
|
||||
publishedTime={post.publishedAt}
|
||||
image={post.coverImage ?? undefined}
|
||||
jsonLd={jsonLd}
|
||||
gaId={settings.ga4_measurement_id}
|
||||
metaPixelId={settings.meta_pixel_id}
|
||||
>
|
||||
<BlogPost
|
||||
title={post.title}
|
||||
|
||||
@@ -25,6 +25,8 @@ Astro.response.headers.set("Cache-Control", "public, s-maxage=60, stale-while-re
|
||||
title="太陽能知識庫"
|
||||
description={settings.seo_default_description || "村屋太陽能嘅實用資訊、安裝心得同回本分析。"}
|
||||
image={settings.og_image}
|
||||
gaId={settings.ga4_measurement_id}
|
||||
metaPixelId={settings.meta_pixel_id}
|
||||
>
|
||||
<BlogIndex posts={items} settings={settings} client:idle />
|
||||
</Base>
|
||||
|
||||
@@ -26,6 +26,8 @@ Astro.response.headers.set(
|
||||
description={settings.seo_default_description}
|
||||
image={settings.og_image}
|
||||
jsonLd={jsonLd}
|
||||
gaId={settings.ga4_measurement_id}
|
||||
metaPixelId={settings.meta_pixel_id}
|
||||
>
|
||||
<HomePage data={data} client:idle />
|
||||
</Base>
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
---
|
||||
import Base from "../layouts/Base.astro";
|
||||
import { SITE_NAME } from "../config";
|
||||
import { getSettings } from "../data/content";
|
||||
import { getDb } from "../lib/db";
|
||||
import { getEnv } from "../lib/env";
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
const settings = await getSettings(getDb(getEnv().DB));
|
||||
const siteName = settings.company_name || SITE_NAME;
|
||||
const email = settings.email || "";
|
||||
---
|
||||
|
||||
<Base
|
||||
title="私隱政策"
|
||||
description={`${siteName} 網站私隱政策:Cookie、分析工具同資料使用方式。`}
|
||||
gaId={settings.ga4_measurement_id}
|
||||
metaPixelId={settings.meta_pixel_id}
|
||||
>
|
||||
<main class="policy">
|
||||
<p class="eyebrow">私隱政策</p>
|
||||
<h1>Cookie 同資料使用</h1>
|
||||
<p class="lead">
|
||||
呢頁解釋我哋點樣喺 {siteName} 網站使用 Cookie 同分析工具。我哋只會喺你同意之後先載入分析工具,亦唔會出售你嘅個人資料。
|
||||
</p>
|
||||
|
||||
<section>
|
||||
<h2>1. 咩係 Cookie</h2>
|
||||
<p>
|
||||
Cookie 係網站存放喺你瀏覽器嘅細細檔案,用嚟記住你嘅設定同行為。你嘅瀏覽器亦可能使用類似技術(例如
|
||||
localStorage)去記住你嘅選擇。
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>2. 我哋用嘅工具</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Google Analytics 4</strong> — 分析網站流量同瀏覽行為(例如瀏覽頁面、停留時間、裝置同來源),
|
||||
幫我哋了解同改善網站內容。
|
||||
</li>
|
||||
<li>
|
||||
<strong>Meta Pixel</strong> — 量度廣告同推廣活動嘅成效,了解訪客喺網站嘅互動情況。
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
呢啲工具由第三方供應商(Google、Meta)提供,佢哋嘅資料處理方式受各自嘅私隱政策約束。我哋唔會透過呢啲工具收集你嘅姓名、電話或電郵等可直接識別身份嘅資料。
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>3. 你嘅選擇</h2>
|
||||
<p>
|
||||
你入到網站時會見到 Cookie 提示,可以選擇「接受」或「拒絕」。
|
||||
<strong>如果你拒絕,我哋唔會載入 Google Analytics 或 Meta Pixel,亦唔會寫入相關 Cookie。</strong>
|
||||
你嘅選擇會存放喺你嘅瀏覽器;你亦可隨時撳下面嘅按鈕清除選擇,下次再重新決定。
|
||||
</p>
|
||||
<button id="reset-consent" type="button">重設 Cookie 偏好</button>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>4. 資料保留同安全</h2>
|
||||
<p>
|
||||
分析資料一般以匿名化或彙總形式保存,並只會保留達到上述目的所需嘅時間。我哋會以合理嘅技術同管理措施保障資料安全。
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>5. 查詢</h2>
|
||||
<p>
|
||||
如你對我哋嘅 Cookie 使用或私隱做法有任何查詢,歡迎電郵
|
||||
{email ? <a href={`mailto:${email}`}>{email}</a> : "我哋"} 同我哋聯絡。
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<p class="back"><a href="/">← 返去主頁</a></p>
|
||||
</main>
|
||||
</Base>
|
||||
|
||||
<script is:inline>
|
||||
(function () {
|
||||
var btn = document.getElementById("reset-consent");
|
||||
if (!btn) return;
|
||||
btn.addEventListener("click", function () {
|
||||
try {
|
||||
window.localStorage.removeItem("cookie_consent");
|
||||
} catch (e) {}
|
||||
window.location.reload();
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.policy {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
padding: 96px 24px 120px;
|
||||
font-family: "Noto Sans HK", "PingFang HK", "Microsoft JhengHei", system-ui, sans-serif;
|
||||
color: #2f2b27;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
margin: 0 0 12px;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0 0 24px;
|
||||
font-family: "Noto Serif HK", serif;
|
||||
font-size: 34px;
|
||||
font-weight: 700;
|
||||
color: #1a1917;
|
||||
}
|
||||
|
||||
.lead {
|
||||
margin: 0 0 40px;
|
||||
font-size: 16px;
|
||||
line-height: 1.9;
|
||||
color: #4a453f;
|
||||
}
|
||||
|
||||
section {
|
||||
margin: 0 0 36px;
|
||||
padding-top: 28px;
|
||||
border-top: 1px solid #e5dfd6;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 0 12px;
|
||||
font-family: "Noto Serif HK", serif;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1a1917;
|
||||
}
|
||||
|
||||
p,
|
||||
li {
|
||||
font-size: 15px;
|
||||
line-height: 1.9;
|
||||
color: #4a453f;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0 0 12px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 8px;
|
||||
padding: 10px 22px;
|
||||
font: inherit;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #1a1917;
|
||||
background: transparent;
|
||||
border: 1px solid #1a1917;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #1a1917;
|
||||
color: #faf8f4;
|
||||
}
|
||||
|
||||
.back {
|
||||
margin-top: 48px;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user