Repository Public
1"use client";2import type { ReactNode } from "react";3import { useT } from "@/i18n";4import { publicDict } from "@/i18n/public";5import { LangToggle, ThemeToggle, Wordmark } from "@/components/ui";67/** `brand` is the owner's accent colour (Settings → Profile): a decorative bar, never used for text. */8export function PublicFrame({ children, narrow, brand }: { children: ReactNode; narrow?: boolean; brand?: string }) {9 const t = useT(publicDict);10 return (11 <div className="min-h-dvh flex flex-col">12 <a href="#main" className="sr-only focus:not-sr-only focus:fixed focus:z-[70] focus:top-3 focus:left-3 btn btn-primary">Aller au contenu / Skip to content</a>13 <header className="sticky top-0 z-40 glass border-b border-[var(--glass-line)] pt-[env(safe-area-inset-top)]">14 {brand ? <div aria-hidden className="h-[3px] w-full" style={{ background: `linear-gradient(90deg, ${brand}, var(--a2))` }} /> : null}15 <div className="flex items-center justify-between px-4 sm:px-6 h-[58px] max-w-5xl w-full mx-auto">16 <Wordmark />17 <span className="flex items-center"><LangToggle /><ThemeToggle /></span>18 </div>19 </header>20 <main id="main" tabIndex={-1} className={`flex-1 w-full mx-auto px-4 sm:px-6 pt-5 pb-6 sm:py-10 outline-none ${narrow ? "max-w-2xl" : "max-w-5xl"}`}>{children}</main>21 <footer className="max-w-5xl w-full mx-auto px-5 sm:px-6 pt-6 pb-[max(1.75rem,env(safe-area-inset-bottom))] flex flex-wrap items-center justify-between gap-3 text-[13px] t-mute">22 <span>SP Calendar · calendar.spboucher.ai</span>23 <nav aria-label="Liens" className="flex gap-5"><a href="/privacy" className="underline underline-offset-2 py-2">{t("privacy")}</a><a href="/admin" className="underline underline-offset-2 py-2">{t("adminLink")}</a></nav>24 </footer>25 </div>26 );27}2829/** Small translated blocks used by server pages. */30export function PublicText({ k, vars }: { k: keyof typeof publicDict.fr; vars?: Record<string, string | number> }) {31 const t = useT(publicDict);32 return <>{t(k, vars)}</>;33}34