From 079f6fb67115a64b5e6c08c2f493cc8273bcbb26 Mon Sep 17 00:00:00 2001 From: codemenschendev Date: Sat, 26 Sep 2026 15:35:07 +0700 Subject: [PATCH] design(web): legal pages, consent banner and header/footer frame The last pages outside the new look, and two things every page shares. - Terms, withdrawal, privacy and imprint: head band, the text in one white card, the other documents as pill links. The security page gets its six promises as three-column cards with a shield icon, and its text and tables in the same card. - Consent banner: switches instead of bare checkboxes, the card look of the other pages. "Allow all" and "Decline all" stay equal in size and style, because consent may not be nudged (EDPB guidelines 03/2022); "Save choice" sits under them as a link. - Header and footer use the 1200px frame of the redesigned pages. The logo sat at 204px in the header and 184px in the footer, and neither matched the page content. No text changed. Project: Appwerk --- apps/web/src/app/globals.css | 25 +++++++++++- apps/web/src/app/legal.css | 40 +++++++++++++++++++ apps/web/src/components/AdConsent.tsx | 42 +++++++++----------- apps/web/src/components/LegalPage.tsx | 50 +++++++++++++----------- apps/web/src/components/SecurityPage.tsx | 38 ++++++++++-------- 5 files changed, 132 insertions(+), 63 deletions(-) create mode 100644 apps/web/src/app/legal.css diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index 786b2ce..6c85c99 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -186,7 +186,8 @@ textarea:focus, .choice:focus-within { outline: 2px solid var(--accent); outline /* ---------- Footer (appwerk styles.css:175-183) ---------- */ footer.site { background: var(--ink); color: #b8becd; padding: 56px 0 40px; margin-top: 0; font-size: 15px; } -footer.site .wrap { max-width: 1120px; } +/* Header and footer share the 1200px frame of the redesigned pages, so the logo lines up. */ +.nav .wrap, footer.site .wrap { max-width: 1200px; } footer.site .nav-logo { color: #fff; } footer.site .logo-by { color: #94a3b8; } .footer-inner { display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; flex-wrap: wrap; margin-bottom: 32px; } @@ -312,3 +313,25 @@ footer.site .logo-by { color: #94a3b8; } .chat-shot img { width: 100%; height: 100%; object-fit: cover; display: block; } .chat-shot-remove { position: absolute; top: 2px; right: 2px; width: 24px; height: 24px; border-radius: 50%; border: 0; background: rgba(11, 14, 20, .75); color: #fff; cursor: pointer; line-height: 24px; padding: 0; } @media (max-width: 520px) { .chat-msg { max-width: 100%; } .chat-compose { flex-direction: column; align-items: stretch; } .chat-compose-side { justify-content: flex-end; } } + +/* ---------- Consent banner (AdConsent) ---------- */ +.consent { position: fixed; left: 16px; right: 16px; bottom: 16px; z-index: 60; max-width: 520px; margin: 0 auto; max-height: calc(100vh - 32px); overflow-y: auto; background: #fff; color: var(--ink); border: 1px solid var(--border); border-radius: 20px; box-shadow: 0 30px 70px rgba(15, 23, 42, 0.22); padding: 22px 22px 18px; } +.consent-title { display: block; font-family: var(--font-display); font-size: 1.1rem; margin-bottom: 6px; } +.consent-text { margin: 0 0 14px; font-size: 0.9rem; line-height: 1.55; color: var(--ink-soft); } +.consent-text a { color: var(--accent-hover); } +.consent-opts { display: grid; gap: 8px; margin-bottom: 16px; } +.consent-opt { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; background: var(--tint); border-radius: 12px; padding: 12px 14px; font-size: 0.86rem; line-height: 1.45; cursor: pointer; } +.consent-opt strong { display: block; font-size: 0.92rem; margin-bottom: 2px; } +.consent-opt span span { color: var(--ink-soft); } +.consent-switch { appearance: none; -webkit-appearance: none; flex: none; position: relative; width: 40px; height: 24px; margin: 2px 0 0; border-radius: 99px; background: #cbd5e1; cursor: pointer; transition: background 0.15s ease; } +.consent-switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(15, 23, 42, 0.25); transition: transform 0.15s ease; } +.consent-switch:checked { background: var(--accent); } +.consent-switch:checked::after { transform: translateX(16px); } +.consent-switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } +.consent-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; } +.consent-btn { padding: 11px 14px; border: 1.5px solid var(--ink); border-radius: 12px; background: #fff; color: var(--ink); font: 600 0.9rem var(--font-body); cursor: pointer; } +.consent-btn:hover { background: var(--ink); color: #fff; } +.consent-save { grid-column: 1 / -1; padding: 6px; background: none; border: 0; font: 600 0.86rem var(--font-body); color: var(--accent); cursor: pointer; } +.consent-save:hover { text-decoration: underline; } +.consent-reopen { background: none; border: 0; padding: 0; color: #b8becd; font-size: 0.88rem; cursor: pointer; font-family: inherit; } +.consent-reopen:hover { color: var(--paper); } diff --git a/apps/web/src/app/legal.css b/apps/web/src/app/legal.css new file mode 100644 index 0000000..5179733 --- /dev/null +++ b/apps/web/src/app/legal.css @@ -0,0 +1,40 @@ +/* Legal pages and the security page, in the look of the home page: head band, the text in one + white card, links to the other documents as pills. Scoped under `.lg`. */ + +.lg { padding-bottom: 80px; } +.lg .wrap { max-width: 1200px; } +.lg .wrap-narrow { max-width: 820px; } +.lg-band { padding: 40px 0 84px; } +.lg-band h1 { font-size: clamp(2rem, 3.6vw, 2.8rem); letter-spacing: -0.04em; margin: 0 0 12px; } +.lg .lg-lede { font-size: 1.1rem; color: var(--ink-soft); line-height: 1.6; max-width: 40em; margin: 0; } +.lg-body { margin-top: -48px; position: relative; } + +.lg .lg-draft { max-width: none; margin: 0 0 16px; border-radius: 14px; font-size: 0.88rem; line-height: 1.55; } +.lg-card { background: #fff; border: 1px solid var(--border); border-radius: 20px; padding: 8px 36px 32px; box-shadow: 0 30px 70px rgba(15, 23, 42, 0.08); } +.lg .lg-card section, .lg .lg-body > section { padding: 0; margin-top: 28px; } +.lg section h2 { font-size: 1.2rem; letter-spacing: -0.02em; margin: 0 0 8px; } +.lg section p { max-width: 68ch; margin: 0 0 10px; line-height: 1.7; } +.lg .lg-updated { margin: 24px 0 12px; font-size: 0.84rem; color: var(--ink-soft); } +.lg-more { display: flex; flex-wrap: wrap; gap: 8px; } +.lg-more a { padding: 7px 14px; border: 1px solid var(--border); border-radius: 99px; background: #fff; font-size: 0.86rem; font-weight: 600; color: var(--ink); text-decoration: none; } +.lg-more a:hover { border-color: #c7d2fe; color: var(--accent-hover); } + +/* Security page: the promises as cards over the band, then the text. */ +.lg-promises { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; margin-top: -48px; position: relative; } +.lg-promise { background: #fff; border: 1px solid var(--border); border-radius: 16px; padding: 20px; box-shadow: var(--shadow); } +.lg-promise h3 { font-size: 1rem; line-height: 1.3; margin: 12px 0 6px; } +.lg-promise p { font-size: 0.88rem; color: var(--ink-soft); line-height: 1.55; margin: 0; } +.lg-promise-ico { width: 40px; height: 40px; border-radius: 12px; display: grid; place-items: center; background: #dcfce7; color: var(--valid); } +.lg-promise-ico .ico { width: 20px; height: 20px; } +.lg .lg-promises + .lg-body { margin-top: 24px; background: #fff; border: 1px solid var(--border); border-radius: 20px; padding: 8px 36px 32px; box-shadow: var(--shadow); } +.lg .lg-table { margin-top: 12px; border-radius: 14px; } + +@media (max-width: 900px) { + .lg-promises { grid-template-columns: repeat(2, minmax(0, 1fr)); } +} +@media (max-width: 640px) { + .lg-promises { grid-template-columns: minmax(0, 1fr); } + .lg .lg-promises + .lg-body { padding: 4px 20px 24px; margin-left: 16px; margin-right: 16px; } + .lg-band { padding: 28px 0 72px; } + .lg-card { padding: 4px 20px 24px; border-radius: 18px; } +} diff --git a/apps/web/src/components/AdConsent.tsx b/apps/web/src/components/AdConsent.tsx index 5bf7a82..7237842 100644 --- a/apps/web/src/components/AdConsent.tsx +++ b/apps/web/src/components/AdConsent.tsx @@ -58,37 +58,31 @@ export function AdConsentBanner({ d, locale }: { d: Dict; locale: Locale }) { }; const option = (key: keyof Consent, title: string, text: string) => ( -