/*! trentpower.fr · authored source */ /* trentpower.fr screen design system Role: defines the public editorial interface, trust surfaces and responsive layout. Constraints: - no external assets - no analytics, cookies, trackers or third-party embeds - active css, js and font filenames revalidate; frozen release archives carry immutable dated assets - oxblood is an accent, not decoration */ /*! trentpower.fr css architecture cascade order: reset → tokens → base → layout → components → pages → utilities → overrides · tokens define decisions (colours, type, spacing, motion) · base styles target elements; reset normalises · layout positions; components are reusable; pages scope by body[data-page] · utilities are minimal helpers; overrides are rare and documented @font-face declarations sit in @layer fonts (added implicitly at the end of the cascade order — font-face lookups ignore layers anyway). */ @layer reset, tokens, base, layout, components, pages, utilities, overrides; @layer fonts { /* klim type foundry fonts used under purchased web licences for trentpower.fr. display strategy: - styles.css declares only the three critical subset @font-face entries below. the full editorial weights live in /fonts-full.css and are loaded after lcp by /app-enhance.js (which also adds `.fonts-loaded` to so the variables in this file resolve to the full families on the next style recalculation). - until /fonts-full.css arrives, every site-wide `font-family: var(--serif|--sans|--mono)` resolves to the critical subset, then to a system fallback. this guarantees no full woff2 enters the lcp critical chain. - hero (signifier critical) keeps `font-display: swap` because it is the single preloaded font and its arrival is brand-critical. the other two critical aliases use `optional`: if the subset is not delivered within 100 ms, the system fallback holds for that page load — acceptable for nav and small mono labels because the fallback stacks were chosen to mirror the brand metrics. */ @font-face { font-family: 'Signifier Critical'; font-style: normal; font-weight: 300; font-display: swap; src: url('/fonts/subsets/signifier-light-hero.woff2') format('woff2'); } @font-face { font-family: 'Söhne Critical'; font-style: normal; font-weight: 500; font-display: optional; src: url('/fonts/subsets/soehne-kraftig-nav.woff2') format('woff2'); } @font-face { font-family: 'Söhne Mono Critical'; font-style: normal; font-weight: 400; font-display: optional; src: url('/fonts/subsets/soehne-mono-buch-labels.woff2') format('woff2'); } } @layer tokens { :root { color-scheme: light; /* Two-surface model with explicit positive scoping. the public profile sits on warm editorial paper. the trust system, archive and source surfaces sit on a slightly cooler warm grey — the records room of the same artefact. overlays use a grey scrim above either surface; the panel itself returns to paper. every public page declares its surface explicitly via or . the :root default below is ivory so a page that forgets the attribute falls through to the editorial surface, never to the record surface — grey is never the global default. */ /* three paper tones — main paper for the public face, record paper for the archive, raised paper for inspected objects. Raised-high is reserved for overlay panels that sit above the scrim regardless of the underlying page surface. */ --paper-main: #FAF7F0; /* front of house · editorial paper */ --paper-record: #E9E5DC; /* back of house · warm archival grey */ --paper-raised: #F7F3EA; /* card on record surface · paper sheet */ --paper-raised-high: #FBF8F1; /* overlay panels (.modal) · highest lift on the record surface */ --paper-project: #FFFDF8; /* homepage editorial insert (what's on in paris card) — warmer than raised-high so it visibly lifts off front-of-house ivory without going stark white */ /* phase 52 · single canonical archival surface so the verify and integrity record cards (plus the command block beneath them) read as one verification-system family of paper sheets. */ --surface-archival: #F4F1EA; --surface-page: var(--paper-main); --surface-card: var(--surface-archival); --overlay-scrim: rgba(28, 25, 22, 0.46); /* rules / hairlines tied to ink density rather than a hard opaque grey, so they read consistently on every paper tone. phase 39 · softened from 0.14 to ~10% mix for editorial calm. phase 45 · further reduced to 8% — dividers now read as deliberate registration marks rather than ui wireframe. phase 60 · softened a further ~10% (8% → 7%) so the hairlines stop competing with typography on dense surfaces (/source/, /verify, /integrity). */ --rule: color-mix(in srgb, var(--ink) 7%, transparent); --rule-strong: rgba(42, 38, 33, 0.22); /* ink and accent — semantic names for the new system. */ --ink: #211F1C; --ink-muted: #67625B; --accent: #6E1A14; /* legacy aliases — older rules (verify-command-block, .modal, etc.) reference these names; new code should prefer the semantic --paper-* / --ink-* / --rule tokens above. */ --bg-public: var(--paper-main); --bg-archive: var(--paper-record); --bg: var(--surface-page); --bg2: var(--paper-record); --card: var(--paper-raised-high); --fg: var(--ink); /* phase 40 · slightly warmer body register (#5f5a53). same warm hue as phase 39 (#5e5952), 1 unit lighter — preserves the editorial warmth while keeping aaa-equivalent serif legibility on mobile. dark mode --fg2 unchanged (handled in dark block). */ --fg2: #5F5A53; --fg3: #706B66; --ac: var(--accent); --ac2: #8B2218; /* warmer oxblood · hover only */ /* accent split: --accent is decorative (rules, dividers, focus- ring fills, button backgrounds, ::after underline tints). in light mode --accent-text aliases --accent (oxblood reads on paper at aaa). in dark mode --accent-text is a brighter coral so accent-coloured text (link hover, code-string, trust bullets, project cta labels, etc.) preserves wcag aa contrast on dark paper, while --accent stays deep crimson for decorative use. see @media (prefers-color-scheme: dark) below. */ --accent-text: var(--accent); --accent-hover: var(--ac2); /* phase 39 · --bd moved from solid #d8d4cc to a 10% ink mix so metadata-row dividers, record-grid hairlines, table edges and security-section borders all read as quieter editorial pacing rather than wireframe scaffolding. --bd-soft kept at the solid lighter tint for surfaces that need a deliberate plate. */ --bd: color-mix(in srgb, var(--ink) 10%, transparent); --bd-soft: #E6E1D8; /* editorial focus-visible tokens. 45 % alpha oxblood reads as a calm typographic accent rather than a browser-debug rectangle. 2 px ring + 3 px offset gives obvious keyboard visibility without dominating the page. component rules consume these via var(--focus-colour) etc.; dark-mode overrides land below. */ --focus-colour: rgba(110, 26, 20, 0.45); --focus-ring-width: 2px; --focus-offset: 3px; --focus-radius: 0; /* phase 46 · semantic radius tokens. three categories — cards/archival objects, larger overlay panels, and true pill objects (the trust-mark certification strip). every literal radius on a card or overlay surface now flows through these tokens so the curvature stays intentional and easy to refine in one place. layout surfaces (body, main, footer, nav, buttons, rules) remain sharp — radius identifies objects, not layout. */ --radius-soft: 7px; --radius-panel: 10px; --radius-pill: 999px; /* Code-token colours · used by .code-str / .code-var inside the trust-command and verify-command panels. tokenised so dark mode and prefers-contrast can override them without touching the component rules. */ --code-string: #2563EB; --code-var: #7C3AED; /* Critical-only stacks. the full editorial family names ('signifier', 'söhne', 'söhne mono') are intentionally absent until /fonts-full.css loads after lcp and adds `.fonts-loaded` to , at which point the override at the bottom of this file flips these stacks to put the full weights first. keeps every full woff2 out of the lcp critical request chain. */ --serif: 'Signifier Critical', Georgia, 'Times New Roman', serif; --sans: 'Söhne Critical', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; --mono: 'Söhne Mono Critical', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; } /* explicit positive surface scoping. - data-surface="editorial" → /, /privacy/ - data-surface="record" → /verify/, /source/, /integrity/, /integrity/releases/, /security/, /security/acknowledgments/, error pages - data-surface="archive" → kept as a synonym of "record" so any cached html still resolves correctly. */ body[data-surface="editorial"] { --surface-page: var(--paper-main); --surface-card: var(--surface-archival); --bg: var(--paper-main); } body[data-surface="record"] { --surface-page: var(--paper-record); --surface-card: var(--surface-archival); --bg: var(--paper-record); } body[data-surface="archive"] { --surface-page: var(--paper-record); --surface-card: var(--surface-archival); --bg: var(--paper-record); } /* Back-of-house pages calm the supporting-text and rule tokens by a small amount. primary tokens (--fg / --ink / --link) stay unchanged so headings, filenames, hashes, fingerprints and action links remain authoritative. the override fires on every page that wears the brand-only masthead — that includes privacy, verify, integrity, source, releases, security and the error pages. the homepage carries no data-masthead attribute and so keeps the original (slightly stronger) muted tones. Contrast: --fg2 #6c6760 vs --paper-record #e9e5dc ≈ 4.6:1; vs --paper-main #faf7f0 ≈ 5.4:1. --fg3 #756f69 vs the same backgrounds ≈ 4.0:1 and 4.7:1. both safely above the 3:1 large- text bar; --fg3 sits at the aa edge for very small mono labels on the warm-grey surface. */ body[data-masthead="brand-only"] { --fg2: #6C6760; --fg3: #756F69; /* phase 39 · brand-only masthead pages get an even quieter rule register (8% mix) so the brand-only chrome stays nearly subliminal against editorial type. */ --rule: color-mix(in srgb, var(--ink) 8%, transparent); } } /* ─── Accessibility-preference overlays ───────────────────────── a third @layer tokens block holds three media-query branches that redefine the canonical token set under user preferences. the surface variants (body[data-surface=…], body[data-masthead=…]) reference these tokens via var(); they automatically inherit the redefined values without their own overrides. - prefers-color-scheme: dark — warm editorial dark, aaa contrast - prefers-contrast: more — pure black/white, thicker rules - dark + contrast (intersect) — pure white on pure black - forced-colors: active — windows high contrast mode */ @layer tokens { @media (prefers-color-scheme: dark) { :root { color-scheme: dark; /* warm editorial paper, not pure black. the hue (~33° / 9% / 9%) mirrors the light paper-main inverted with the warm cast preserved. */ --paper-main: #1B1916; --paper-record: #22201B; --paper-raised: #26231F; --paper-raised-high: #2C2925; --paper-project: #1F1D1A; /* phase 52 · dark-mode canonical archival surface mirrors light. */ --surface-archival: #26231F; --overlay-scrim: rgba(0, 0, 0, 0.55); /* hairlines flip to ink-on-paper opacities expressed in the dark ink hue, so they read consistently on every paper tone. phase 39 · softened to 12% (vs 10% in light). phase 45 · further reduced to 10% — dark dividers now match the same registrational register as the light :root above. phase 60 · softened a further ~10% (10% → 9%) in step with the light variant; dense surfaces breathe a touch more. */ --rule: color-mix(in srgb, var(--ink) 9%, transparent); --rule-strong: rgba(240, 234, 224, 0.30); /* cream ink — warm, editorial, aaa against #1b1916. */ --ink: #F0EAE0; /* ~14.6:1 vs --paper-main */ --ink-muted: #C9C2B7; /* ~9.6:1 vs --paper-main */ /* deep crimson — lacquered ink, oxblood leather, archival annotation. replaces the earlier bright-coral dark-mode accent (#e68675). used for decorative accent: focus-ring fills, hover ::after underlines, button backgrounds with text on top (aaa contrast white-on-crimson), divider tints, glyph fills. text use of accent (color: var(--accent-text)) migrates to var(--accent-text) below — at #9e2f2a on dark paper #1b1916 contrast is ~2.4:1, which fails wcag aa for text. the split-token architecture keeps the editorial crimson register on decoration and keeps every accent- coloured text use AA-compliant. */ --accent: #9E2F2A; /* ~2.4:1 vs --paper-main · decorative only */ --accent-text: #D86459; /* ~4.9:1 vs --paper-main · text only · aa */ --accent-hover: #B13A34; /* hover · brightens subtly, not glow */ --ac2: #B13A34; /* legacy alias, anchored on hover hue */ --fg3: #A39C92; /* tertiary mono labels */ /* phase 39 · --bd moved from solid #3a352f to a 12% ink mix (paired with --rule above). --bd-soft retained as a solid tone for surfaces that need a plate. */ --bd: color-mix(in srgb, var(--ink) 12%, transparent); --bd-soft: #332F2A; /* Focus-visible token: anchored on the brighter --accent-text hue (#d86459) so the ring composites to ≥3:1 against dark paper (wcag 1.4.11 non-text contrast). the deep crimson #9e2f2a is too dark for focus rings on dark paper — anchored at 2.4:1 raw, which fails 1.4.11 even with high alpha. */ --focus-colour: rgba(216, 100, 89, 0.85); /* Code-token colours — desaturated for editorial calm. */ --code-string: #99D5A1; /* mint, ~7.5:1 */ --code-var: #C8AEEA; /* warm violet, ~8.9:1 */ } /* selection contrast on dark paper: paper-on-deep-crimson (the light-mode `color: var(--bg)` default) drops to ~2.4:1 in dark mode. override to cream-on-deep-crimson (~6.2:1, aa). */ ::selection { color: var(--ink); } /* Brand-only masthead overrides on dark paper: calm fg2/fg3 slightly more so they sit gently above body text. */ body[data-masthead="brand-only"] { --fg2: #B8B0A4; --fg3: #968F86; /* phase 39 · brand-only masthead on dark paper softened to 10% ink mix (matches the brand-only register on light). */ --rule: color-mix(in srgb, var(--ink) 10%, transparent); } } @media (prefers-contrast: more) { :root { --ink: #000000; --ink-muted: #1A1A1A; --paper-main: #FFFFFF; --paper-record: #F4F2EC; --rule: rgba(0, 0, 0, 0.45); --rule-strong: rgba(0, 0, 0, 0.7); } a { text-decoration-thickness: 2px; text-underline-offset: 0.18em; } :focus-visible { /* keep: wcag 2.4.7 — increased focus visibility under user- requested high contrast must override component-level rings. */ outline-width: 3px !important; outline-offset: 3px !important; } } @media (prefers-color-scheme: dark) and (prefers-contrast: more) { :root { --ink: #FFFFFF; --paper-main: #000000; --paper-record: #0A0A0A; --rule: rgba(255, 255, 255, 0.55); --rule-strong: rgba(255, 255, 255, 0.8); } } /* windows high contrast mode (and other forced-colors environments). remap key tokens to system colour keywords so user-controlled palettes win. forced-color-adjust is intentionally not set; interactive controls keep their own visible borders so they remain distinguishable from body text. */ @media (forced-colors: active) { :root { --ink: CanvasText; --paper-main: Canvas; --paper-record: Canvas; --paper-raised: Canvas; --paper-raised-high: Canvas; --accent: LinkText; --rule: CanvasText; --rule-strong: CanvasText; } a { color: LinkText; } .cite-btn, .site-footer__language button, .verify-record-action { color: ButtonText; border-color: ButtonText; } :focus-visible { outline-color: Highlight; } } } @layer layout { /* Brand-only masthead. the full trent power + nav strip is the homepage identity. every other page (privacy, the entire record layer, error pages) shows only trent power. same height, hairline rule, mobile two-line stack — the brand stays exactly as on the homepage; the wayfinding list disappears. (Retired: the nav-toggle / nav-links system is gone site-wide; the masthead now stands alone in every header.) */ } @layer base { /* opentype defaults wherever signifier renders editorial prose: kerning, ligatures, contextual alternates, old-style figures. */ .hero-statement, .hero-body, .principle-title, .principle-body, .chapter-title, .chapter-detail, .project-desc, .project-subline, .preview-editorial, .page-title, .page-lede, .page-body p, .page-subtitle, .modal-text { font-feature-settings: "kern" 1, "liga" 1, "onum" 1, "calt" 1; font-kerning: normal; font-variant-numeric: oldstyle-nums proportional-nums; text-rendering: optimizeLegibility; } /* tabular lining figures wherever evidence renders, so columns of numerals align and dates / hashes / file paths read precisely. */ code, .technical-list, .code-path, .fingerprint, .section-label, .chapter-label, .page-meta, .preview-meta, .preview-title, .site-footer__colophon, .nav-mark, .contact-email { font-variant-numeric: tabular-nums lining-nums; } } @layer reset { /* reset & base */ *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; } ::selection { background: var(--ac); color: var(--bg); } html { font-size: 16px; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } @media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } } } @layer tokens { /* anchor offset for the fixed nav. 52px nav height plus a small breathing room so section labels land cleanly just under the nav, not flush. */ :root { --nav-offset: 60px; /* Anchor-offset tokens for native anchor navigation on the home section ids. mobile nav-inner height varies with the device — ~58 px on non-notched displays and ~102 px on notched iphones (env(safe-area-inset-top) + padding + content). the mobile value is dynamic: `calc(env(safe-area-inset-top) + 4.5rem)` = 72 px non-notch / ~116 px notched, landing the section heading ~14 px below the nav at every viewport. desktop nav is `position: fixed` at 52 px — the 7 rem fixed value lands the heading with calm breathing room and doesn't need an env() lookup (desktop browsers don't expose a meaningful safe-area-inset-top). */ --anchor-offset-mobile: calc(env(safe-area-inset-top) + 4.5rem); --anchor-offset-desktop: 7rem; /* Safe-area-inset tokens — surface env() values as named tokens so edge-touching elements (sticky/fixed nav, footer, overlays) can refer to them via var() with a sane 0 fallback. `viewport-fit= cover` (set in ) lets the layout reach the full visual viewport including the notch/Dynamic island and the ios home-indicator strip; the tokens give us a single point to reason about edge clearance. use only where the layout actually reaches a viewport edge — the brief's guidance is "do not blindly add safe-area padding everywhere." most centred / max-width content does not need it. */ --safe-top: env(safe-area-inset-top, 0px); --safe-right: env(safe-area-inset-right, 0px); --safe-bottom: env(safe-area-inset-bottom, 0px); --safe-left: env(safe-area-inset-left, 0px); } } @layer base { [id] { scroll-margin-top: var(--nav-offset); } /* native anchor navigation on the home section ids lives in @layer overrides below — bare id selectors (per the brief) are legal there. search "anchor landing offsets" to find the declarations. */ html, body { /* `overflow-x: clip` is the modern variant — does not establish a scroll context the way `hidden` does, so positioned descendants remain reachable via fragment navigation. older browsers ignore `clip` and fall through to the next declaration. */ overflow-x: hidden; overflow-x: clip; } /* editorial abbreviation register. native html gets a quiet dotted underline so the conventional "hover for expansion" affordance reads correctly without shouting. browsers without dotted-underline support fall through to nothing — acceptable degradation. */ abbr[title] { text-decoration: underline dotted var(--rule); text-decoration-thickness: 1px; text-underline-offset: 0.18em; cursor: help; } /* phase 43 · semantic enrichment register. `` marks the first definitional occurrence of a concept (integrity manifest, source mirror, signed release, page record, detached signature). browser-default italic is overridden — the element should read as a quiet definitional anchor, not as a typographic flourish. */ dfn { font-style: normal; font-weight: 500; color: var(--fg); } /* `` marks computed output — sha-256 hashes, pgp fingerprints, validation timestamps. mono register so it sits inside the technical-mono family with ``, but slightly smaller so the eye reads it as derived output rather than authored input. */ samp { font-family: var(--mono); font-size: 0.92em; color: var(--fg); overflow-wrap: anywhere; } /* `` marks publication titles in editorial register. browser- default italic preserved — in serif body prose this reads as published-work convention; in mono surfaces the slant is barely perceptible. */ cite { font-style: italic; color: inherit; } /* editorial focus-visible system. calm oxblood ring at 45 % alpha, 2 px width, 3 px offset. component overrides land in @layer components below (text links → typographic underline-on-focus instead of a box; buttons / nav controls / ctas → token-based outline tightened to the same width and offset). :focus:not(:focus-visible) is stripped so a touch tap on ios safari doesn't leave a persistent outline behind on the tapped element. keyboard users still get :focus-visible. the prefers-contrast: more block elsewhere in this file overrides these to 3 px solid var(--ac) for high-contrast at users. */ :focus-visible { outline: var(--focus-ring-width) solid var(--focus-colour); outline-offset: var(--focus-offset); border-radius: var(--focus-radius); } :focus:not(:focus-visible) { outline: none; } @media (pointer: coarse) { :focus:not(:focus-visible) { outline: none; } } /* inline text links (body copy, hero body, trust-mark, contact secondary, footer privacy, page-back) prefer a typographic underline-on-focus over a box outline. the underline reads as continuous emphasis with the typeface; an outline would look like a form control. component selectors enumerated explicitly so the typographic treatment doesn't leak into buttons or icon controls that need a visible ring. */ .page-body a:focus-visible, .hero-body a:focus-visible, .trust-mark a:focus-visible, .contact-secondary a:focus-visible, .site-footer__actions a:focus-visible, .page-back:focus-visible { outline: none; text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 0.18em; text-decoration-color: var(--focus-colour); } body { font-family: var(--sans); background-color: var(--bg); /* paper · two fixed background layers sit above the flat hex: a soft corner vignette so the page feels held in two hands, and a tiny inline svg fractal-noise giving the surface tooth at ~4.5% alpha. both are background-attachment:fixed so they do not scroll. the vignette uses var(--ink) so dark mode inverts it automatically; the noise reads near-identically on cream and on near-black. data-url inside a stylesheet bypasses script-src. */ background-image: radial-gradient(ellipse 120% 90% at 50% 50%, transparent 55%, color-mix(in srgb, var(--ink) 4%, transparent) 100%), url("data:image/svg+xml;utf8,"); background-attachment: fixed, fixed; background-size: auto, 260px 260px; background-repeat: no-repeat, repeat; color: var(--fg); line-height: 1.6; font-synthesis: none; } } @layer overrides { /* Print-only elements never render on screen. print.css restores them inside @media print. defined here so the rule applies even though print.css is loaded with media="print" and never reaches screen. no !important: cascade-layer order makes this rule sit in the `overrides` layer, which beats every other named screen layer for normal-importance declarations. removing !important is critical so that print.src.css's @layer print-overrides {!important} rules can override these in print mode — !important across cascade layers reverses precedence (earlier layer wins), so a screen-layer !important here would beat the print-layer !important and the homepage would print as a blank page. */ .print-only, .print-profile, .print-trust-sheet, .print-utility-sheet, .print-header, .print-footer, .print-citation, .print-meta, .print-kicker, .print-title, .print-seal { display: none; } } @layer components { /* /verify/ public verification route. verify.js renders into #verify-root using only textcontent and setattribute on safe attributes. Same-origin only. */ /* phase 56 · .verify-kicker rule retired (the html element was removed in phase 55; the rule was an orphan). */ /* /verify/ scoped layout verify.js renders real
children inside #verify-root, which would otherwise inherit the global `section { padding: clamp(80px, 12vh, 160px) 0 }` rule from the homepage hero rhythm. the scoped reset below cancels that inheritance and re-establishes a tighter editorial rhythm specific to the verification page. */ .verify-page section { padding: 0; border-bottom: 0; min-height: 0; } /* editorial column. wider than prose pages so the page record can carry presence, but narrow enough to keep line length readable. */ .verify-page .page-body { max-width: 880px; margin: 0 auto; /* top padding tightened from clamp(48,7vw,88) so the kicker sits closer to the masthead , /verify/ is a utility-record page, not a hero-driven landing. */ padding: clamp(40px, 6vw, 64px) 0; } @media (max-width: 700px) { .verify-page .page-body { max-width: none; padding: 32px 0 48px; } /* card actions stack vertically on narrow viewports — flex column keeps the hairline-underline rhythm of each link intact. */ .verify-page .verify-record-actions { flex-direction: column; gap: 0.5rem 0; line-height: 1.4; } .verify-page .verify-record-action { align-self: flex-start; } /* evidence line splits "validated YYYY-MM-DD" onto its own line so the row reads as three calm lines (source / file·size / validated) rather than wrapping mid-row. desktop keeps the inline " · " join. */ .verify-page .verify-rg-evidence-sep { display: none; } .verify-page .verify-rg-evidence-validated { display: block; } } /* hero. compressed so the record card appears sooner. title uses the utility-page scale (clamp 42 to 76px), the intro paragraph sits close beneath, and the gap to the card is small. */ .verify-page .page-title { max-width: 720px; font-size: clamp(42px, 7vw, 76px); line-height: 0.95; margin-bottom: clamp(20px, 3vw, 28px); } .verify-page .page-lede { max-width: 620px; margin-bottom: 0; } /* ───────────────────────────────────────────────────────────── phase 33 · verify-page micro-grid record system ───────────────────────────────────────────────────────────── the page record is a signed technical certificate. the layout is a definition-list grid where each row pairs a small mono uppercase label with a readable value. inspired by swiss archival records, museum object labels, financial terminals and printed release ledgers. the current-edition panel below reuses the same grid via the `--quiet` modifier. */ :is(.verify-page, .sw-reset-page, .security-page, .source-page) .verify-card { margin-block-start: clamp(2rem, 5vw, 3.25rem); margin-block-end: clamp(2rem, 6vw, 4rem); /* phase 50 · padding compressed to match the integrity record card register (clamp 1.2-1.9rem). archival rhythm unifies. phase 52 · background promoted to the canonical archival surface so verify + integrity cards read as one paper sheet family. border colour promoted to the canonical --rule token so the divider register matches across pages. */ width: 100%; max-width: 100%; min-width: 0; padding: clamp(1.2rem, 4vw, 1.9rem); background: var(--surface-archival); border: 1px solid var(--rule); /* phase 39 · 2px outer radius matches the project-card register so verify, integrity and sw-reset records read as the same family of mounted archival sheets. .record-grid__row stays sharp — only the outer container is softened. phase 46 · literal 8px → var(--radius-soft) (7px). */ border-radius: var(--radius-soft); /* paper-raise via a quiet light-mode shadow only; dark mode drops the shadow so it doesn't muddy the page. */ box-shadow: 0 18px 40px rgb(0 0 0 / 0.06); } @media (prefers-color-scheme: dark) { :is(.verify-page, .sw-reset-page, .security-page, .source-page) .verify-card { /* phase 52 · dark-mode card surface promoted to canonical archival surface for verify / integrity parity. */ background: var(--surface-archival); box-shadow: none; } } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .verify-card, :is(.verify-page, .sw-reset-page, .security-page, .source-page) .verify-card__header, .verify-intro-panel, .verify-intro-list, .integrity-record-dl, .integrity-rg, .integrity-record-actions { min-width: 0; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .verify-card__header { margin-block-end: clamp(1.25rem, 4vw, 2rem); } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .verify-card__header .eyebrow { margin: 0; font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--fg3); } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .verify-card__header h2 { /* the user warned the theatrical 9vw spec; tightened to a clamp that still feels composed at desktop without bullying the rest of the page. */ margin: 0.45rem 0 0.4rem; font-family: var(--serif); font-size: clamp(1.75rem, 6.5vw, 3.25rem); font-weight: 300; line-height: 0.98; letter-spacing: -0.012em; color: var(--fg); } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .verify-status { margin: 0; font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.08em; color: var(--fg3); } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid { margin: 0; display: grid; /* phase 52 · rule colour unified across verify / integrity. */ border-top: 1px solid var(--rule); } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid__row { display: grid; grid-template-columns: minmax(7.25rem, 0.34fr) minmax(0, 1fr); gap: clamp(1rem, 4vw, 2rem); /* phase 51 · row padding tightened 0.85 → 0.8rem. phase 52 · rule colour unified to var(--rule). */ padding-block: 0.8rem; border-bottom: 1px solid var(--rule); } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid dt { font-family: var(--mono); font-size: 0.64rem; line-height: 1.2; letter-spacing: 0.13em; text-transform: uppercase; color: var(--fg3); /* recede comes from --fg3 + 0.64rem + tracking; no further opacity reduction — at this size 0.82 would push the blend below aa (4.5:1) in both modes. */ margin: 0; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid dd { margin: 0; min-width: 0; font-size: clamp(0.9rem, 2.4vw, 1rem); /* phase 51/57 · ledger line-height progressively tightened: 1.5 → 1.35 → 1.28 so the mono metadata rows match the integrity .integrity-rg-value register across both cards. */ line-height: 1.28; color: var(--fg); overflow-wrap: anywhere; word-break: break-word; } /* phase 51 · long mono outputs (paths, hashes) wrap cleanly on narrow viewports so the value column never overflows the card. .record-grid code already had this; samp inherits the same rule. */ :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid samp { font-family: var(--mono); overflow-wrap: anywhere; word-break: break-word; } /* phase 51 · the verify page's fingerprint as compact ledger hex. smaller than .integrity-fingerprint (which has wbr-broken 4-char groups inside the larger integrity record card); this one sits as a single inline-wrapped hash in the verify card's value column. */ :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-fingerprint { display: block; font-family: var(--mono); font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; color: var(--fg); overflow-wrap: anywhere; } .page-hash { overflow-wrap: anywhere; word-break: break-word; font-size: 0.88rem; line-height: 1.45; letter-spacing: 0.01em; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid a { color: inherit; text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--fg) 18%, transparent); transition: color 0.2s, border-bottom-color 0.2s; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid a:hover, :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid a:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid code { font-family: var(--mono); font-size: 0.86em; background: transparent; padding: 0; overflow-wrap: anywhere; word-break: break-word; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-meta { display: block; margin-block-start: 0.3rem; font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.04em; color: var(--fg3); } /* phase 51 · record-actions refactored as a dot-separator inline phrase. phase 56 · class renamed to .record-tools; border-top divider above the rail retired (spacing alone marks the transition from card to actions); register aligned with the integrity inline- action vocabulary (.record-inline-action / .copy-fingerprint / .trust-code-copy) so the verify rail joins one declarative system across the site. button + anchor children now carry the .record-inline-action class and inherit styling from the shared rule. */ :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-tools { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.55rem; /* phase 57 · gap to the card above tightened so the utility rail sits as the immediate footer of the card. phase 58 · further compressed so the rail reads as a tight archival footer rhythm against the last record row above. */ margin-block-start: 0.35rem; font-family: var(--mono); font-size: 0.58rem; line-height: 1.25; letter-spacing: 0.055em; color: var(--fg3); } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-tools > * { display: inline-flex; align-items: baseline; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-tools .record-inline-action + .record-inline-action::before { content: "·"; margin-right: 0.55rem; opacity: 0.45; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .edition-panel { /* phase 56 · further ~25% reduction on both block margins so current edition reads as a tight extension of the page record, and the page closes immediately afterwards. */ margin-block-start: clamp(0.9rem, 3vw, 1.9rem); margin-block-end: clamp(0.85rem, 2.3vw, 1.4rem); } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .edition-panel h2 { /* phase 52 · h2 scale reduced so current edition no longer reads as section-heavy. it supports the page record above, doesn't compete with it. */ margin: 0 0 0.85rem; font-family: var(--serif); font-size: clamp(1.1rem, 3.5vw, 1.4rem); font-weight: 300; line-height: 1.1; letter-spacing: -0.008em; color: var(--fg); } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid--quiet { color: var(--fg2); /* phase 52 · rule colour unified to var(--rule). */ border-top-color: var(--rule); } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid--quiet .record-grid__row { /* phase 53 · per-row hairlines retired in the --quiet variant. phase 55 · closing :last-child hairline also retired so the current-edition panel ends without a floating divider between itself and the back link. spacing alone carries the close. */ padding-block: 0.85rem; border-bottom: 0; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid--quiet .record-grid__row--fingerprint { padding-block-start: 1.05rem; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid--quiet dt { font-size: 0.6rem; /* recede comes from --fg3 + 0.6rem + tracking; opacity reduction would drop the blend below aa at this size. */ } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid--quiet .record-grid__row--fingerprint dt { display: flex; flex-direction: column; align-items: flex-start; gap: 0.45rem; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid--quiet dd { font-size: clamp(0.78rem, 2vw, 0.88rem); } /* mobile stacking · labels above values when the row gets tight. */ @media (max-width: 620px) { :is(.verify-page, .sw-reset-page, .security-page, .source-page) .verify-card { padding: clamp(1.25rem, 6vw, 1.8rem); } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid__row { grid-template-columns: 1fr; gap: 0.35rem; padding-block: 0.9rem; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid dt { font-size: 0.62rem; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid dd { font-size: 0.95rem; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid--quiet .record-grid__row { padding-block: 0.7rem; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid--quiet .record-grid__row--fingerprint { padding-block-start: 0.95rem; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid--quiet dt { font-size: 0.58rem; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid--quiet dd { font-size: 0.84rem; } :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-tools { display: grid; gap: 0.4rem; } /* phase 58 · when the utility rail stacks vertically on mobile, the dot pseudo before each non-first item would render as a leading bullet and read as an accidental list. hide it. */ :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-tools .record-inline-action + .record-inline-action::before { content: none; } } /* ───────────────────────────────────────────────────────────── verify · other-records + current-edition section structure ───────────────────────────────────────────────────────────── two distinct sections sit below the page-record card: the "other records" sibling-route index and the "current edition" panel. spacing alone separates them — no bridging rule, no shared border. releases must read as the close of other records, never as the lead of current edition. */ .verify-page .other-records { /* tightened block-end · a visible
divider now carries the section break; spacing only completes the rhythm. */ margin: clamp(2rem, 5vw, 3rem) 0 clamp(1.75rem, 6vw, 2.75rem); } /* shared peer-heading register · other records and current edition sit as siblings at the same hierarchy level, divided by the visible
section-divider below. */ .verify-page .other-records-title, .verify-page .current-edition .current-edition-title { margin: 0 0 1.25rem; font-family: var(--serif); font-size: clamp(1.55rem, 5vw, 2rem); font-weight: 400; line-height: 1.12; letter-spacing: -0.015em; color: var(--fg); text-transform: none; } /* peer-section divider · soft hairline between other records and current edition. matched to --rule, with generous bottom margin so the divider clearly closes the section above and opens the one below rather than floating between them. */ .verify-page .section-divider--after-records { margin: 0 0 clamp(2rem, 7vw, 3.25rem); border: 0; border-top: 1px solid var(--rule); height: 0; } .verify-page .other-records-grid { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(2.5rem, 12vw, 5rem); row-gap: 1rem; } .verify-page .other-records-grid li { display: block; } .verify-page .other-records-grid a { display: inline-block; width: fit-content; color: var(--fg2); font-family: var(--mono); font-size: 0.86rem; line-height: 1.35; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.2em; transition: color 0.2s, text-decoration-color 0.2s; } .verify-page .other-records-grid a:hover, .verify-page .other-records-grid a:focus-visible { color: var(--accent-text); outline: 0; } .verify-page .other-records-grid [aria-current="page"] { display: inline-block; width: fit-content; color: var(--fg3); text-decoration: none; cursor: default; font-family: var(--mono); font-size: 0.86rem; line-height: 1.35; } /* current-edition · sits as a fresh section, not as a continuation of the other-records list. no top margin — the block-end margin on .other-records carries the separation. the inner record-grid drops its top hairline so the h2 and the rows read as one editorial block. */ .verify-page .edition-panel.current-edition { margin-top: 0; padding-top: 0; margin-block-end: clamp(0.85rem, 2.3vw, 1.4rem); } .verify-page .current-edition .record-grid { border-top: 0; } /* override the shared .record-card .fingerprint-section border so edition and signing key read as one continuous record block — the visible section break belongs above the h2, not between data rows. natural row padding from .record-grid--quiet keeps the breathing without painting a line. */ .verify-page .current-edition .fingerprint-section { margin-top: 0; padding-top: 0; border-top: 0; } @media (max-width: 700px) { .verify-page .other-records { margin-block-end: clamp(2.5rem, 9vw, 4rem); } .verify-page .other-records-grid { column-gap: clamp(1.5rem, 10vw, 3rem); } } /* ───────────────────────────────────────────────────────────── legacy verify-page rules (kept for backwards compat with archived/frozen content under /integrity/releases// that may still reference the .verify-thispage / .verify-rg class system). the new active /verify/ dom uses .verify-card + .record-grid above. ───────────────────────────────────────────────────────────── */ /* /verify/ reads as three composed objects: hero → record card → chooser → back. the card carries the visual weight; the chooser sits outside it as quiet secondary nav. related records is gone , the card already shows source mirror and release archive, and the chooser links to integrity and releases. */ .verify-page .verify-record-card { background: var(--surface-card); border: 1px solid var(--rule); /* shared card padding scale (matches /integrity/ signed release card and /security/ architecture card so the trust system uses one paper-card register). */ padding: clamp(28px, 4.5vw, 48px); /* phase 21: card is now the hero object below the lede. tighter top margin so the lede flows directly into the card; bottom margin opens the gap to the supporting infrastructure (current edition) panel that now sits below. */ margin: clamp(16px, 2.2vw, 24px) 0 clamp(32px, 5vw, 56px); max-width: 860px; /* subtle paper-raise , restrained shadow, not modern card. */ box-shadow: 0 18px 44px rgba(0, 0, 0, 0.035); } .verify-page .verify-record-card .verify-thispage { margin: 0; } /* ── editorial card internals ── the record reads as a publication colophon, not a metadata table. page record kicker (mono, restrained) → page title (serif, large) → short status line → grouped editorial rows (citation / location / evidence / fingerprint / archive) → mono-utility actions footer. hairlines separate groups, never rows. */ .verify-page .verify-record-kicker { font-family: var(--mono); font-size: 0.7rem; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg3); margin: 0 0 0.55rem; } .verify-page .verify-record-title { font-family: var(--serif); font-size: clamp(28px, 4.4vw, 38px); font-weight: 300; letter-spacing: -0.012em; line-height: 1.12; color: var(--fg); margin: 0 0 0.35rem; } .verify-page .verify-record-status { font-family: var(--mono); font-size: 0.74rem; letter-spacing: 0.025em; color: var(--fg3); margin: 0 0 clamp(24px, 4vw, 32px); } .verify-page .verify-rg { /* default groups have no top hairline; rules are added selectively via .verify-rg--ruled at boundaries the editorial rhythm wants (status→citation, evidence→fingerprint, fingerprint→archive). */ padding: 0.85rem 0 0.6rem; } .verify-page .verify-rg--ruled { border-top: 1px solid var(--bd); padding-top: 1rem; } .verify-page .verify-rg-label { font-family: var(--mono); font-size: 0.68rem; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg3); margin: 0 0 0.4rem; } .verify-page .verify-rg-value { margin: 0; color: var(--fg); overflow-wrap: anywhere; word-break: break-word; } .verify-page .verify-rg-value--mono { font-family: var(--mono); font-size: 0.82rem; line-height: 1.55; } .verify-page .verify-fingerprint { /* Single-line visual abbreviation; full value lives on the title/aria-label and is what the copy action returns. */ white-space: nowrap; overflow-wrap: normal; word-break: normal; } .verify-page .verify-rg-value--serif { font-family: var(--serif); font-size: clamp(15px, 1.6vw, 17px); font-weight: 300; line-height: 1.5; } .verify-page .verify-rg-link { color: var(--fg); text-decoration: none; border-bottom: 1px solid var(--bd); transition: color 0.2s, border-bottom-color 0.2s; word-break: break-all; overflow-wrap: anywhere; } .verify-page .verify-rg-link:hover, .verify-page .verify-rg-link:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); outline: 0; } .verify-page .verify-rg-value--serif .verify-rg-link { border-bottom-width: 1px; } .verify-page .verify-record-actions { border-top: 1px solid var(--rule); padding-top: 0.85rem; margin: 1rem 0 0; font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.025em; color: var(--fg2); line-height: 1.6; /* flex + gap, no inline " · " separators (verify.js stopped emitting them). wrapped actions never orphan a bullet. */ display: flex; flex-wrap: wrap; gap: 0.4rem 1.4rem; } .verify-page .verify-record-action { appearance: none; font: inherit; letter-spacing: inherit; color: var(--fg2); background: transparent; border: 0; border-bottom: 1px solid var(--bd); padding: 0; cursor: pointer; text-decoration: none; text-transform: none; transition: color 0.2s, border-bottom-color 0.2s; } .verify-page .verify-record-action:hover, .verify-page .verify-record-action:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); outline: 0; } .verify-page .verify-record-action[data-state="copied"] { color: var(--accent-text); border-bottom-color: var(--accent-text); } /* legacy separator class kept hidden so cached html never paints a stray bullet; new verify.js doesn't emit them. */ .verify-page .verify-record-actions-sep { display: none; } /* /verify/ "other records" uses the shared .related-nav grammar above; only the unknown-route fallback needs its own bottom margin so it doesn't collide with back. */ .verify-page .verify-unknown { margin: 0 0 clamp(36px, 5vw, 64px); } /* ───────────────────────────────────────────────────────────── .trust-disclosure — shared collapsible component ───────────────────────────────────────────────────────────── one quiet disclosure grammar across /verify/ and /integrity/: small mono summary, single right-aligned +/− indicator, hairline top rule, no native browser triangle, no duplicated marker. Variants: .trust-disclosure--compact /verify/ "choose another record" .trust-disclosure--technical /integrity/ "verify release locally" */ .trust-disclosure { /* silent utility row — no top hairline (was a section divider). quiet vertical margin lets the disclosure sit as a single line beneath the card, not as a separate page section. */ margin: clamp(14px, 2.2vw, 20px) 0 clamp(14px, 2.2vw, 20px); padding-top: 0; } /* when a disclosure follows the integrity record-card, the paper card above already provides the visual anchor; tighten further so the "verify release locally" line reads as a small footer of the card. phase 57 · gap halved so the accordion reads as an extension of the release record card, not a new section beneath it. */ .integrity-record-card + .trust-disclosure { margin-top: clamp(3px, 0.8vw, 8px); } .trust-disclosure summary { list-style: none; display: flex; align-items: center; justify-content: space-between; gap: 1rem; cursor: pointer; font-family: var(--mono); font-size: 0.7rem; line-height: 1.35; letter-spacing: 0.025em; color: var(--fg2); transition: color 0.2s; } .trust-disclosure summary::-webkit-details-marker { display: none; } .trust-disclosure summary::marker { content: none; } .trust-disclosure summary::after { /* fine editorial chevron — rotates 90deg when open. replaces the earlier +/− marker so the disclosure reads as archival, not as a generic accordion control. */ content: '›'; display: inline-block; flex: 0 0 auto; font-family: var(--mono); font-size: 1em; line-height: 1; color: var(--fg3); transform: translateY(-0.06em); transition: transform 0.22s ease, color 0.15s; } .trust-disclosure[open] > summary::after { transform: rotate(90deg) translateX(-0.06em); } .trust-disclosure summary:hover, .trust-disclosure summary:focus-visible { color: var(--accent-text); outline: 0; } .trust-disclosure summary:hover::after, .trust-disclosure summary:focus-visible::after { color: var(--accent-text); } .trust-disclosure__label { display: inline; } /* phase 50 · summary → body gap tightened ~30% so the disclosure reads as one continuous technical appendix rather than two stacked blocks. */ .trust-disclosure__body { margin-top: clamp(10px, 1.5vw, 14px); } /* technical — short note + command block + copy button. */ .trust-disclosure--technical .trust-disclosure__body > p { font-family: var(--mono); font-size: 0.74rem; color: var(--fg2); margin: 0 0 1rem; line-height: 1.55; } .trust-code-block { /* a little breathing room above the command block so it reads as an archival printout, not a stacked terminal panel. */ margin: 0; padding-top: 0.15rem; } .trust-code-block-header { display: flex; justify-content: flex-end; margin-bottom: 0.45rem; } /* phase 55 · standalone .trust-code-copy rules retired. all inline-action styling now lives in the combined .record-inline- action / .copy-fingerprint / .trust-code-copy rule further down, keeping the integrity command-block copy and the fingerprint copy in lockstep. */ .trust-code { /* phase 50 · command block aligned with the card-object family: 7px outer radius (var(--radius-soft)). background promoted to --surface-card (which now resolves to --surface-archival in phase 52). border at canonical --rule. phase 52 · padding compressed ~8% so the command slip reads as a terminal printout rather than a developer card. */ margin: 0; padding: clamp(0.9rem, 2.75vw, 1.25rem); background: var(--surface-card); border: 1px solid var(--rule); border-radius: var(--radius-soft); font-family: var(--mono); font-size: 0.74rem; line-height: 1.6; color: var(--fg); /* display wraps long urls cleanly; copy returns the original. */ white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; max-width: 100%; } .trust-code .code-cmd { color: var(--fg); } .trust-code .code-var { color: var(--fg2); } .trust-code .code-str { color: var(--accent-text); } @media (max-width: 700px) { .trust-code { font-size: 0.66rem; padding: 0.85rem 0.95rem; line-height: 1.55; } } /* the unscoped .verify-thispage / .verify-related margin rules below no longer apply , related records is removed and .verify-thispage's spacing is owned by the card. */ /* chooser strip , same calm mono utility-link family as .verify-related-strip and .verify-thispage-actions. the current record reads as a muted span (no underline, no hover state) so the row never points back at itself awkwardly. */ .verify-chooser-strip { font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.025em; color: var(--fg2); margin: 0; line-height: 1.6; } .verify-chooser-link { color: var(--fg2); text-decoration: none; border-bottom: 1px solid var(--bd); transition: color 0.2s, border-bottom-color 0.2s; } .verify-chooser-link:hover, .verify-chooser-link:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); outline: 0; } .verify-chooser-link--current { color: var(--fg3); border-bottom: 0; cursor: default; } .verify-chooser-sep { color: var(--fg3); margin: 0 0.4em; } /* page record , the centrepiece. top + bottom hairlines from the dl already render the frame; widen the grid so labels and values have room without becoming a card. */ .verify-page .verify-thispage-list { padding: 26px 0 24px; } @media (min-width: 720px) { .verify-page .verify-thispage-row { grid-template-columns: 190px minmax(0, 1fr); column-gap: 48px; row-gap: 16px; } } .verify-page .verify-thispage-label { font-family: var(--mono); font-size: 11px; letter-spacing: 0.04em; color: var(--fg3); text-transform: uppercase; } .verify-page .verify-thispage-value { font-size: 15px; line-height: 1.4; } .verify-page .verify-thispage-value--mono { font-size: 13px; } /* connected records , closing nav line, separated by a single hairline so it reads as the editorial endpoint, not as a stranded paragraph. */ .verify-page .verify-related { border-top: 1px solid var(--bd); padding-top: 28px; } /* back link , placed, not abandoned. */ /* phase 52 · back link pulls another 15% closer to the closing content so the page closes tight rather than floating. */ .verify-page .page-back { margin-top: clamp(16px, 2.5vw, 24px); } /* Unknown-route fallback , small actions strip mirrors the page-record actions strip so the fallback never reads as broken. */ .verify-page .verify-unknown-actions { font-family: var(--mono); font-size: 0.78rem; letter-spacing: 0.025em; color: var(--fg); margin: 0.7rem 0 0; line-height: 1.5; } .verify-page .verify-unknown-action { color: var(--fg); text-decoration: none; border-bottom: 1px solid var(--bd); transition: color 0.2s, border-bottom-color 0.2s; } .verify-page .verify-unknown-action:hover, .verify-page .verify-unknown-action:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); outline: 0; } .verify-page .verify-unknown-actions-sep { color: var(--fg3); margin: 0 0.4em; } /* legacy reset the .verify-page section rule above is the load-bearing fix. these selectors are retained for any future contributor who reintroduces the older div-based render. */ #verify-root, #verify-root > div, .verify-command { padding: 0; border: 0; } .verify-root { margin-top: 1rem; } /* ───────────────────────────────────────────────────────────── /integrity/ — the signed-release hub ───────────────────────────────────────────────────────────── same editorial dna as the /verify/ page record card: warm paper background, large serif headline, mono labels, hairline rules, oxblood only as accent on hover. the card is the central object; the command block is collapsed by default below it; history and related sit as quiet closing utilities. */ .integrity-record-card { background: var(--surface-card); border: 1px solid var(--rule); /* phase 39 · outer radius matches .verify-card and .project- card. internal kickers, dl rows, action rows stay sharp. phase 46 · literal 8px → var(--radius-soft) (7px). */ border-radius: var(--radius-soft); /* phase 47 · card padding tightened ~25% so the record reads as a compact archival ledger rather than a spaced-out ui panel. phase 50 · further compression to compact archival register. */ width: 100%; max-width: min(100%, 720px); min-width: 0; padding: clamp(1.2rem, 4vw, 1.9rem); margin: clamp(24px, 3.5vw, 36px) 0 clamp(18px, 2.5vw, 26px); box-shadow: 0 18px 44px rgba(0, 0, 0, 0.035); } .integrity-record-kicker { font-family: var(--mono); font-size: 0.7rem; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg3); margin: 0 0 0.55rem; } .integrity-record-title { font-family: var(--serif); font-size: clamp(28px, 4.4vw, 38px); font-weight: 300; letter-spacing: -0.012em; line-height: 1.12; color: var(--fg); margin: 0 0 0.35rem; } .integrity-record-status { font-family: var(--mono); font-size: 0.74rem; letter-spacing: 0.025em; color: var(--fg3); margin: 0 0 clamp(20px, 3vw, 28px); } .integrity-record-dl { margin: 0; padding: 0; } /* phase 47 · record-row rhythm tightened so the rows read as one ledger system, not a spaced list. Phase-45 inter-row 2rem margin retired; the symmetric padding-block + ruled border now carries the inter-row separation. fingerprint row gets a touch more breathing room via .integrity-rg--fingerprint below. */ .integrity-rg { /* phase 50 · row padding tightened 0.85 → 0.72rem so the ledger reads as a denser archival sheet. fingerprint row keeps its touch more breathing via .integrity-rg--fingerprint below. */ padding-block: 0.72rem; } .integrity-rg--ruled { border-top: 1px solid var(--bd); } .integrity-rg--fingerprint { padding-block: 1.05rem 0.95rem; } .integrity-rg--fingerprint .integrity-rg-label { display: block; } /* span exists only so the copy button can sit as a non-uppercased sibling on the same flex row — inherits label register. */ .integrity-rg-label-text { display: inline-block; } .integrity-rg-label { font-family: var(--mono); /* phase 45 · slightly larger + wider tracking + quieter colour so the labels read as quiet archival headings, with the value below carrying the weight. phase 47 · bottom margin tightened from 0.45 → 0.35. phase 50 · further tightened 0.35 → 0.2. phase 54 · alpha moved from opacity to color-mix. phase 59 · letter-spacing 0.14 → 0.12em so the row labels match the dominant label register (kicker, history-heading pre-section-kicker, etc.). one tracking value across the small-caps system. phase 60 · color-mix 78% → plain --fg3. at 0.72rem the mix blended below aa contrast in light mode. recede comes from size + uppercase + tracking + --fg3 alone. */ font-size: 0.72rem; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg3); margin: 0 0 0.2rem; } .integrity-rg-value { font-family: var(--mono); font-size: 0.82rem; /* phase 47/57 · ledger line-height tightened progressively: 1.55 → 1.35 → 1.28 so metadata reads as a dense archival ledger. */ line-height: 1.28; color: var(--fg); margin: 0; overflow-wrap: anywhere; word-break: break-word; } .integrity-rg-desc { font-family: var(--mono); font-size: 0.7rem; color: var(--fg2); /* phase 47 · note sits closer to the value above + tighter internal leading. */ margin: 0.18rem 0 0; line-height: 1.25; } .integrity-rg-link { color: var(--fg); text-decoration: none; border-bottom: 1px solid var(--bd); transition: color 0.2s, border-bottom-color 0.2s; word-break: break-all; } .integrity-rg-link:hover, .integrity-rg-link:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); outline: 0; } .integrity-rg-sep { display: inline-block; margin: 0 0.4em; color: var(--fg3); } /* Cross-row alignment via subgrid at desktop breakpoints. below 720px the label/value/description stack as before. at ≥720px with subgrid support, the label sits left and the value+description stack on the right, with column edges aligned across every record. browsers without subgrid keep the stacked layout — no visual regression. */ @media (min-width: 720px) { @supports (grid-template-columns: subgrid) { .integrity-record-dl { display: grid; grid-template-columns: 200px minmax(0, 1fr); column-gap: 1.4rem; } .integrity-rg { display: grid; grid-template-columns: subgrid; grid-column: 1 / -1; align-items: baseline; } .integrity-rg-label { grid-column: 1; align-self: baseline; margin-bottom: 0; } .integrity-rg-value, .integrity-rg-desc { grid-column: 2; } } } .fingerprint-grid { display: grid; grid-template-columns: repeat(3, max-content); justify-content: start; align-items: baseline; column-gap: clamp(1.1rem, 5vw, 2rem); row-gap: 0.55rem; width: max-content; max-width: 100%; overflow-x: auto; padding-block: 0.25rem; font-family: var(--mono); font-size: clamp(1rem, 4.1vw, 1.15rem); line-height: 1.35; letter-spacing: 0.01em; color: var(--fg); font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; font-variant-ligatures: none; background: transparent; } .fingerprint-grid span { white-space: nowrap; } @media (max-width: 360px) { .fingerprint-grid { grid-template-columns: repeat(2, max-content); } } .record-action-row { margin-block: 0.25rem 0.75rem; } .text-action { font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.02em; text-decoration: underline; text-underline-offset: 0.18em; } .release-card .fingerprint-section, .record-card .fingerprint-section { margin-top: 1.35rem; padding-top: 1rem; border-top: 1px solid var(--rule); } .integrity-record-actions { border-top: 1px solid var(--rule); /* phase 47 · tighter rhythm between the last record row, the hairline, and the actions strip below. phase 57 · wrapper register aligned with .record-inline-action so the separator spans + child anchors all read at one typographic level (0.58rem / 0.055em). phase 59 · vertical rhythm tightened so the strip sits as a close archival footer to the data rows above, matching the verify utility rail's compact register (phase 58). */ padding-top: 0.55rem; padding-bottom: 0.14rem; margin: 0.55rem 0 0; font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.055em; color: var(--fg3); line-height: 1.4; /* flex + gap, no inline " · " separators — a wrapped action can never orphan a bullet at the start of a new line. */ display: flex; flex-wrap: wrap; gap: 0.4rem 1.4rem; } /* shared utility-action register — copy citation, copy fingerprint, open mirror, view manifest, view releases all read as one family. */ .integrity-record-action { appearance: none; font: inherit; letter-spacing: inherit; color: var(--fg2); background: transparent; border: 0; border-bottom: 1px solid var(--bd); padding: 0; cursor: pointer; text-decoration: none; text-transform: none; transition: color 0.2s, border-bottom-color 0.2s; } .integrity-record-action:hover, .integrity-record-action:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); outline: 0; } .integrity-record-action[data-state="copied"] { color: var(--accent-text); border-bottom-color: var(--accent-text); } /* legacy separator class kept for any cached html that still emits it; a hidden display rule means a stray never paints a bullet. new html omits the separators entirely. */ .integrity-record-actions-sep { display: none; } @media (max-width: 700px) { .integrity-record-actions { flex-direction: column; gap: 0.5rem 0; line-height: 1.4; } .integrity-record-action { align-self: flex-start; } } /* compact inline strip variant — download zip · TAR.GZ · view manifest · view releases. Single-line on desktop with explicit middots between items (so a wrap shows the structure clearly); on mobile, keeps the inline flow rather than stacking, so the row reads as one calm strip even at 320px. restrained underline weight (uses --rule, not --bd) keeps the row quieter than the data-bearing dl above it. no top rule on the strip itself — .integrity-record-copy above it carries the single rule that separates fingerprint hex from the grouped action area. */ .integrity-record-actions.integrity-record-actions--strip { display: block; margin: 0.25rem 0 0; font-size: 0.66rem; letter-spacing: 0.02em; line-height: 1.7; } .integrity-record-actions--strip .integrity-record-action { color: var(--fg3); border-bottom-color: var(--rule); } .integrity-record-actions--strip .integrity-record-action:hover, .integrity-record-actions--strip .integrity-record-action:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); } .integrity-record-actions--strip .integrity-record-actions-sep { display: inline; margin: 0 0.45em; color: var(--fg3); border: 0; } /* two grouped lines inside the strip — download zip · TAR.GZ on one line, view manifest · view releases on another. the grouping by purpose (downloads vs nav) gives the strip an intentional rhythm instead of a single long string of accidental footnotes. */ .integrity-record-actions--strip .integrity-record-actions-line { display: block; } .integrity-record-actions--strip .integrity-record-actions-line + .integrity-record-actions-line { margin-top: 0.2rem; } @media (max-width: 700px) { .integrity-record-actions.integrity-record-actions--strip { display: block; /* override the legacy column rule above */ } .integrity-record-actions--strip .integrity-record-action { align-self: auto; /* override the legacy flex-start rule */ } } /* phase 49 · the standalone .integrity-record-copy strip was retired. the copy action now sits inline on the fingerprint row's
header (see .integrity-rg--fingerprint .integrity-rg-label above and the .copy-fingerprint rule further down). */ /* /integrity/ Verify-release-locally disclosure: chrome lives in the shared .trust-disclosure rules above. no /integrity/-specific summary styling needed — the page just adds the technical variant. */ /* Page-level note linking out to /verify/. a quiet bridge — sits close to the disclosure above so it reads as a footnote to local verification, not a fresh body paragraph. */ .integrity-page-level-note { font-family: var(--mono); font-size: 0.64rem; letter-spacing: 0.03em; color: var(--fg3); margin: clamp(6px, 1vw, 10px) 0 clamp(18px, 2.6vw, 24px); line-height: 1.55; } .integrity-page-level-note a { color: var(--fg); border-bottom: 1px solid var(--bd); text-decoration: none; transition: color 0.2s, border-bottom-color 0.2s; } .integrity-page-level-note a:hover, .integrity-page-level-note a:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); outline: 0; } /* history — small section heading + body + link, no hairlines. tighter top margin so the page reads as a continuous editorial thread, not three separate modules stacked. mobile reduces ~20% so the long card+disclosure+history stack feels finished, not bottom-heavy. */ .integrity-history { /* phase 57 · top margin further compressed ~15% so the history continuation sits as a soft transition, not a section break. */ margin: clamp(0.9rem, 2.5vw, 1.7rem) 0 clamp(10px, 1.6vw, 16px); } .integrity-history-heading { /* phase 56 · history label drops to the section-kicker register — smaller, wider tracking so the label reads as an editorial section marker rather than a competing heading. opacity reduction retired — at 0.58rem on this token the blend would fall below aa contrast (lighthouse remediation). recede now comes from size + tracking + --fg3 alone. */ font-family: var(--mono); font-size: 0.58rem; font-weight: 500; letter-spacing: 0.16em; text-transform: uppercase; color: var(--fg3); margin: 0 0 0.35rem; } .integrity-history-body { font-family: var(--serif); font-size: clamp(15px, 1.5vw, 17px); font-weight: 300; color: var(--fg); line-height: 1.55; /* phase 50 · symmetric block margin pulls the body closer to heading above and link below — heading, body, link read as one compact archival entry. */ margin: 0 0 0.45rem; } .integrity-history-link { font-family: var(--mono); font-size: 0.78rem; color: var(--fg); text-decoration: none; border-bottom: 1px solid var(--bd); transition: color 0.2s, border-bottom-color 0.2s; } .integrity-history-link:hover, .integrity-history-link:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); outline: 0; } /* ───────────────────────────────────────────────────────────── shared support-navigation pattern — .related-nav ───────────────────────────────────────────────────────────── single grammar for every secondary link strip across the trust pages: small mono label, one quiet hairline top rule, inline links with " · " separators. used by /integrity/ related records and /verify/ related records. reads as a finishing footer — never a content section, never a second module. */ .related-nav, .integrity-related, .related-grid { /* give the closing support nav one clear breath below the primary object so the page ends composed rather than cramped. */ margin: clamp(16px, 2.8vw, 24px) 0 clamp(10px, 1.8vw, 16px); } .related-nav-label, .integrity-related-label { /* Support-nav label register — quieter than the in-card metadata labels (page record, manifest, history) so support nav never competes with the page-record card or the section it follows. title case, gentle tracking, mono very small, fg3. the final shelf of the archive — should read as a footnote, not a section. */ font-family: var(--mono); font-size: 0.6rem; font-weight: 500; letter-spacing: 0.05em; text-transform: none; color: var(--fg3); margin: 0 0 0.35rem; } .related-nav-list, .integrity-related-list, .related-grid-list { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.8rem 1.5rem; font-family: var(--mono); font-size: 0.82rem; color: var(--fg3); line-height: 1.7; } .related-nav-list li, .integrity-related-list li, .related-grid-list li { display: block; } @media (min-width: 760px) { .related-nav-list, .integrity-related-list, .related-grid-list { grid-template-columns: repeat(3, minmax(0, 1fr)); } } .related-nav-list a, .integrity-related-list a, .related-grid-list a { color: var(--fg2); font-family: var(--mono); font-size: 0.82rem; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.22em; transition: color 0.2s, text-decoration-color 0.2s; } .related-nav-list a:hover, .related-nav-list a:focus-visible, .integrity-related-list a:hover, .integrity-related-list a:focus-visible, .related-grid-list a:hover, .related-grid-list a:focus-visible { color: var(--accent-text); outline: 0; } .related-nav-list span[aria-current="page"] { color: var(--fg3); cursor: default; } @media (max-width: 700px) { .related-nav-list, .integrity-related-list, .related-grid-list { font-size: 0.78rem; } } /* ── shared command component (used on /verify/ and /integrity/) ── header strip (title left + copy button right) sits above the
   so the button never overlaps the command. Mobile-safe wrapping. */

.verify-command {
  margin: 0 0 1.6rem;
  max-width: 100%;
}

.verify-command:last-of-type {
  margin-bottom: 0.6rem;
}

.verify-command-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem 1rem;
  flex-wrap: wrap;
  margin: 0 0 0.4rem;
}

.verify-command-title {
  font-family: var(--serif);
  font-size: clamp(14px, 1.5vw, 16px);
  font-weight: 400;
  letter-spacing: 0;
  line-height: 1.3;
  color: var(--fg);
  margin: 0;
  text-transform: none;
}

.verify-command-copy {
  appearance: none;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  color: var(--fg3);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--bd, #D8D4CC);
  border-radius: 0;
  padding: 0.15rem 0;
  cursor: pointer;
  transition: color 0.2s, border-bottom-color 0.2s;
  text-transform: none;
  line-height: 1.3;
}

.verify-command-copy:hover,
.verify-command-copy:focus-visible {
  color: var(--ac, #6E1A14);
  border-bottom-color: var(--ac, #6E1A14);
  outline: 0;
}

.verify-command-copy[data-state="copied"] {
  color: var(--ac, #6E1A14);
  border-bottom-color: var(--ac, #6E1A14);
}

.verify-command-block {
  background: var(--bg2);
  border: 1px solid var(--bd);
  border-radius: 4px;
  padding: 0.85em 1em;
  margin: 0;
  overflow-x: auto;
  max-width: 100%;
}

.verify-command-block code {
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.6;
  color: var(--fg);
  white-space: pre-wrap;
  word-break: break-word;
  overflow-wrap: anywhere;
}

.verify-command-note {
  font-family: var(--serif);
  font-size: clamp(13px, 1.4vw, 15px);
  font-weight: 300;
  line-height: 1.45;
  color: var(--fg2);
  max-width: 64ch;
  margin: 0.6rem 0 1rem;
}

/* general routes mode (no path) + unknown-route fallback. */
.verify-general-body {
  font-family: var(--serif);
  font-size: clamp(15px, 1.6vw, 18px);
  font-weight: 300;
  line-height: 1.55;
  color: var(--fg2);
  max-width: 62ch;
  margin: 0 0 1.5rem;
}

.verify-unknown-path {
  font-family: var(--mono);
  font-size: 0.85rem;
  color: var(--fg2);
  margin: 0 0 0.8rem;
  word-break: break-all;
}

/* trust routes , compact mental-model block
   mirrors the homepage approach-list pattern: mono numerals, signifier
   label, calm serif descriptor. restrained editorial composition; not
   a dashboard, not a sitemap. */
.trust-routes {
  margin: clamp(28px, 4vw, 40px) 0 clamp(20px, 3vw, 28px);
  max-width: 64ch;
}
.trust-routes-heading {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg3);
  margin: 0 0 0.7rem;
}
.trust-routes-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.trust-routes-item {
  display: grid;
  grid-template-columns: 2.4rem minmax(0, 1fr);
  column-gap: 0.9rem;
  padding: 0.65rem 0;
  align-items: baseline;
  border-top: 1px solid var(--bd);
}
.trust-routes-item:last-child {
  border-bottom: 1px solid var(--bd);
}
.trust-routes-num {
  font-family: var(--mono);
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--accent-text);
  line-height: 1.3;
}
.trust-routes-body {
  min-width: 0;
}
.trust-routes-title {
  display: inline-block;
  font-family: var(--serif);
  font-size: clamp(15px, 1.55vw, 17px);
  font-weight: 400;
  color: var(--fg);
  line-height: 1.3;
  text-decoration: none;
  border-bottom: 1px solid var(--bd);
  transition: color 0.2s, border-bottom-color 0.2s;
}
.trust-routes-title:hover,
.trust-routes-title:focus-visible {
  color: var(--accent-text);
  border-bottom-color: var(--accent-text);
  outline: 0;
}
/* Current-page state , non-interactive, no underline, slightly muted
   so the row reads as the visitor's current location, not a self-link. */
.trust-routes-title--current {
  color: var(--fg3);
  border-bottom: 0;
}
.trust-routes-desc {
  display: block;
  margin: 0.18rem 0 0;
  font-family: var(--serif);
  font-size: clamp(13px, 1.4vw, 14.5px);
  font-weight: 300;
  color: var(--fg2);
  line-height: 1.45;
}

/* verify
   Page-level record card for citation, source mirror, fingerprint and
   archived release. hero, white record card, quiet record selector,
   back link. no command blocks; release verification lives on
   /integrity/. */

/* section heading shared across verify-* sub-sections. editorial
   signifier, same family as the privacy/integrity/security subheads
   so /verify/ feels native to that family. */
.verify-section-heading {
  font-family: var(--serif);
  font-size: clamp(18px, 2.2vw, 22px);
  font-weight: 300;
  letter-spacing: -0.012em;
  line-height: 1.2;
  color: var(--fg);
  margin: 0 0 0.5rem;
  text-transform: none;
}
.verify-section-heading--small {
  font-size: clamp(15px, 1.7vw, 17px);
}

/* short serif intro paragraph that sits below a section heading. */
.verify-section-intro {
  font-family: var(--serif);
  font-size: clamp(14px, 1.5vw, 16px);
  font-weight: 300;
  line-height: 1.5;
  color: var(--fg2);
  margin: 0 0 1.1rem;
  max-width: 64ch;
}

/* ── Section: this page ──
   editorial public record. no per-row borders , let typography and
   spacing carry the rhythm. identity rows then evidence rows; combined
   file row; page fingerprint with inline copy button. status strip
   above the list replaces the standalone verification chain section. */
/* .verify-thispage block-rhythm is owned by the scoped `.verify-page
   .verify-thispage` clamp() rule near the top of this file. */

/* inline status strip , three short mono chips, fg3, no badges, no
   icons. sits between the section heading and the metadata list. */
.verify-thispage-status {
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.025em;
  color: var(--fg3);
  margin: -0.1rem 0 0.7rem;
  line-height: 1.4;
}
.verify-thispage-status-sep {
  color: var(--fg3);
  opacity: 0.7;
}
.verify-thispage-status-chip {
  white-space: nowrap;
}

.verify-thispage-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  margin: 0;
  padding: 0.7rem 0;
  border-top: 1px solid var(--bd);
  border-bottom: 1px solid var(--bd);
}
.verify-thispage-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.05rem;
  padding: 0.45rem 0;
}
@media (min-width: 720px) {
  .verify-thispage-row {
    grid-template-columns: 200px 1fr;
    column-gap: 1.4rem;
    padding: 0.5rem 0;
    align-items: baseline;
  }
}
.verify-thispage-label {
  margin: 0;
  font-family: var(--serif);
  font-size: 0.92rem;
  font-weight: 400;
  color: var(--fg3);
  line-height: 1.4;
}
.verify-thispage-value {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(14px, 1.55vw, 16px);
  font-weight: 300;
  color: var(--fg);
  line-height: 1.4;
  min-width: 0;
  word-break: break-word;
  overflow-wrap: anywhere;
}
.verify-thispage-value--mono {
  font-family: var(--mono);
  font-size: 0.85em;
  word-break: break-all;
}
.verify-thispage-link {
  font-family: inherit;
  font-size: inherit;
  color: var(--fg);
  text-decoration: none;
  border-bottom: 1px solid var(--bd);
  word-break: break-all;
  overflow-wrap: anywhere;
  transition: color 0.2s, border-bottom-color 0.2s;
}
.verify-thispage-link:hover,
.verify-thispage-link:focus-visible {
  color: var(--accent-text);
  border-bottom-color: var(--accent-text);
}

/* page fingerprint row , value stacks: hash on its own line, then a
   small copy fingerprint button below. same pattern as the cite-overlay
   hash treatment so it never overlaps. */
.verify-thispage-fingerprint-value {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  align-items: flex-start;
}
.verify-thispage-fingerprint {
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--fg2);
  word-break: break-all;
  overflow-wrap: anywhere;
  background: transparent;
  width: 100%;
}
.verify-thispage-fingerprint-copy {
  appearance: none;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.03em;
  color: var(--fg3);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--bd);
  padding: 0.1rem 0;
  cursor: pointer;
  transition: color 0.2s, border-bottom-color 0.2s;
  text-transform: none;
}
.verify-thispage-fingerprint-copy:hover,
.verify-thispage-fingerprint-copy:focus-visible {
  color: var(--accent-text);
  border-bottom-color: var(--accent-text);
  outline: 0;
}
.verify-thispage-fingerprint-copy[data-state="copied"] {
  color: var(--accent-text);
  border-bottom-color: var(--accent-text);
}

/* compact actions strip beneath the page-record dl. same restrained
   mono utility-link style as .verify-related-strip, so the two strips
   sit as a calm pair at the bottom of /verify/. */
.verify-thispage-actions {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.025em;
  color: var(--fg);
  margin: 0.7rem 0 0;
  line-height: 1.5;
}
.verify-thispage-action {
  appearance: none;
  font: inherit;
  letter-spacing: inherit;
  color: var(--fg);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--bd);
  padding: 0;
  cursor: pointer;
  text-decoration: none;
  text-transform: none;
  transition: color 0.2s, border-bottom-color 0.2s;
}
.verify-thispage-action:hover,
.verify-thispage-action:focus-visible {
  color: var(--accent-text);
  border-bottom-color: var(--accent-text);
  outline: 0;
}
.verify-thispage-action[data-state="copied"] {
  color: var(--accent-text);
  border-bottom-color: var(--accent-text);
}
.verify-thispage-actions-sep {
  color: var(--fg3);
  margin: 0 0.4em;
}

/* ── Section: verify locally , closing line ── */
.verify-local-closing {
  font-family: var(--serif);
  font-size: clamp(13px, 1.4vw, 14.5px);
  font-weight: 300;
  line-height: 1.5;
  color: var(--fg2);
  margin: 1.2rem 0 0;
  max-width: 64ch;
}

/* ── proof summary card ──
   compact two-column dl , label column content-led on desktop, fixed
   on mobile so long urls and hashes don't crush labels. status row at
   the top carries a thin oxblood mark (when the page is found). one
   hairline above and below the dl, no per-row dividers. */
/* ── "verify locally" , 2 command blocks with descriptions ── */
/* .verify-local block-rhythm is owned by the scoped `.verify-page
   .verify-local` clamp() rule. */
.verify-local-desc {
  font-family: var(--serif);
  font-size: clamp(13px, 1.4vw, 14.5px);
  font-weight: 300;
  color: var(--fg2);
  line-height: 1.45;
  margin: 0 0 0.55rem;
  max-width: 60ch;
}
.verify-local-desc--second {
  margin-top: 1.4rem;
}

/* ── related records , compact link strip ──
   .verify-related block-rhythm + top hairline owned by the scoped
   `.verify-page .verify-related` rule. */
.verify-related-strip {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.025em;
  color: var(--fg);
  margin: 0;
  line-height: 1.5;
}
.verify-related-link {
  color: var(--fg);
  text-decoration: none;
  border-bottom: 1px solid var(--bd);
  transition: color 0.2s, border-bottom-color 0.2s;
}
.verify-related-link:hover,
.verify-related-link:focus-visible {
  color: var(--accent-text);
  border-bottom-color: var(--accent-text);
}
.verify-related-sep {
  color: var(--fg3);
  margin: 0 0.4em;
}

/* "route not in map" calm notice (renderunknown). */
.verify-unknown {
  margin: 0 0 1.8rem;
}

@media (max-width: 600px) {
  .verify-command-block {
    padding: 0.75em 0.9em;
  }
  .verify-command-block code {
    font-size: 11px;
    line-height: 1.55;
  }
  .verify-local-desc--second { margin-top: 1.1rem; }
}

/* accessibility */

/* subtle highlight for keyboard-navigated sections */
:target {
  outline-offset: 4px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 999;
  font-family: var(--mono);
  font-size: 12px;
  padding: 8px 16px;
  background: var(--fg);
  color: var(--bg);
  text-decoration: none;
}

.skip-link:focus {
  left: 8px;
  top: 8px;
}

/* legacy :focus-visible rule replaced by the editorial focus-visible
   system in @layer base above. keep .skip-link's focus override
   only — it needs to remain prominent so keyboard users can spot
   the bypass. */

:focus:not(:focus-visible) {
  outline: none;
}

/* layout */

.site {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 clamp(24px, 5vw, 80px);
}

/* Safe-area bottom on the main content surface so the last in-flow cta
   (e.g. the homepage `view project` button) never sits beneath ios
   safari's bottom chrome when the user has not yet scrolled to retract
   it. the footer carries its own safe-area-inset-bottom via .footer. */
main.site {
  padding-bottom: env(safe-area-inset-bottom);
}

section {
  padding: clamp(80px, 12vh, 160px) 0;
  border-bottom: 1px solid var(--bd);
}

section:last-of-type {
  border-bottom: none;
}

/* hero → approach divider · the hero is a 
, so the generic section { border-bottom } rule never paints a rule below it. add a top hairline on the first section so the hero-to-approach transition reads as resolved as the section-to-section breaks that follow. matched to the same --bd token + clamp padding the rest of the spine uses. */ .home-profile > section:first-of-type { border-top: 1px solid var(--bd); } /* nav ── the nav family lives inside @layer components so the homepage disclosure rules in @layer pages (later in cascade order) can win normally for the open-state layout (display:grid · text-align:right) without needing !important. per css cascade layers level 5, normal declarations cascade later-layer-wins; !important declarations cascade earlier-layer-wins, with unlayered author rules sitting above all layered author rules. leaving these rules unlayered would silently override @layer pages every time. ── */ @layer components { .nav { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: var(--bg); border-bottom: 1px solid var(--bd); /* Safe-area top padding so the nav row sits below the ios notch / dynamic island in `viewport-fit=cover` mode on non-home pages. homepage @layer pages override (mobile) sets its own padding- block via env(safe-area-inset-top) — that wins via cascade. */ padding-top: var(--safe-top); } .nav-inner { max-width: 1200px; margin: 0 auto; /* horizontal safe-area padding: the inset-left/right tokens are non-zero on landscape iphone (notch on one side); max() with the existing clamp() keeps the editorial register on every other orientation while preventing the nav from slipping under the notch in landscape. */ padding-block: 0; padding-inline-start: max(clamp(24px, 5vw, 80px), var(--safe-left)); padding-inline-end: max(clamp(24px, 5vw, 80px), var(--safe-right)); display: flex; justify-content: space-between; align-items: center; /* phase 40 · 52 → 54px gives a touch more vertical breathing so the header reads as deliberate rather than utilitarian. within the 60px --nav-offset buffer, anchor scrolling stays correct. */ height: 54px; } /* the wordmark is identity · söhne mono carries the archival voice. phase 40 · tracking 0.10 → 0.09em pulls the wordmark slightly tighter so it reads as set type rather than spread-out caps. */ .nav-mark { font-family: var(--mono); font-size: 11px; font-weight: 500; letter-spacing: 0.09em; text-transform: uppercase; color: var(--fg); text-decoration: none; } /* nav-toggle / nav-links / nav-toggle-glyph / nav-toggle-label — retired site-wide. the masthead is the only header element now; the homepage scrolls naturally and discovery is by reading. */ /* narrow phones: tighten the masthead horizontally so the stacked trent / power lockup sits cleanly against the page edge. */ @media (max-width: 600px) { .nav-inner { padding-inline: 15px; height: 58px; align-items: center; } .nav-mark { display: block; max-width: 4.55rem; white-space: normal; line-height: 1.08; font-size: 11px; letter-spacing: 0.09em; } .nav-mark span { display: block; } } @media (max-width: 390px) { .nav-inner { padding-inline: 13px; } .nav-mark { max-width: 4.35rem; font-size: 10.5px; letter-spacing: 0.08em; } } } /* end @layer components — masthead. */ /* section 1 · hero / positioning */ .hero { min-height: 100vh; min-height: 100svh; /* ios safari ≥15.4 retracts chrome via the small viewport unit */ display: flex; flex-direction: column; justify-content: flex-end; /* widened from 52px so the hero text always clears the nav at every width (52px desktop nav, 58px mobile nav, plus a small breathing budget). with sticky positioning the nav reserves flow space, so this padding-top is partly belt-and-braces — keeps text away from the very top edge even when the nav scroll-hides. */ padding-top: clamp(60px, 8vh, 80px); padding-bottom: clamp(60px, 10vh, 120px); /* no border-bottom: the trust-mark sits inside the hero in dom flow after .hero-body, so a 1px line here would cut between hero text and the trust mark. section-to-section separation comes from the `section { border-bottom }` rule on .section-approach below. */ } /* hero eyebrow. the "trent power" label is rendered via CSS-generated content so reader view extracts only one author signal (from the article body / metadata), not a duplicate eyebrow text node. the
in html is empty + aria-hidden. */ .hero-name::before { content: "Trent Power"; } .hero-name { font-family: var(--mono); font-size: clamp(11px, 1.2vw, 13px); font-weight: 500; letter-spacing: 0.2em; text-transform: uppercase; color: var(--fg3); margin-bottom: clamp(24px, 4vh, 48px); } .hero-statement { font-family: var(--serif); font-size: clamp(42px, 10vw, 82px); font-weight: 300; line-height: 0.98; letter-spacing: -0.035em; color: var(--fg); max-width: 920px; margin-bottom: clamp(32px, 5vh, 56px); } .hero-body { font-family: var(--serif); font-size: clamp(15px, 1.6vw, 19px); font-weight: 300; line-height: 1.65; color: var(--fg2); /* phase 40 · ~5% wider measure (580 → 610px). on mobile this gives the hero paragraph fewer awkward short-line wraps; on desktop it stays well below the 720px page-title cap so the editorial hierarchy is preserved. */ max-width: 610px; } /* hero entrance · single reveal contract (phase 37) ────────────────────────────────────────────────────────── default: hero pieces start invisible and lifted. when js is off (no .js class on ), the override below paints them at rest immediately so no-JS visitors see the hero. when js is on and html.enhanced has been added (after DOMContentLoaded in app.js), each piece animates up in sequence with the trust mark participating as the last beat. reduced-motion users see the resting state regardless. */ /* hero reveal · gated on html.js (set inline pre-paint by the language bootstrap, runs synchronously before the first paint frame). hiding and the reveal keyframe are now driven by the same class, so a failed or slow app.js cannot leave the hero stuck invisible — the animation starts in the same paint tick the hide begins. when js is disabled entirely the hide rule never matches and the hero paints resting, no fallback rule needed. */ html.js .hero-name, html.js .hero-statement, html.js .hero-body, html.js .trust-mark { opacity: 0; transform: translateY(14px); } html.js .hero-name { animation: revealUp 700ms cubic-bezier(.2,.7,.2,1) forwards; } html.js .hero-statement { animation: revealUp 800ms cubic-bezier(.2,.7,.2,1) 90ms forwards; } html.js .hero-body { animation: revealUp 850ms cubic-bezier(.2,.7,.2,1) 180ms forwards; } html.js .trust-mark { animation: revealUp 850ms cubic-bezier(.2,.7,.2,1) 270ms forwards; } /* hero entrance keyframe · animates from the initial-hidden state set above (opacity: 0; transform: translateY(14px)) to resting. referenced by each of the four hero pieces with a staggered delay. */ @keyframes revealUp { to { opacity: 1; transform: translateY(0); } } @media (prefers-reduced-motion: reduce) { .hero-name, .hero-statement, .hero-body, .trust-mark { opacity: 1 !important; transform: none !important; animation: none !important; } } /* ─── trust mark ────────────────────────────────────────── bordered editorial tag under the hero body. reads as an intentional certification badge, not three plain links. sits inside
in dom order after .hero-statement and .hero-body. participates in the `html.enhanced` hero reveal sequence defined above. links to /privacy/, /source/, /integrity/ at link-inherit colours (never default blue). */ .trust-mark { display: inline-flex; flex-wrap: wrap; align-items: center; width: fit-content; max-width: 100%; margin-block-start: clamp(1.2rem, 3vw, 1.8rem); /* phase 40 · slightly more present so the certification strip reads as deliberate rather than apologetic. phase 46 · radius elevated to var(--radius-pill) (999px). phase 49 · reversed back to var(--radius-soft) (7px). phase 53 · subtle background tint promotes the trust mark from bordered text strip to archival label — visually matches the verify / integrity card surface (--surface-archival) so the mark reads as a small object from the same paper family. */ background: var(--surface-archival); padding: 0.6rem 0.8rem; border: 1px solid color-mix(in srgb, var(--fg) 14%, transparent); border-radius: var(--radius-soft); font-family: var(--mono); font-size: 0.7rem; line-height: 1.2; letter-spacing: 0.07em; color: var(--fg3); } .trust-mark a { color: inherit; text-decoration: none; } .trust-mark a:visited { color: inherit; } .trust-mark a + a::before { content: "·"; display: inline-block; margin-inline: 0.55rem; opacity: 0.55; color: currentColor; /* phase 39 · optical-centre the dot between cap heights; matches the same pattern used on the footer actions row. */ transform: translateY(-0.02em); } .trust-mark a:hover, .trust-mark a:focus-visible { color: var(--fg); text-decoration: underline; text-underline-offset: 0.18em; } .trust-mark a:focus-visible { outline: 1px solid currentColor; outline-offset: 0.25rem; } @media (max-width: 700px) { .trust-mark { font-size: 0.64rem; letter-spacing: 0.06em; } } /* section labels */ /* section labels render in söhne mono. söhne mono does not ship `smcp` / `c2sc` opentype features in this licence cut, so we use measured css uppercase + restrained tracking rather than faux small caps. */ .section-label { font-family: var(--mono); font-size: 10.5px; font-weight: 500; letter-spacing: 0.10em; text-transform: uppercase; color: var(--fg3); margin-bottom: clamp(40px, 6vh, 72px); } /* the single static oxblood mark on the page: the section index numeral. */ .section-label .label-num { color: var(--accent-text); margin-right: 8px; } /* section 2 · focus & approach */ .principles { display: grid; gap: 0; list-style: none; } /* principles, trajectory chapters, and project cards are visible by default so the page renders correctly without javascript. the `.js` class (added by the inline language script before first paint) opts into the scroll-reveal behaviour. */ .principle { padding: clamp(28px, 4vh, 48px) 0; border-bottom: 1px solid var(--bd-soft); display: grid; grid-template-columns: 1fr; gap: 8px; } .js .principle { opacity: 0; transform: translateY(16px); transition: opacity 0.6s ease, transform 0.6s ease; } .js .principle.visible { opacity: 1; transform: translateY(0); } .principle:last-child { border-bottom: none; } .principle-title { font-family: var(--serif); font-size: clamp(22px, 3vw, 30px); font-weight: 400; line-height: 1.2; color: var(--fg); letter-spacing: -0.018em; } .principle-body { font-family: var(--serif); font-size: clamp(14px, 1.4vw, 17px); font-weight: 300; line-height: 1.6; color: var(--fg2); max-width: 560px; } @media (min-width: 768px) { .principle { grid-template-columns: 1fr 1fr; gap: clamp(24px, 4vw, 80px); align-items: baseline; } } /* section 3 · trajectory */ .trajectory-grid { display: grid; gap: 0; } /* trajectory · scroll-reveal initial state. the IntersectionObserver in app.js promotes each .trajectory-item to .visible as it enters the viewport. gating on .js means the items are visible by default when scripting is unavailable — no orphaned hidden cards. */ .js .trajectory-item { opacity: 0; transform: translateY(16px); transition: opacity 0.6s ease, transform 0.6s ease; } .js .trajectory-item.visible { opacity: 1; transform: translateY(0); } /* legacy .chapter-* typography retired — the new .trajectory-item / .trajectory-kicker / .trajectory-title / .trajectory-detail rules in the trajectory chronology block downstream now carry the same register. anchors to #clienteling-definition still resolve to the in the principles list above. */ /* trajectory · resilient editorial chronology. three breakpoint modes share one dom — a vertical archival chronology under 760 px, a two-column editorial grid at 760–1099 (label + rail on the left, items stacked 2×2 on the right), and a four-column horizontal timeline at 1100 and above with a rail running across the top of each column. all layout sits inside css grid + flex; no viewport- percentage anchoring, no transform hacks, no per-card absolute placement. custom properties handle the few tunable knobs. */ .section-trajectory { --trajectory-line: var(--bd-soft); --trajectory-marker: var(--fg3); --trajectory-current: var(--accent); --trajectory-muted: var(--fg3); --trajectory-gap: clamp(1.5rem, 3.5vw, 4rem); --trajectory-card-max: 22rem; } .trajectory-list { list-style: none; padding: 0; margin: 0; } .trajectory-item { min-width: 0; position: relative; /* belt-and-braces marker reset · ios safari occasionally paints decimal markers even when the parent ol has list-style: none. setting list-style on the li and emptying ::marker kills the "1. 2. 3." numbering for good. */ list-style: none; } .trajectory-item::marker { content: ""; } .trajectory-year { display: block; font: 500 11px/1.2 var(--mono); letter-spacing: 0.04em; color: var(--trajectory-muted); font-variant-numeric: tabular-nums lining-nums; margin: 0; } .trajectory-item--current .trajectory-year { color: var(--trajectory-current); font-weight: 500; } .trajectory-marker { display: block; width: 0.55rem; height: 0.55rem; border: 1px solid var(--trajectory-marker); border-radius: 999px; background: var(--bg); box-sizing: border-box; } .trajectory-item--current .trajectory-marker { width: 0.7rem; height: 0.7rem; background: var(--trajectory-current); border-color: var(--trajectory-current); } .trajectory-card { display: flex; flex-direction: column; gap: 0.45rem; max-width: var(--trajectory-card-max); margin: 0; padding: 0; } .trajectory-kicker { font: 500 10px/1.1 var(--mono); letter-spacing: 0.10em; text-transform: uppercase; color: var(--trajectory-muted); margin: 0; } .trajectory-title { font: 400 clamp(15px, 1.4vw, 17px)/1.3 var(--serif); letter-spacing: -0.012em; color: var(--fg); margin: 0; text-wrap: balance; } .trajectory-org { font: 500 10px/1.2 var(--mono); letter-spacing: 0.06em; text-transform: none; color: var(--trajectory-current); margin: 0; } .trajectory-detail { font: 300 clamp(13px, 1vw, 14px)/1.55 var(--serif); color: var(--fg2); margin: 0; } .trajectory-period { font: 500 9.5px/1.4 var(--mono); letter-spacing: 0.06em; color: var(--trajectory-muted); font-variant-numeric: tabular-nums lining-nums; margin: 2px 0 0; white-space: nowrap; } .trajectory-period time { font: inherit; color: inherit; } /* ── mobile · vertical archival chronology ─────────────────── rail is a left border on the list; markers pull into the gutter over the rail. a subtle border-top separates each row. spacing is generous but never oversized. */ @media (max-width: 759.98px) { .trajectory-list { margin: clamp(2.4rem, 9vw, 3.6rem) 0 0; padding: 0 0 0 1.25rem; border-left: 1px solid var(--trajectory-line); } .trajectory-item { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.5rem; padding: 0 0 clamp(1.6rem, 6vw, 2.2rem) clamp(1rem, 3.5vw, 1.4rem); } .trajectory-item + .trajectory-item { border-top: 1px solid var(--trajectory-line); padding-top: clamp(1.6rem, 6vw, 2.2rem); } .trajectory-year { order: 1; } .trajectory-marker { position: absolute; top: clamp(1.6rem, 6vw, 2.2rem); left: calc(-1.25rem - 0.275rem); } .trajectory-item:first-child .trajectory-marker { top: 0; } .trajectory-item--current .trajectory-marker { left: calc(-1.25rem - 0.35rem); } .trajectory-card { order: 2; gap: 0.45rem; } .trajectory-title { font-size: clamp(15px, 4vw, 17px); max-width: 24rem; } .trajectory-detail { font-size: clamp(13px, 3.7vw, 15px); max-width: 32rem; } } /* ── tablet · two-column editorial grid (760–1099) ──────────── the section label sits in a quiet left rail; the four items stack 2×2 on the right. no horizontal axis (it would clip at this width); the chronology reads as a structured editorial index. */ @media (min-width: 760px) and (max-width: 1099.98px) { .section-trajectory { display: grid; grid-template-columns: minmax(10rem, 14rem) minmax(0, 1fr); column-gap: clamp(2rem, 4vw, 3.5rem); align-items: start; } .section-trajectory > .section-label { grid-column: 1; margin: 0; padding-right: clamp(1rem, 2vw, 2rem); border-right: 1px solid var(--trajectory-line); min-height: 2rem; } .trajectory-list { grid-column: 2; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(1.5rem, 3vw, 2.5rem); row-gap: clamp(1.8rem, 3.5vw, 2.8rem); margin: 0; } .trajectory-item { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto auto; row-gap: 0.55rem; padding-top: 1.1rem; border-top: 1px solid var(--trajectory-line); } .trajectory-year { grid-row: 1; margin: 0; } .trajectory-marker { grid-row: 2; justify-self: start; margin: 0; } .trajectory-card { grid-row: 3; max-width: 24rem; } } /* ── desktop · four-column horizontal chronology (≥ 1100) ───── rail is a single horizontal hairline across the top of the list via ::before. each item is a column: year above marker above card. marker margins are calibrated so the dot's vertical centre lands on the rail regardless of font metrics. */ @media (min-width: 1100px) { .trajectory-list { position: relative; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); column-gap: var(--trajectory-gap); margin: clamp(3rem, 7vw, 5rem) 0 0; } .trajectory-list::before { content: ""; position: absolute; left: 0; right: 0; top: 2.4rem; border-top: 1px solid var(--trajectory-line); } .trajectory-item { display: flex; flex-direction: column; align-items: flex-start; min-width: 0; } .trajectory-year { margin: 0 0 1rem; } .trajectory-marker { margin: 0 0 1.25rem; /* year-bottom + 1rem gap + 0.55rem dot puts dot centre at the rail (rail sits at top: 2.4rem on the list). */ align-self: flex-start; } .trajectory-item--current .trajectory-marker { margin: -0.075rem 0 1.175rem; } .trajectory-card { max-width: var(--trajectory-card-max); } } /* ── print · existing print profile owns the trajectory ─────── */ @media print { .trajectory-list { display: none; } } @media (prefers-reduced-motion: reduce) { .trajectory-item { animation: none; transition: none; } } /* section 4 · personal projects */ .project-card { /* editorial insert on the homepage — its own --paper-project tone so it lifts visibly off the front-of-house ivory and reads as a sheet placed on the page, never as a record-layer card. phase 39 · 2px outer radius softens the object quality just enough to read as a mounted archival sheet rather than a mathematically sharp web card. internal rules, metadata rows and buttons stay sharp. phase 46 · literal 8px → var(--radius-soft) (7px). */ background: var(--paper-project); border: 1px solid var(--rule); border-radius: var(--radius-soft); padding: clamp(36px, 8vw, 48px) clamp(28px, 8vw, 44px) clamp(34px, 9vw, 52px); margin-top: clamp(40px, 7vh, 64px); position: relative; } @media (min-width: 768px) { .project-card { padding: clamp(48px, 5vw, 72px); } } .js .project-card { opacity: 0; transform: translateY(20px); transition: opacity 0.7s ease, transform 0.7s ease; } .js .project-card.visible { opacity: 1; transform: translateY(0); } .project-card::before { content: ''; position: absolute; top: 0; left: 0; width: 3px; height: 100%; background: var(--ac); } /* editorial cards opt out of the accent rail. */ .project-card--editorial::before { content: none; display: none; } .project-card--editorial { border-left: 1px solid var(--rule); } .project-name { font-family: var(--mono); /* phase 40 · slightly larger and tighter so the project title reads as the clear visual anchor of the card (söhne mono is only available in 400/500 — no heavier weight option, so the hierarchy bump comes from size + tracking instead of weight). stays mono caps, stays at full --fg ink. */ font-size: clamp(15px, 1.7vw, 19px); font-weight: 500; letter-spacing: 0.10em; text-transform: uppercase; color: var(--fg); margin-bottom: 18px; } .project-desc { font-family: var(--serif); font-size: clamp(19px, 5vw, 22px); font-weight: 400; line-height: 1.58; letter-spacing: -0.01em; color: var(--fg2); max-width: 32ch; margin-bottom: 34px; } .project-subline { font-family: var(--serif); font-size: clamp(14px, 1.6vw, 16px); font-style: italic; font-weight: 400; line-height: 1.55; color: var(--fg2); margin-top: 0; margin-bottom: 28px; } .project-link { font-family: var(--mono); font-size: 11px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent-text); text-decoration: none; display: inline-flex; align-items: center; gap: 8px; padding: 10px 0; border-bottom: 1px solid var(--bd); transition: border-color 0.2s, gap 0.2s; } .project-link:hover { border-color: var(--accent-text); gap: 12px; } /* Defensive: anywhere a typographic mark like → is rendered, force it through söhne mono so font-fallback can't substitute the glyph. */ .arrow { font-family: var(--mono); } .project-link .arrow { transition: transform 0.2s; } .project-link:hover .arrow { transform: translateX(2px); } /* button variant · replaces inline style="" for csp compliance */ .project-link-btn { margin-top: 24px; background: none; border: none; border-bottom: 1px solid var(--bd); cursor: pointer; } /* project preview · mimics the app ui */ .project-preview { margin-top: 34px; border-top: 1px solid var(--bd); padding-top: 26px; } .preview-header { font-family: var(--mono); font-size: 10px; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg3); margin-bottom: 16px; } .preview-items { display: grid; gap: 0; list-style: none; } .preview-item { padding: 10px 0; border-bottom: 1px solid var(--bd-soft); display: flex; justify-content: space-between; align-items: baseline; gap: 16px; } .preview-item:last-child { border-bottom: none; } .preview-title { font-family: var(--mono); font-size: 11px; font-weight: 500; color: var(--fg); } .preview-meta { font-family: var(--mono); font-size: 10px; color: var(--fg3); white-space: nowrap; } .preview-editorial { font-family: var(--serif); font-style: italic; font-size: 11px; color: var(--fg3); margin-top: 2px; } .project-preview-caption { font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em; color: var(--fg3); margin-top: 14px; line-height: 1.5; } .preview-tier { font-size: 9px; letter-spacing: 0.08em; padding: 1px 5px; border: 1px solid var(--bd); color: var(--fg3); margin-right: 6px; } .tier-walk { border-color: var(--bd); color: var(--fg3); } .tier-metro { border-color: var(--accent-text); color: var(--accent-text); } /* contact */ .section-contact { padding: clamp(80px, 12vh, 160px) 0 clamp(60px, 8vh, 100px); } @media (max-width: 767px) { .section-contact { padding-top: clamp(64px, 9.5vh, 112px); } } /* small editorial sentence above the email — gives the contact section a reason to write rather than three abrupt anchors. serif, muted, with a narrow measure so it reads as a brief abstract, not a paragraph. */ .contact-note { font-family: var(--serif); font-size: clamp(15px, 1.5vw, 17px); font-weight: 300; line-height: 1.55; color: var(--fg2); max-width: 32rem; margin: 0 0 clamp(1.1rem, 3vw, 1.5rem); } .contact-address { font-style: normal; margin: 0; } .contact-email { font-family: var(--mono); font-size: clamp(14px, 2vw, 22px); font-weight: 400; color: var(--fg); text-decoration: none; border-bottom: 1px solid var(--bd); padding-bottom: 4px; transition: border-color 0.2s; /* Long-text resilience: at 200% zoom on a 320 px viewport the rendered mailto could otherwise overflow the column. word-break stays normal so spaces in the readable form are preserved. */ overflow-wrap: anywhere; word-break: normal; } .contact-email:hover { border-color: var(--accent-text); } /* Long-token resilience for narrow viewports / 200% zoom. mono url and project-preview titles can each overrun their column at 320px width without `overflow-wrap: anywhere`. fingerprint grids are component-controlled and therefore excluded here. */ .preview-title { overflow-wrap: anywhere; word-break: normal; } .integrity-record-status, .verify-status, .record-meta, .integrity-rg-value, .integrity-rg-desc, .integrity-rg-link, .integrity-rg-value code, :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid dd, :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid a, :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid code, :is(.verify-page, .sw-reset-page, .security-page, .source-page) .record-grid samp, .verify-intro-row dd, .verify-intro-row dd code, .source-entry-name a, .source-entry-meta { max-width: 100%; overflow-wrap: anywhere; word-break: break-word; white-space: normal; font-variant-ligatures: none; } .record-fingerprint, .record-fingerprint bdi { overflow-wrap: anywhere; word-break: break-all; font-variant-ligatures: none; } .contact-secondary { margin-top: 20px; } .contact-secondary a { font-family: var(--mono); font-size: 11px; color: var(--fg3); text-decoration: none; letter-spacing: 0.04em; transition: color 0.2s; } .contact-secondary a:hover { color: var(--fg); } /* Mobile-only contact refinement: email gains a touch more presence without becoming a headline; linkedin link stays secondary. */ @media (max-width: 767px) { .contact-email { font-size: clamp(15.5px, 4.3vw, 18px); letter-spacing: 0.01em; } .contact-secondary a { font-size: 11px; } } /* footer · centred editorial colophon (phase 37 stabilisation) ─────────────────────────────────────────────────────────────── one footer system. one cite trigger contract. no border line. centred on every breakpoint, calm and balanced — reads as an intentional colophon rather than a left rail of metadata. markup: `.cite-btn` is retained as a styling alias on the cite button element (the l8 touch-target validator looks for a standalone `.cite-btn { min-height: 44px }` rule in compiled css). phase 36 had already deleted every legacy `.footer-*` selector; this rewrite keeps that purge in place. */ /* footer · four elements, centred, vertically stacked. ── seal · · · — the typographer's return, mono, very wide tracking, fg3 at 55% opacity. screen-reader hidden. ── colophon — mono publication imprint: year, name, city/country separated by nbsp pairs (no commas between the three tokens), one comma kept inside the paris/france cluster. ── language switcher — serif, full language names in their own language. active = ink; alternate = fg3 lifting to accent on hover with a hairline border-bottom. italic slash separator. ── actions rail (optional) — mono, dot-separated. cite & verify · privacy. omitted on data-surface="record" or "archive". the home page and the privacy page are the only two surfaces that keep the rail. tokens: --bg / --fg / --fg3 / --accent / --bd-soft / --mono / --serif. */ .site-footer { margin-top: clamp(1.5rem, 4vw, 2.5rem); border-top: 1px solid var(--bd-soft); padding: 34px 28px calc(30px + env(safe-area-inset-bottom)); text-align: center; color: var(--fg3); display: flex; flex-direction: column; align-items: center; gap: 16px; } /* seal — three middle dots stretched apart by 0.8em letter-spacing, color reads as fg3 dialled back to 55% opacity. aria-hidden because the dots are a typographic flourish, not content. */ .site-footer__seal { font-family: var(--mono); font-size: 10px; letter-spacing: 0.8em; /* trailing letter-spacing pushes the visual centre right by 0.8em; pull the block back so · · · is optically centred. */ padding-inline-start: 0.8em; color: color-mix(in srgb, var(--fg3) 55%, transparent); margin: 0; line-height: 1; user-select: none; } /* colophon — publication imprint. nbsp pairs already in the markup so we don't need word-spacing tricks. tabular numerals keep 2026 from drifting between editions. */ .site-footer__colophon { font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; color: var(--fg3); font-variant-numeric: tabular-nums lining-nums; margin: 0; line-height: 1.4; } /* language switcher — serif, weight 300, full names. the alternate sits in fg3 and lifts to accent on hover with a 1 px border-bottom underline. the slash separator is italic 14 px in fg3 at 50% opacity so it reads as punctuation, not as a control. */ .site-footer__language { display: inline-flex; align-items: baseline; justify-content: center; gap: 0; font-family: var(--serif); font-size: 15px; font-weight: 300; line-height: 1.2; letter-spacing: 0; margin: 0; } .site-footer__language button { appearance: none; -webkit-appearance: none; border: 0; background: transparent; color: var(--fg3); font: inherit; text-decoration: none; border-bottom: 1px solid transparent; padding: 0 6px; /* 44×44 touch target carried by the row, not by per-button box geometry — letter-spacing + padding-inline keep the buttons tap-friendly without forcing a chrome line-box. */ min-height: 44px; min-width: 44px; cursor: pointer; transition: color 200ms ease, border-color 200ms ease; } .site-footer__language button[aria-pressed="true"] { color: var(--fg); } .site-footer__language button:hover, .site-footer__language button:focus-visible { color: var(--accent); border-bottom-color: var(--accent); outline: 0; } .site-footer__language button:focus-visible { outline: 1px solid var(--focus-colour, currentColor); outline-offset: 0.3rem; } .site-footer__lang-sep { font-family: var(--serif); font-style: italic; font-size: 14px; line-height: 1; color: color-mix(in srgb, var(--fg3) 50%, transparent); margin-inline: 4px; user-select: none; } /* actions rail — mono publication metadata. middle-dot separator at 50% opacity. follows the language switcher hover rule: color to accent, hairline border-bottom on. */ .site-footer__actions { display: inline-flex; align-items: baseline; justify-content: center; gap: 0; font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.06em; color: var(--fg3); margin: 0; line-height: 1.3; } .site-footer__actions a, .site-footer__actions button { appearance: none; -webkit-appearance: none; border: 0; background: transparent; color: inherit; font: inherit; letter-spacing: inherit; text-decoration: none; border-bottom: 1px solid transparent; padding: 0; min-height: 30px; cursor: pointer; transition: color 200ms ease, border-color 200ms ease; } .site-footer__actions a:hover, .site-footer__actions a:focus-visible, .site-footer__actions button:hover, .site-footer__actions button:focus-visible { color: var(--accent); border-bottom-color: var(--accent); outline: 0; } .site-footer__actions a:focus-visible, .site-footer__actions button:focus-visible { outline: 1px solid var(--focus-colour, currentColor); outline-offset: 0.3rem; } .site-footer__actions-sep { margin-inline: 8px; color: color-mix(in srgb, currentColor 50%, transparent); user-select: none; } /* l8 touch-target validator: dedicated `.cite-btn` rule with min-height 44px must appear in compiled css. the cite button no longer carries the class but the rule stays so the validator sees it. */ .cite-btn { min-height: 44px; } /* forced-colours / windows high-contrast */ @media (forced-colors: active) { .site-footer__language button:focus-visible, .site-footer__actions a:focus-visible, .site-footer__actions button:focus-visible { outline: 2px solid CanvasText; } } /* quiet settle animation when the language row updates */ @keyframes footerLanguageSettle { from { opacity: 0.72; transform: translateY(4px); } to { opacity: 1; transform: none; } } .site-footer__language.language-updated { animation: footerLanguageSettle 260ms ease-out; } @media (prefers-reduced-motion: reduce) { .site-footer__language.language-updated { animation: none; } } /* privacy page */ .page { padding-top: clamp(80px, 12vh, 140px); padding-bottom: clamp(60px, 8vh, 100px); } .page-title { font-family: var(--serif); font-size: clamp(40px, 10.5vw, 58px); font-weight: 300; line-height: 1.05; letter-spacing: -0.03em; color: var(--fg); margin-bottom: clamp(32px, 5vh, 56px); } .page-body { max-width: 580px; } .page-lede { font-family: var(--serif); font-size: clamp(24px, 6.3vw, 30px); font-weight: 400; line-height: 1.5; letter-spacing: -0.01em; color: var(--fg); max-width: 31ch; margin-bottom: 1.6em; } .page-body p { font-family: var(--serif); font-size: clamp(17px, 4.6vw, 20px); font-weight: 400; line-height: 1.6; letter-spacing: -0.008em; color: var(--fg2); max-width: 34ch; margin-bottom: 1.4em; } @media (min-width: 768px) { .page-lede { font-size: clamp(24px, 2.6vw, 30px); max-width: 50ch; } .page-body p { font-size: clamp(18px, 2vw, 21px); line-height: 1.65; max-width: 62ch; } } .page-body p:last-child { margin-bottom: 0; } /* refined editorial underlines under signifier text. quiet by default, oxblood on hover/focus, thin and offset so they don't visually weigh down large serif type. */ .page-body a { color: var(--fg); text-decoration: underline; text-decoration-thickness: 1px; text-decoration-color: var(--bd); text-underline-offset: 0.14em; border-bottom: none; transition: text-decoration-color 0.2s; } .page-body a:hover, .page-body a:focus-visible { text-decoration-color: var(--accent-text); } .page-meta { font-family: var(--mono); font-size: 11px; color: var(--fg3); letter-spacing: 0.06em; } /* technical evidence · file paths, hashes, fingerprints, verification resources. mono, slightly tabular, wraps cleanly on narrow viewports. does not inherit the editorial old-style figures. */ .technical-list { list-style: none; margin: 24px 0 32px; padding: 0; font-family: var(--mono); font-size: clamp(13px, 3.4vw, 15px); line-height: 1.7; letter-spacing: 0.02em; color: var(--fg2); } .technical-list li { padding: 4px 0; } .technical-list a, .code-path { font-family: var(--mono); font-size: inherit; color: var(--fg); word-break: break-word; overflow-wrap: anywhere; text-decoration: underline; text-decoration-thickness: 1px; text-decoration-color: var(--bd); text-underline-offset: 0.18em; border-bottom: none; transition: text-decoration-color 0.2s; } .technical-list a:hover, .technical-list a:focus-visible { text-decoration-color: var(--accent-text); } /* /integrity/releases/ — release register. the page is a quiet archival index, not a download surface. two groups (current release / archive) each carrying release rows. no cards, no boxes — hairline rules between rows do the work. vertical rhythm tightened ~25-30% from the first-pass values so the page reads as a finished register on mobile rather than a sparse list. whitespace remains the hierarchy; it is just no longer being used as emptiness. */ /* releases page sections opt out of the generic section { padding + border-bottom } rule — three nested
elements (release- index + two release-groups) were each inheriting 80-160px of vertical padding and a hairline border-bottom, producing large empty voids and unnecessary horizontal rules between the intro, current release, archive, and related records. .release-index and .release-group only exist on the releases page so class specificity (0,1,0) beats the generic section selector (0,0,1) without needing the body[data-page=…] layer wrap. */ .release-index { /* intro → current release · compact editorial gap. */ padding: 0; border-bottom: 0; margin: clamp(3rem, 9vw, 4.5rem) 0 0; } .release-group { /* current → archive · slightly larger than the in-card rhythm so the two groups read as peer sections, but no full section-block void between them. */ padding: 0; border-bottom: 0; margin: clamp(3rem, 9vw, 4.5rem) 0 0; } .release-group:first-child { margin-top: 0; } .release-group-label { /* shared metadata-label register — matches page record, manifest, history across the trust system. title-case mono, very small, muted; never competes with the release titles below it. border-top + padding-top retired — the rule was floating between sections and fragmenting the page. spacing alone separates current release from archive. */ font-family: var(--mono); font-size: 0.62rem; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg3); margin: 0 0 1rem; } /* related-records → archive gap · with the generic section padding retired, .related-nav's default 16-24px top margin reads as cramped against the archive card above. scope via the unique adjacent-sibling on the releases page (.release-index is only emitted there) so we beat the shared .related-nav margin on specificity (0,2,0 > 0,1,0) without leaving the unlayered tier. */ .release-index + .related-nav { margin-top: clamp(4rem, 11vw, 6rem); } .release-row { /* phase 42 · each release is now an archival object — framed, padded, subtly lifted off the page surface so the register reads as a release registry of preserved snapshots rather than a loose list of rows. matches the verify-card / project- card / integrity-record-card object family. internal hierarchy (date / title / meta / actions) stays sharp. phase 46 · literal 8px → var(--radius-soft) (7px). */ background: color-mix(in srgb, var(--surface-page) 92%, white 8%); border: 1px solid var(--rule); border-radius: var(--radius-soft); padding: clamp(1.4rem, 4vw, 2rem); margin-bottom: clamp(0.85rem, 2vw, 1.25rem); } .release-row:last-child { margin-bottom: 0; } .release-row--current { /* current release gets a touch more presence than the archive rows — slightly heavier title, marginally more breathing room. */ padding-bottom: clamp(1.5rem, 4.5vw, 2.2rem); } .release-date { /* shared metadata-label register again — date sits as the small uppercase locator above the human-readable title. */ font-family: var(--mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg3); font-variant-numeric: tabular-nums lining-nums; margin: 0 0 0.3rem; } .release-title { font-family: var(--sans); font-style: normal; font-weight: 400; font-size: clamp(15px, 3.6vw, 17px); line-height: 1.35; color: var(--fg); margin: 0 0 0.25rem; } .release-row--current .release-title { font-size: clamp(16px, 3.8vw, 18px); } .release-meta { font-family: var(--mono); font-size: 0.64rem; letter-spacing: 0.04em; color: var(--fg3); margin: 0; line-height: 1.5; } /* compact action strip — download zip · download TAR.GZ · checksums & signature · view release. inline middots between actions, wraps cleanly on narrow screens; restrained underline weight (--rule) so the row reads as a footnote to the release title above. */ .release-actions { margin: 0.7rem 0 0; font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.025em; line-height: 1.7; color: var(--fg3); } .release-action { color: var(--fg2); text-decoration: none; border-bottom: 1px solid var(--rule); transition: color 0.2s, border-bottom-color 0.2s; } .release-action:hover, .release-action:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); outline: 0; } /* phase 42 · actions separator now rendered as ::before pseudo on each sibling after the first (matches the .site-footer__actions and .trust-mark separator pattern). the legacy .release-actions-sep elements are no longer emitted in current html, but the rule is kept to hide any cached html that still includes them. */ .release-actions > * + *::before { content: "·"; display: inline-block; margin-inline: 0.42rem; opacity: 0.45; transform: translateY(-0.02em); color: currentColor; } .release-actions-sep { display: none; } @media (max-width: 700px) { .release-row--current { padding-bottom: clamp(1.3rem, 5vw, 1.8rem); } /* mobile stacks each action on its own line — middots are hidden and links sit as a clean four-line list. the wrapping behaviour of an inline strip becomes unnecessary, and "checksums & signature" is given room to breathe on the smallest screens. */ .release-actions { display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.66rem; line-height: 1.5; } .release-action { align-self: flex-start; } .release-actions > * + *::before { display: none; } } .fingerprint { display: inline-block; font-family: var(--mono); font-size: clamp(13px, 3.4vw, 15px); line-height: 1.8; letter-spacing: 0.08em; color: var(--fg); overflow-wrap: anywhere; word-break: break-all; user-select: text; -webkit-user-select: text; } /* designed proof list · three numbered verification artefacts on the integrity page. söhne mono for numerals and file paths, signifier for descriptions, hairline rule between items. the oxblood accent only on the proof numeral. */ .proof-list { margin: 24px 0 28px; /* real
    for reader view extraction; .proof-num spans carry the visual numbering, so suppress browser default markers + padding. */ list-style: none; padding: 0; } .proof-item { display: grid; grid-template-columns: 2.4rem minmax(0, auto) minmax(0, 1fr); align-items: baseline; column-gap: clamp(10px, 2vw, 16px); padding: 14px 0; border-top: 1px solid var(--bd-soft); color: inherit; } .proof-list .proof-item:last-of-type { border-bottom: 1px solid var(--bd-soft); } .proof-num { font-family: var(--mono); font-size: 11px; font-weight: 500; letter-spacing: 0.14em; color: var(--accent-text); font-variant-numeric: tabular-nums lining-nums; } /* only the file path is a link. the description is descriptive prose and never underlines. */ .proof-path { font-family: var(--mono); font-size: clamp(12px, 3vw, 14px); letter-spacing: 0.02em; color: var(--fg); overflow-wrap: anywhere; word-break: break-word; text-decoration: none; border-bottom: 1px solid transparent; transition: border-bottom-color 0.2s; } .proof-path:hover, .proof-path:focus-visible { border-bottom-color: var(--accent-text); } .proof-desc { font-family: var(--serif); font-size: clamp(14px, 3.4vw, 16px); line-height: 1.5; color: var(--fg2); text-decoration: none; border: 0; } @media (max-width: 600px) { .proof-item { grid-template-columns: 1.8rem minmax(0, 1fr); grid-template-rows: auto auto; row-gap: 4px; } .proof-num { grid-row: 1 / span 2; align-self: start; padding-top: 0.3em; } } /* fingerprint block. signifier label, mono fingerprint code, restrained copy button below. button keeps a minimum width so the copied state does not shift its placement and never wraps to the same line as the fingerprint. */ .fingerprint-block { display: grid; gap: 12px; margin: 24px 0 32px; } .fingerprint-label { font-family: var(--serif); font-size: clamp(19px, 5vw, 24px); font-weight: 400; line-height: 1.25; letter-spacing: -0.01em; text-transform: none; color: var(--fg); margin: 0; } .fingerprint { display: block; user-select: text; -webkit-user-select: text; overflow-wrap: anywhere; word-break: break-word; } /* phase 49 · copy fingerprint as an inline editorial action. phase 50 · quieter still — smaller, lighter, subordinate to the fingerprint itself. opacity 0.72 by default; lifts to 1 on hover/ focus so the action remains discoverable. js hook (.copy- fingerprint[data-copy-target]) and i18n bindings preserved. text-transform cancels the parent .integrity-rg-label uppercase cascade; opacity here multiplies with the parent 0.78 — combined effective opacity ~0.56 at rest, lifting to 0.78 on interaction. */ /* phase 49 · copy fingerprint as an inline editorial action. phase 54 · restored text-decoration: underline pattern, explicit font-weight + line-height. parent .integrity-rg-label opacity moved to color-mix so the button's declared opacity renders directly. phase 55 · .record-inline-action joined as a shared class so copy fingerprint + copy command + any future inline action sit in one declarative register. existing .copy-fingerprint and .trust-code-copy selectors remain in the combined rule for compatibility with the cite.js js bindings. */ .record-inline-action, .copy-fingerprint, .trust-code-copy { appearance: none; -webkit-appearance: none; background: transparent; border: 0; padding: 0; font-family: var(--mono); font-size: 0.58rem; font-weight: 400; line-height: 1.2; letter-spacing: 0.055em; text-transform: none; color: var(--fg2); text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--fg2) 42%, transparent); text-underline-offset: 0.18em; cursor: pointer; transition: color 0.2s, text-decoration-color 0.2s; } .record-inline-action:hover, .record-inline-action:focus-visible, .copy-fingerprint:hover, .copy-fingerprint:focus-visible, .trust-code-copy:hover, .trust-code-copy:focus-visible { color: var(--fg); text-decoration-color: currentColor; outline: 0; } .record-inline-action[data-state="copied"], .copy-fingerprint[data-state="copied"], .trust-code-copy[data-state="copied"] { color: var(--accent-text); text-decoration-color: var(--accent-text); } /* One-line philosophy sentence on /source/, sitting between the intro lede and the download affordance. same body register as the rest of the page-body but a touch quieter; reads as a calm catalogue note, not an epigraph. no italic — italics here would be the only italic on the page and read as decorative rather than systemic. */ .source-about { margin: 12px 0 0; font-family: var(--serif); font-size: clamp(15px, 1.5vw, 17px); font-weight: 300; color: var(--fg2); line-height: 1.55; } /* quiet helper line under the philosophy sentence: explains the .txt mirror convention in one breath so a first-time visitor understands why the catalogue links don't open as HTML/CSS/JS. mono register, fg3, smaller than .source-about; sits as a footnote, not a second epigraph. */ .source-mirror-note { margin: 6px 0 0; font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.025em; color: var(--fg3); line-height: 1.55; } /* phase 61 · .source-mirror-intro retired. the source-mirror reading rule is now implicit: each link opens the readable .txt mirror. the page lede + per-file descriptions carry the reader without a companion gloss. */ /* Group-label count register — "pages, 12". same fg3 mono register as the label itself; no extra colour. */ .source-group-count { color: var(--fg3); font-weight: 400; } /* quiet "download the source archive" affordance on /source/. sits between the page lede and the directory listing. mono, small, calm — no button styling. the links inherit the page's underlined-link treatment. */ .source-download { margin-top: 12px; font-family: var(--mono); font-size: clamp(10.5px, 2.7vw, 12px); line-height: 1.6; color: var(--fg2); } .source-download span[data-i18n] { /* keep the lede on its own conceptual line by allowing the browser to break before the first separator on narrow widths; the inline phrase wraps as a single sentence followed by a calm list of three downloads rather than a fragmented chain. */ display: inline; } .source-download a { color: inherit; white-space: nowrap; text-decoration-color: color-mix(in srgb, var(--fg2) 42%, transparent); } .source-download a:hover, .source-download a:focus-visible { color: var(--fg); text-decoration-color: currentColor; } /* /source/ — editorial provenance registry. the source page is a public inspection ledger, not a directory dump. each editorial group renders as a section with a quiet mono kicker, a single-line serif gloss, and a
    body of .source-entry rows. each entry is a two-column micro-grid: filename left, mono meta right (kind · size · validated date), with an optional short SHA-256 below. typography is mono for identifiers and metadata so the page reads as a typeset archival catalogue. */ /* widen the page body on /source/ to match /verify/. the catalogue is the longest editorial run on the site; the default 580px prose column reads as an austere ribbon and over-isolates the right field. 880px gives the registry room to breathe without becoming a sprawl. */ .source-page .page-body { max-width: 880px; } @media (max-width: 700px) { .source-page .page-body { max-width: none; } } /* the about line that follows the page lede on /source/. names the page's editorial taxonomy in one calm sentence so the reader has a mental map before scrolling into the registry. matched to the lede register (serif, 300, fg2) but slightly smaller and below max-width so it reads as continuation, not a new paragraph. */ /* phase 61 · .source-intro_about, .source-fingerprint-note, .source-mirror-intro and .source-philosophy retired. /source/ now opens on a single editorial lede plus the download line, and the files themselves carry the page. */ .source-registry-wrap { margin-block-start: clamp(28px, 4.5vw, 40px); display: grid; gap: clamp(2.4rem, 5.5vw, 3.6rem); } .source-group-gloss { margin: 0 0 1.1rem; font-family: var(--serif); font-size: clamp(14.5px, 1.8vw, 16px); font-weight: 300; line-height: 1.55; color: var(--fg2); max-width: 60ch; } .source-group { margin: 0; padding: 0; } .source-group-header { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; margin: 0 0 0.85rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--rule); } .source-group-title { margin: 0; font-family: var(--mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase; color: var(--fg3); } .source-group-count { margin: 0; font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.06em; color: var(--fg3); font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; } .source-registry { margin: 0; padding: 0; display: grid; gap: 0; } /* registry row — a three-area micro-grid: row 1: filename (left) · mono meta (right) row 2: editorial description spanning the full width no per-row border. rhythm comes from generous padding so the page reads as an authored ledger, not a directory index. a single faint hairline appears between entries via the + sibling selector at very reduced opacity. */ .source-entry { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: clamp(0.85rem, 3.5vw, 1.5rem); row-gap: clamp(0.32rem, 0.9vw, 0.45rem); align-items: baseline; padding-block: clamp(0.85rem, 1.8vw, 1.05rem); } .source-entry + .source-entry { border-block-start: 1px solid color-mix(in srgb, var(--bd-soft) 55%, transparent); } .source-entry:last-child { border-bottom: 0; } .source-entry-name { margin: 0; min-width: 0; font-family: var(--mono); font-size: clamp(0.78rem, 2.4vw, 0.86rem); font-weight: 500; line-height: 1.45; letter-spacing: 0.01em; color: var(--fg); word-break: break-all; overflow-wrap: anywhere; font-variant-ligatures: none; } .source-entry-name a { color: inherit; text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--fg) 16%, transparent); transition: color 0.2s, border-bottom-color 0.2s; } .source-entry-name a:hover, .source-entry-name a:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); outline: 0; } .source-entry-name code { font-family: inherit; font-size: inherit; background: transparent; padding: 0; } /* quiet "raw" link alongside the reader link */ .source-entry-raw { font-family: var(--mono); font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg3); text-decoration: none; margin-inline-start: 0.5rem; opacity: 0.7; transition: opacity 0.15s ease, color 0.15s ease; } .source-entry-raw:hover, .source-entry-raw:focus-visible { color: var(--accent-text); opacity: 1; } .source-entry-meta { margin: 0; justify-self: end; text-align: right; font-family: var(--mono); font-size: 0.66rem; line-height: 1.45; letter-spacing: 0.04em; color: var(--fg3); opacity: 0.82; font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; font-variant-ligatures: none; white-space: normal; } .source-entry-meta abbr { text-decoration: none; border: 0; cursor: default; color: var(--fg3); } .source-entry-sep { margin: 0 0.18rem; opacity: 0.55; } .source-entry-size { font-variant-numeric: tabular-nums lining-nums; } .source-entry-date { color: var(--fg3); } .source-entry-hash { /* the hash is a citation, not a primary label. soft enough to recede when the eye is scanning filenames; selectable and full-precision via the title attribute on the . */ margin: 0.18rem 0 0; font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.045em; color: color-mix(in srgb, var(--fg3) 70%, transparent); line-height: 1.4; } .source-entry-hash abbr { text-decoration: none; border: 0; cursor: default; margin-right: 0.35rem; opacity: 0.7; } .source-entry-hash samp { font-family: inherit; font-size: inherit; color: inherit; } /* editorial description row — the why-this-file-exists line. sits below the filename/meta on its own row, spanning the full grid width so the prose has room to breathe. serif register tied to the page's narrative copy. width capped at 44ch for editorial discipline: short measures force prose to be precise and let the registry breathe between filenames and their commentary. */ .source-entry-desc { grid-column: 1 / -1; margin: 0; font-family: var(--serif); font-size: clamp(13.5px, 1.65vw, 15px); font-weight: 300; line-height: 1.5; color: var(--fg2); max-width: 44ch; } /* quiet role label placed inline at the head of the description for trust-critical files. text only — no badge, no border, no colour wash. mono uppercase tracking signals "this is a system role, not editorial prose"; the trailing middot lets the prose flow without a hard break. */ .source-entry-role { display: inline; margin-inline-end: 0.45rem; font-family: var(--mono); font-size: 0.62rem; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase; color: var(--fg3); opacity: 0.82; white-space: nowrap; } .source-entry-role::after { content: " ·"; margin-inline-start: 0.1rem; color: color-mix(in srgb, var(--fg3) 55%, transparent); } /* critical-file row. quietly distinguish the few files that carry the trust system (signed manifest, detached signature, public signing key, canonical identity record, disclosure surface). the brief forbids badges; the distinction is a hair more contrast on the role kicker. no extra chrome, no left border — restraint is the point. the filename already reads as primary ink across the registry, so no override there. */ .source-entry[data-critical="true"] .source-entry-role { color: var(--fg2); opacity: 1; } /* phase 61 · .source-philosophy retired — explanatory density removed in favour of the catalogue itself. */ /* editions lineage — a quiet bibliographic ledger of signed editions, sitting between the registry and the related-records nav. inherits the verify-card chrome via the .source-page trust-system selector so it reads as a paper sheet. each record is a two-column micro-grid: the edition slug on the left, an annotation note + a linked artefact list on the right. */ .source-editions { /* phase 60a · extra breathing above the lineage so it reads as a publication shelf, not a continuation of the registry. */ margin-block-start: clamp(2.8rem, 7vw, 4.2rem); margin-block-end: 0; /* phase 60 · same tightening as .source-verification so the two archival panels read as siblings, flush with the registry above. */ padding: clamp(1rem, 3.4vw, 1.45rem); } .source-editions-list { margin: 0; display: grid; gap: 0; /* phase 60a · drop the top hairline; the panel's own border and the .verify-card__header already establish the upper bound. bibliographic, not tabular. */ border-top: 0; } .source-edition { display: grid; grid-template-columns: minmax(6rem, max-content) minmax(0, 1fr); column-gap: clamp(1rem, 3vw, 1.6rem); align-items: baseline; padding-block: 0.75rem; border-bottom: 1px solid var(--rule); } .source-edition:last-child { border-bottom: 0; } .source-edition-id { margin: 0; font-family: var(--mono); /* phase 60 · invert hierarchy. edition slug becomes the dominant element of the row, not the artefact list. larger size, full primary ink colour, slight weight bump. annotation registers fall in beneath. */ font-size: clamp(0.92rem, 2.4vw, 1.02rem); font-weight: 600; letter-spacing: 0.06em; color: var(--fg); font-variant-numeric: tabular-nums; } .source-edition-id time { color: inherit; } /* phase 61 · the right column now stacks a serif annotation note above a mono artefact list. annotation says "current signed release"; the artefacts are canonical links. publication history register, not a tabular row. */ .source-edition-meta { margin: 0; display: grid; gap: 0.35rem; min-width: 0; } .source-edition-note { margin: 0; font-family: var(--serif); font-size: clamp(13.5px, 1.7vw, 15px); font-weight: 300; font-style: italic; line-height: 1.45; color: var(--fg2); max-width: 44ch; } .source-edition-artefacts { margin: 0; font-family: var(--mono); /* phase 60 · drop one tier smaller + reduce opacity so the canonical artefact links register as tertiary annotation, not a competing label row. */ font-size: 0.62rem; line-height: 1.6; letter-spacing: 0.05em; color: var(--fg3); opacity: 0.82; word-break: break-word; overflow-wrap: anywhere; font-variant-ligatures: none; } .source-edition-artefacts a { color: var(--fg2); text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--fg2) 28%, transparent); transition: color 0.2s, border-bottom-color 0.2s; } .source-edition-artefacts a:hover, .source-edition-artefacts a:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); outline: 0; } .source-edition-artefacts code { font-family: inherit; font-size: inherit; background: transparent; padding: 0; } .source-editions { /* phase 60a · softer card edges than verify-card defaults so the panel reads as an archival insert, not a ui component. */ border-color: color-mix(in srgb, var(--ink) 5%, transparent); background: color-mix(in srgb, var(--surface-archival) 88%, var(--bg) 12%); box-shadow: none; } /* phase 61 · related-records nav. closes the page quietly. one small uppercase mono kicker, three inline links separated by middots. no card, no border, no explanatory paragraph — cryptographic trust and page-level provenance live on their own surfaces. */ .source-related { margin-block-start: clamp(2.4rem, 5vw, 3.2rem); margin-block-end: clamp(2rem, 4vw, 2.6rem); padding: 0; } .source-related-title { margin: 0 0 0.75rem; font-family: var(--mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase; color: var(--fg3); } .source-related-nav { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.6rem 1.1rem; font-family: var(--serif); font-size: clamp(15px, 1.9vw, 17px); line-height: 1.4; } .source-related-nav a { color: var(--fg); text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--fg) 24%, transparent); transition: color 0.2s, border-bottom-color 0.2s; } .source-related-nav a:hover, .source-related-nav a:focus-visible { color: var(--accent-text); border-bottom-color: var(--accent-text); outline: 0; } @media (max-width: 540px) { .source-entry { grid-template-columns: 1fr; row-gap: 0.35rem; padding-block: 0.95rem; } .source-entry-meta { justify-self: start; text-align: left; } .source-entry-desc { max-width: none; } .source-edition { grid-template-columns: 1fr; row-gap: 0.2rem; padding-block: 0.8rem; } } /* citation drawer. tiny, non-modal, opened directly above the footer cite button and left-aligned to it. position is computed by cite.js on open; css only owns shape, not placement. closes on escape, outside click, or after any action. no focus trap. */ .cite-drawer { position: fixed; z-index: 160; width: min(220px, calc(100vw - 24px)); border: 1px solid var(--bd); background: var(--card); padding: 10px 12px; display: grid; gap: 6px; } .cite-drawer[hidden] { display: none; } .cite-drawer button, .cite-drawer a { appearance: none; background: none; border: 0; padding: 6px 0; font-family: var(--mono); font-size: 10.5px; color: var(--fg3); text-align: left; text-decoration: none; cursor: pointer; } .cite-drawer button:hover, .cite-drawer a:hover, .cite-drawer button:focus-visible, .cite-drawer a:focus-visible { color: var(--fg); } /* Verify-live disclosure · gives the summary the same hierarchy as the page so it doesn't read as a tiny disclosure widget. */ .verify-disclosure { margin: clamp(24px, 5vh, 36px) 0 0; } .verify-disclosure summary { cursor: pointer; list-style: none; display: grid; grid-template-columns: 1.1em minmax(0, 1fr); column-gap: 0.45em; align-items: baseline; padding: 4px 0; } .verify-disclosure summary::-webkit-details-marker { display: none; } .verify-disclosure summary::before { content: "+"; grid-column: 1; font-family: var(--mono); font-size: 0.72em; color: var(--fg3); transform: none; } .verify-disclosure[open] summary::before { content: "−"; transform: none; } /* Two-line title + subtitle stacks in column 2 */ .verify-disclosure summary .verify-title, .verify-disclosure summary .verify-subtitle { grid-column: 2; } .verify-title { font-family: var(--serif); font-size: clamp(18px, 4.8vw, 22px); font-weight: 400; color: var(--fg); display: inline; } .verify-subtitle { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.06em; color: var(--fg3); } .page-subtitle { font-family: var(--serif); font-size: clamp(14px, 1.4vw, 17px); font-weight: 400; color: var(--fg); margin-top: 2.5em; margin-bottom: 0.5em; } .page-back { display: inline-block; /* subtle archival navigation marker, but not so compressed that the page feels clipped at the bottom. */ margin-top: clamp(24px, 4vh, 40px); font-family: var(--mono); font-size: 11px; color: var(--fg3); text-decoration: none; letter-spacing: 0.04em; opacity: 0.84; transition: color 0.2s, opacity 0.2s; } .page-back:hover, .page-back:focus-visible { color: var(--fg); opacity: 1; } pre { font-family: var(--mono); overflow-x: auto; -webkit-overflow-scrolling: touch; padding: 1em 1.2em; color-scheme: light; -webkit-appearance: none; background-color: var(--bg2); border: 1px solid var(--bd); border-radius: 4px; font-size: 0.72em; line-height: 1.7; margin: 0; color: var(--fg); } code { font-family: var(--mono); } .code-cmd { color: var(--accent-text); } .code-str { color: var(--code-string); } .code-var { color: var(--code-var); font-weight: 500; } /* progressive disclosure the site uses a single .trust-disclosure component for every collapsible section (verify chooser previously, integrity verify- release-locally now). the trust-disclosure rules above own all the chrome — list-style suppression, the right-aligned +/- marker, the hairline rhythm. the legacy generic `summary` rules that lived here previously leaked a ▸ triangle and a dotted border onto the trust-disclosure, making it read red/technical; they are intentionally removed. new
    elsewhere should either use .trust-disclosure or declare their own scoped rules. */ /* access modal */ .modal-overlay { position: fixed; inset: 0; z-index: 200; background: rgba(31, 30, 28, 0); backdrop-filter: blur(0); -webkit-backdrop-filter: blur(0); display: flex; align-items: center; justify-content: center; /* full-screen overlay: pad each side by max(24px, safe-area-inset) so the modal content never sits under the notch / home-indicator in `viewport-fit=cover` mode. the cite drawer is JS-positioned so it does not need this here. */ padding-top: max(24px, var(--safe-top)); padding-right: max(24px, var(--safe-right)); padding-bottom: max(24px, var(--safe-bottom)); padding-left: max(24px, var(--safe-left)); pointer-events: none; /* backdrop fades on its own slower curve so the scrim "settles" after the paper card has arrived. matches the brief's "paper emerging from fog" target. */ transition: background 260ms cubic-bezier(.22,.61,.36,1), backdrop-filter 260ms cubic-bezier(.22,.61,.36,1), -webkit-backdrop-filter 260ms cubic-bezier(.22,.61,.36,1); } .modal-overlay.active { /* darker warm scrim so the page recedes noticeably under the modal. --overlay-scrim is the single source of truth (both modals consume it); bumped to ~0.46 opacity so the publication card reads as a lifted artefact rather than floating on a translucent veil. blur widened to 6 px to reinforce the fog effect under the scrim. */ background: var(--overlay-scrim); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); pointer-events: auto; } .modal { /* the panel sits on paper (the artefact in front of the scrim). raised-high paper — slightly lighter than --surface-card so an overlay opened above either page surface still reads as a lifted sheet, not as a flat plate. phase 41 · 2px outer radius matches the radius scope (cards, trust mark, verify-card). padding tightened ~15% so the panel reads as a denser editorial insert rather than a generously spaced ui modal. phase 46 · overlay panels promote to var(--radius-panel) (10px) — slightly more present than the card radius so the modal reads as a lifted panel, not a card. phase 62 · padding tightened a further ~12% so the project modal sits as a private studio note rather than a generously spaced ui panel. less empty lower space. */ background: var(--paper-raised-high); border: 1px solid var(--rule); border-radius: var(--radius-panel); box-shadow: none; padding: clamp(22px, 3.4vw, 34px); max-width: 440px; width: 100%; position: relative; opacity: 0; transform: translateY(7px); /* paper emerging from fog: opacity rises faster than transform so the card materialises before it finishes settling. easing chosen to feel decelerating without spring overshoot. */ transition: opacity 150ms cubic-bezier(.22,.61,.36,1), transform 200ms cubic-bezier(.22,.61,.36,1); } .modal-overlay.active .modal { opacity: 1; transform: translateY(0); } /* editorial modal · no accent rail. the card carries no alert state. */ .modal::before { content: none; display: none; } .modal-label { font-family: var(--mono); font-size: 10.5px; font-weight: 500; letter-spacing: 0.09em; text-transform: uppercase; color: var(--fg3); /* phase 62 · tighter trailing gap so the panel reads as a private studio note. */ margin-bottom: 16px; } .modal-text { font-family: var(--serif); font-size: clamp(17px, 4.6vw, 21px); font-weight: 400; line-height: 1.5; color: var(--fg2); /* phase 41 · hairline rule below the body text anchors the cta visually so it reads as the action of the panel, not a quiet trailing link. phase 62 · spacing tightened ~15% so the cta reads as the immediate next gesture, not a separated block. */ margin-bottom: clamp(16px, 3vw, 22px); padding-bottom: clamp(16px, 3vw, 22px); border-bottom: 1px solid var(--rule); } /* phase 62 · the project modal cta is no longer a directional "→ trent@trentpower.fr" link. it reads as a two-line studio note: a quiet mono label above ("access by request"), the email on the second line. less startup, more archival. */ .modal-cta { font-family: var(--mono); font-size: 12px; font-weight: 400; letter-spacing: 0.04em; color: var(--accent-text); text-decoration: none; display: inline-flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 6px 0; border-bottom: 1px solid var(--bd); transition: border-color 0.2s, color 0.2s; } .modal-cta:hover, .modal-cta:focus-visible { border-color: var(--accent-text); outline: 0; } .modal-cta-label { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--fg3); } /* visible email below the label — keeps the action self-evident on every locale (the email is the same in all five). */ .modal-cta-email { font-family: var(--mono); overflow-wrap: anywhere; word-break: normal; } /* legacy text close , no longer rendered; the project modal now uses the top-right × pattern shared with the cite overlay. kept as a no-op so any cached html still styles cleanly if it appears. */ .modal-close { display: block; margin-top: 28px; font-family: var(--sans); font-size: 11px; font-weight: 400; letter-spacing: 0.02em; color: var(--fg3); background: none; border: none; cursor: pointer; padding: 8px 4px; transition: color 0.2s; } .modal-close:hover, .modal-close:focus-visible { color: var(--fg); } /* top-right × close , single visible close affordance for the project modal. mirrors .cite-modal-close-x so the modal family is consistent. phase 41 · colour bumped fg3 → fg2 (same warm grey, ~6% darker) so the close glyph reads as deliberate authoritative chrome rather than a casual tertiary marker. */ .modal-close-x { position: absolute; top: 0.5rem; right: 0.7rem; width: 1.8rem; height: 1.8rem; display: inline-flex; align-items: center; justify-content: center; font-family: var(--sans); font-size: 1.3rem; line-height: 1; color: var(--fg2); background: transparent; border: 0; cursor: pointer; padding: 0; transition: color 0.2s; z-index: 1; } .modal-close-x:hover, .modal-close-x:focus-visible { color: var(--accent-text); outline: 0; } /* cite overlay , page record same modal family as the view project modal; wider card to fit a metadata table and an actions strip. lazily injected by cite.js. */ /* cite modal · publication record card. five elements top-down: close × (mobile only) · header (eyebrow + title + lede) · meta
    · primary actions (verify · view source) · secondary actions (copy citation · print). the card is paper lifted by colour, not by chrome — no shadow, no border. */ .modal.cite-modal { /* shares the .modal family treatment with the homepage view-project modal: same background, same 1 px rule border, same panel radius, same motion (inherited from .modal). only the cite-specific layout overrides live here — text alignment, padding, max-width, internal gap. consistency across both modals is the contract. */ text-align: left; padding: clamp(22px, 3.6vw, 32px); padding-right: clamp(24px, 4vw, 36px); max-width: 26rem; width: min(100%, 26rem); max-height: 92vh; overflow-y: auto; display: flex; flex-direction: column; gap: clamp(14px, 2.4vw, 20px); } /* close × — mobile-only affordance; desktop relies on click-outside + escape (the brief's preference). mobile placement is a tiny dot in the top-right; opacity stays low until hovered / focused so the card surface reads as paper, not as a control panel. */ .cite-modal-close-x { position: absolute; top: 0.55rem; right: 0.6rem; width: 1.6rem; height: 1.6rem; display: inline-flex; align-items: center; justify-content: center; appearance: none; -webkit-appearance: none; background: transparent; border: 0; font: 15px/1 var(--mono); color: color-mix(in srgb, var(--fg3) 55%, transparent); cursor: pointer; padding: 0; transition: color 180ms ease, opacity 180ms ease; } .cite-modal-close-x:hover, .cite-modal-close-x:focus-visible { color: var(--fg2); outline: 0; } @media (min-width: 700px) { /* desktop: the visible × is retired. escape and click-outside both close the overlay (wired by app-enhance's TP_OVERLAY). keeping it on tap-only surfaces where there is no escape key and click-outside is harder with one thumb. */ .cite-modal-close-x { display: none; } } .cite-modal-header { display: flex; flex-direction: column; gap: 8px; margin: 0; } /* eyebrow · the small-caps publication mark above the title. wider tracking, lighter opacity. quieter than the surrounding type. */ .cite-modal-kicker { font: 500 9px/1.1 var(--mono); letter-spacing: 0.2em; text-transform: uppercase; color: color-mix(in srgb, var(--fg3) 65%, transparent); margin: 0; } /* title · serif, slightly smaller than before so the modal does not feel like a hero panel. balance hint keeps the mobile wrap clean. */ .cite-modal-title { font: 300 clamp(20px, 3.8vw, 24px)/1.22 var(--serif); letter-spacing: -0.015em; color: var(--fg); margin: 0; text-wrap: balance; } /* lede · softer contrast, one-sentence descriptor, narrower measure. */ .cite-modal-lede { font: 300 13.5px/1.5 var(--serif); color: var(--fg2); margin: 0; max-width: 32ch; } /* meta · single quiet row reading as an archival record. mono labels are no longer uppercase-shouting; the type carries the system register without raising its voice. */ .cite-modal-meta { margin: 0; display: flex; flex-direction: column; gap: 3px; font: 500 10.5px/1.45 var(--mono); letter-spacing: 0.04em; color: var(--fg3); font-variant-numeric: tabular-nums lining-nums; } .cite-modal-meta-row { display: flex; align-items: baseline; gap: 8px; margin: 0; } .cite-modal-meta-row dt { color: color-mix(in srgb, var(--fg3) 75%, transparent); margin: 0; } .cite-modal-meta-row dd { margin: 0; color: var(--fg2); } /* actions · two tiers with a subtle hierarchy. primary (verify · view source) reads as the inspectable record; secondary (copy citation · print) reads as utility. left-aligned stack; no oversized click target; restrained hover. */ .cite-modal-actions { display: flex; flex-direction: column; gap: 10px; margin: clamp(2px, 1vw, 8px) 0 0; padding: 0; } .cite-modal-actions-row { display: flex; flex-direction: column; align-items: flex-start; gap: 0; } .cite-modal-actions-row--secondary { margin-top: 6px; padding-top: 8px; border-top: 1px solid color-mix(in srgb, var(--rule) 35%, transparent); } .cite-modal-action { appearance: none; -webkit-appearance: none; font: 400 14px/1.4 var(--serif); color: var(--fg); background: transparent; border: 0; text-align: left; text-decoration: none; padding: 8px 0; cursor: pointer; border-bottom: 1px solid transparent; align-self: flex-start; transition: color 180ms ease, border-color 180ms ease; } .cite-modal-action--primary { color: var(--fg); font-weight: 400; } .cite-modal-action--secondary { font: 400 13px/1.4 var(--serif); color: var(--fg2); } .cite-modal-action:hover, .cite-modal-action:focus-visible { color: var(--accent); border-bottom-color: color-mix(in srgb, var(--accent) 55%, transparent); outline: 0; } .cite-modal-action[data-state="copied"] { color: var(--accent); } /* visually-hidden · aria-live region for copy announcements. */ .cite-overlay .visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; padding: 0; margin: -1px; } /* reduced motion */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; /* keep: wcag 2.3.3 motion-reduction override */ transition-duration: 0.01ms !important; /* keep: wcag 2.3.3 motion-reduction override */ } .hero-name, .hero-statement, .hero-body, .trust-mark, .principle, .trajectory-item, .project-card, .js .principle, .js .trajectory-item, .js .project-card { opacity: 1; transform: none; transition: none; } } /* print */ @media print { .nav { display: none; } section { padding: 40px 0; } .hero { min-height: auto; } } } @layer components { /* Security-page architecture diagram. the swaps to the mobile portrait variant under 600px. on desktop the figure widens beyond the prose column but stays visually centred in the viewport. */ .architecture-figure { /* hug the "1. architecture" summary above; rely on the disclosure's own bottom rhythm for the gap below. */ margin: 0 auto clamp(20px, 3vw, 32px); } .architecture-picture { display: block; } .page-diagram { display: block; width: 100%; height: auto; max-width: 100%; margin: 0 auto; } @media (min-width: 900px) { .architecture-figure { width: min(92vw, 1120px); margin-left: auto; margin-right: auto; transform: none; } } /* /security/ as a posture document — open editorial sections with real h2 headings, soft hairline rules between them, no accordion chrome. phase 62 · section rhythm and sub-section spacing widened ~7% and the inter-section rule softened to var(--rule) so spacing, not lines, carries the hierarchy on this denser page. */ .security-section { margin: clamp(44px, 7vw, 72px) 0; padding: clamp(32px, 5vw, 42px) 0 0; border-top: 1px solid var(--rule); border-bottom: 0; min-height: 0; } .security-section:first-of-type { margin-top: clamp(32px, 4.5vw, 44px); padding-top: 0; border-top: 0; } .security-section-heading { font-family: var(--serif); font-size: clamp(22px, 4.4vw, 30px); font-weight: 400; line-height: 1.2; color: var(--fg); margin: 0 0 clamp(16px, 2.2vw, 24px); letter-spacing: -0.005em; } .security-section .security-subheading { /* quieter subhead — won't compete with the section h2. phase 62 · breathing above + below loosened so the subsection groupings read as composed rather than compressed. */ font-family: var(--mono); font-size: 0.76rem; font-weight: 400; letter-spacing: 0.025em; color: var(--fg2); margin: clamp(1.7rem, 3vw, 2.1rem) 0 clamp(0.55rem, 1vw, 0.7rem); text-transform: none; } .security-section .security-subheading strong { font-weight: 400; } /* phase 44 · semantic subsections inside a .security-section. threat-model and controls now use real
    +

    groupings instead of

    subheadings. neutralise the global `section { padding ; border-bottom }` rule so these inner blocks remain flat editorial passages, not nested bordered panels. spacing comes from .security-subheading and the list rhythm above. */ .security-section .security-subsection { padding: 0; border-bottom: 0; min-height: 0; margin: 0; } .security-architecture-note { font-family: var(--mono); font-size: 0.78rem; letter-spacing: 0.02em; color: var(--fg2); margin: clamp(18px, 2.5vw, 24px) 0 0; line-height: 1.55; } /* phase 42 · the bespoke .architecture-card / .architecture-card-dl / .architecture-row component family was retired in favour of the shared `.verify-card` + `.record-grid` micro-grid (now scoped to `.security-page` as well). the security architecture section reuses the canonical archival-object pattern, so the visual register is one family across verify / sw-reset / security. */ /* Security-page editorial lists · signifier prose with proper specificity (no !important needed; .page-body scope wins the cascade). */ .page-body ul.i18n-list { font-family: var(--serif); font-size: 1rem; font-weight: 400; line-height: 1.6; list-style: disc; padding-left: 1.25rem; margin: 0.5rem 0 1rem; } .page-body ul.i18n-list li { font-family: var(--serif); font-size: 1rem; font-weight: 400; line-height: 1.6; margin-bottom: 0.2rem; } .page-body details p[data-i18n$="_heading"], .page-body details strong[data-i18n$="_heading"] { font-family: var(--serif); font-size: 0.9rem; font-weight: 400; color: var(--fg2); margin: 1rem 0 0.2rem; } /* bullets inside .security-section share the same calm refinement the prior .security-disclosure block had — list-style:disc, smaller marker, refined serif body. phase 62 · inter-item spacing widened so the threat-model + controls lists read as composed cadence, not stacked rows. */ .page-body .security-section ul.i18n-list { padding-left: 1.05rem; margin: clamp(0.75rem, 1.5vw, 1rem) 0 clamp(1.35rem, 2.4vw, 1.6rem); list-style: disc; } .page-body .security-section ul.i18n-list li { font-family: var(--serif); font-size: clamp(15px, 4vw, 17px); line-height: 1.65; margin-bottom: clamp(0.5rem, 1vw, 0.65rem); color: var(--fg); } .page-body .security-section li::marker { color: var(--fg3); font-size: 0.7em; } /* legacy ".trajectory-chapter ul" reset retired — the new trajectory chronology has no nested lists. .trajectory-item carries its own list-style reset downstream. */ /* ─── language switch · viewport-anchor stabilisation ── while a language switch is in flight, suppress all transitions and animations so the page does not visibly jump. the .is-language-switching class is added by app.js around the text-replacement step and removed once layout settles (raf × 2). */ } @layer overrides { html.is-language-switching { scroll-behavior: auto !important; } html.is-language-switching .principle, html.is-language-switching .trajectory-item, html.is-language-switching .project-card, html.is-language-switching .hero-name, html.is-language-switching .hero-statement, html.is-language-switching .hero-body, html.is-language-switching .trust-mark { transition: none !important; animation: none !important; } html.is-language-switching .nav { transition: none !important; } /* ─── source-reader: controlled reader-box width ─────────────── id hook (#source-view-root) caps the source-reader column at a book-page proportion so the code panel never stretches awkwardly on very wide viewports. chrome (intro, actions, document map, footer) and the code layout share the same max-width so nothing drifts off-axis. lives here in @layer overrides because the id selector belongs to the overrides register per the css-architecture brief (l4). */ .source-reader-page #source-view-root { max-width: 72rem; margin-inline: auto; } /* ─── anchor landing offsets · home section ids ──────────────── native anchor navigation lands the section heading below the sticky header with elegant breathing room. bare id selectors here in @layer overrides per the brief; specificity (1,0,0) is fine because the rule is intentionally narrow (four ids, one property, no other rule competes for these targets). no js scrollintoview, no scrollto, no preventdefault on anchor clicks — inithomenav() in app.js closes the menu but lets the browser perform native anchor navigation. css owns the offset register. */ @media (max-width: 760px) { #approach, #trajectory, #projects, #contact { scroll-margin-top: var(--anchor-offset-mobile); } } @media (min-width: 761px) { #approach, #trajectory, #projects, #contact { scroll-margin-top: var(--anchor-offset-desktop); } } } @layer components { /* ── /source/view/ — source code reader ──────────────────────── archival inspection surface. warm paper background, mono type, muted line numbers. intentionally not ide-like. */ /* source code sizing — single source of truth. token classes change colour only; no size, weight, or spacing overrides. spacing rhythm — three tokens, used everywhere in this surface so that gaps between labels, annotation lines, and code groups read as one mathematical system. browser defaults are forbidden. the section-divider rhythm uses --source-rule-rise above the rule and --source-rule-fall below, so every annotation block (author comment, structural tag, generated) breathes identically. */ .source-reader-page { --source-code-size: clamp(0.78rem, 2.2vw, 0.92rem); --source-code-line-height: 1.55; --source-gap-xs: 0.45rem; --source-gap-sm: 0.85rem; --source-gap-md: 1.4rem; --source-rule-rise: 2.75rem; --source-rule-fall: 1.25rem; } /* the matching reader-box max-width rule lives at the top of this file in the existing top-level @layer overrides block (search "#source-view-root"). ID-keyed selectors must live in @layer overrides per l4 of the css- architecture validator. */ /* reader intro: eyebrow + filename + description + meta. spacing on this surface uses --source-gap-xs / sm / md only — no ad-hoc rem values, no browser defaults. that is the rhythm contract. */ .source-reader-page .reader-intro { margin-block-end: var(--source-gap-md); } .source-reader-page .reader-intro .eyebrow { font-family: var(--mono); font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase; color: var(--fg3); margin: 0 0 var(--source-gap-xs) 0; } .source-reader-page .reader-intro h1 { font-family: var(--mono); font-size: clamp(1rem, 2.5vw, 1.25rem); font-weight: 400; color: var(--fg); margin: 0 0 var(--source-gap-xs) 0; line-height: 1.3; } .source-reader-page .reader-intro h1 code { font-family: inherit; } .source-reader-page .reader-description { font-family: var(--serif); font-size: clamp(0.9rem, 2.2vw, 1rem); color: var(--fg2); margin: 0 0 var(--source-gap-xs) 0; line-height: 1.6; } .source-reader-page .reader-meta { font-family: var(--mono); font-size: 0.75rem; color: var(--fg3); margin: var(--source-gap-xs) 0 0; } .source-reader-page .reader-meta abbr { text-decoration: none; cursor: default; } .source-reader-page .reader-meta-date { font-family: var(--mono); font-size: 0.75rem; color: var(--fg3); margin: var(--source-gap-xs) 0 0; } .source-reader-page .reader-provenance { font-family: var(--mono); font-size: 0.6875rem; color: var(--fg3); margin: var(--source-gap-sm) 0 0; letter-spacing: 0.01em; } .reader-provenance-label { text-transform: uppercase; letter-spacing: 0.12em; font-size: 0.6rem; opacity: 0.75; margin-inline-end: 0.35em; } .reader-provenance-link { color: var(--fg3); text-decoration: none; } .reader-provenance-link:hover, .reader-provenance-link:focus-visible { color: var(--fg2); } /* reader-intent — one quiet italic-serif conceptual line under the file description. interpretive content: hidden in raw mode (see body[data- source-mode="raw"] .reader-intent rule above). */ .source-reader-page .reader-intent { font-family: var(--serif); font-style: italic; font-size: clamp(0.85rem, 2vw, 0.95rem); color: var(--fg3); margin: var(--source-gap-xs) 0 0; line-height: 1.5; opacity: 0.85; } /* source integrity block — small monospace ledger anchoring the reader to a verifiable archival artefact. canonical · edition · sha-256 short · signed release. quiet two-column key/value layout, no fills, no rules. */ .source-reader-page .reader-integrity { display: grid; grid-template-columns: max-content 1fr; column-gap: var(--source-gap-sm); row-gap: var(--source-gap-xs); margin: var(--source-gap-sm) 0 0; font-family: var(--mono); font-size: 0.66rem; color: var(--fg3); letter-spacing: 0.04em; line-height: 1.4; } .source-reader-page .reader-integrity-key { margin: 0; text-transform: uppercase; letter-spacing: 0.12em; opacity: 0.7; } .source-reader-page .reader-integrity-val { margin: 0; color: var(--fg2); word-break: break-all; } .reader-integrity-val--mono { font-variant-numeric: tabular-nums lining-nums; } /* action row: canonical · verify · raw · copy code · source / annotated · wrap lines */ .source-reader-page .reader-actions { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 0; margin-block-end: var(--source-gap-md); } .reader-action { font-family: var(--mono); font-size: 0.75rem; color: var(--fg2); background: none; border: none; padding: 0; cursor: pointer; text-decoration: none; transition: color 0.15s ease; } .reader-action:hover, .reader-action:focus-visible { color: var(--accent); } .reader-action + .reader-action::before, .reader-action + .reader-view-toggle::before, .reader-view-toggle + .reader-action::before { content: ' · '; color: var(--fg3); pointer-events: none; } .reader-action--wrap[aria-pressed="true"] { color: var(--accent); } /* code shell — horizontal scroll wrapper, never overflows the page. on mobile (≤700px) the wrap toggle defaults to on (set by source-view.js), so the shell width simply fills the viewport with no inner scroll. on desktop, the shell sits inside the 72rem reader-box and any line that exceeds the column scrolls within this wrapper, never the page. */ .code-shell { width: 100%; max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; border: 1px solid color-mix(in srgb, var(--bd-soft) 55%, transparent); border-radius: 1px; background: var(--surface-archival); } .code-reader { margin: 0; padding-block: clamp(1.15rem, 3vw, 1.6rem); /* on mobile the code panel respects the iphone home-bar / notch insets so the reader never feels optically pressed against the viewport edge. */ padding-inline: max(clamp(0.875rem, 3vw, 1.25rem), env(safe-area-inset-left)); font-family: var(--mono); font-size: var(--source-code-size); line-height: var(--source-code-line-height); background: transparent; overflow-x: visible; } /* hard size lock — every character in the code panel shares identical metrics. token classes may change colour only; no size, weight, letter-spacing, or text-transform overrides are permitted. */ .code-reader, .code-reader code, .code-reader span, .code-line, .line-code, .line-code * { font-family: var(--mono); font-size: var(--source-code-size); line-height: var(--source-code-line-height); font-weight: 400; letter-spacing: 0; text-transform: none; } .code-reader code { display: block; } /* line layout: 4ch number column + flexible code column. align-items: start keeps line number at top of wrapped lines. */ .code-line { display: grid; grid-template-columns: 4ch minmax(0, 1fr); column-gap: 0.8rem; align-items: start; } /* line-number is an tag — reset link styles, registration-mark quiet */ a.line-number { position: relative; display: block; text-align: right; text-decoration: none; color: var(--fg3); user-select: none; -webkit-user-select: none; opacity: 0.55; flex-shrink: 0; font-variant-numeric: tabular-nums lining-nums; } /* §/number crossfade on line hover */ .ln-num, .ln-sym { display: block; text-align: right; transition: opacity 0.12s; } .ln-sym { position: absolute; inset: 0; opacity: 0; color: color-mix(in srgb, var(--accent) 55%, var(--fg3)); font-style: normal; } .code-line:hover a.line-number { opacity: 0.85; } .code-line:hover .ln-num { opacity: 0; } .code-line:hover .ln-sym { opacity: 1; } /* wrap state — default off: preserve original line integrity, horizontal scroll allowed. when on: wrap within the code column; continuation indents under code, not number. */ .code-reader:not(.is-wrapped) .line-code { white-space: pre; } .code-reader.is-wrapped .line-code { white-space: pre-wrap; overflow-wrap: anywhere; } .line-code { color: var(--fg); } /* focus line — warm parchment register, not editor blue. the active highlight is interpretive (it is a citation overlay, not source content), so it fades after 2s (controlled by source-view.js) and collapses to a persistent gutter marker: a 2px paper-warm rule along the left edge of each cited line. the citation is still locatable without the page shouting. */ .code-line--range-active { background: color-mix(in srgb, var(--accent) 8%, color-mix(in srgb, var(--paper) 60%, transparent)); outline: 1px solid color-mix(in srgb, var(--accent) 20%, transparent); outline-offset: 1px; border-radius: 2px; min-width: 100%; transition: background-color var(--transition-quiet), outline-color var(--transition-quiet); } .code-line--range-marked { position: relative; } .code-line--range-marked::before { content: ''; position: absolute; inset-block: 0; inset-inline-start: -0.4rem; width: 2px; background: color-mix(in srgb, var(--accent) 60%, var(--fg3)); opacity: 0.7; pointer-events: none; } /* single-line :target retains a subtler version of the active highlight so direct hits still feel anchored. js adds .code-line--range-active for fade behaviour; this rule covers the cold-load case where only the url hash is in play. */ .code-line:target { background: color-mix(in srgb, var(--accent) 7%, transparent); border-radius: 2px; outline: 1px solid color-mix(in srgb, var(--accent) 18%, transparent); outline-offset: 1px; min-width: 100%; } /* in-source links — when the source viewer detects a routed url inside the rendered code (href="/privacy/", https://trentpower.fr/styles.css, etc.) it splits the matching text node and inserts an anchor pointing at the corresponding source mirror in this same reader. the visual register is intentionally quiet: dotted underline, current colour, no token bracket. the goal is "this can be opened", not "this is a hyperlink". focus state lifts to the accent for one frame so keyboard readers can locate the active target. */ a.source-link, a.source-link:visited { color: inherit; text-decoration: underline dotted color-mix(in srgb, var(--accent) 35%, var(--bd-soft)); text-underline-offset: 3px; text-decoration-thickness: 1px; } a.source-link:hover { text-decoration-color: var(--accent); } a.source-link:focus-visible { outline: 1px solid color-mix(in srgb, var(--accent) 60%, transparent); outline-offset: 2px; border-radius: 1px; } /* selection toolbar — quiet floating control strip that appears only when the reader has at least one line selected. lower-right on desktop; sticky-bottom on mobile with the iOS home-indicator inset honoured. paper background, hairline border, mono labels. never glassy, never a code-host ui. */ .source-toolbar { position: fixed; right: var(--sp-4); bottom: var(--sp-4); z-index: 60; display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.6rem; background: var(--paper); border: 1px solid var(--bd-soft); border-radius: 12px; box-shadow: 0 6px 16px rgba(31, 30, 28, 0.10); font-family: var(--mono); font-size: 0.78rem; line-height: 1.2; color: var(--fg); } .source-toolbar[hidden] { display: none; } .source-toolbar__count { padding-inline: 0.35rem 0.55rem; color: var(--fg2); letter-spacing: 0.02em; } .source-toolbar__btn { appearance: none; -webkit-appearance: none; background: transparent; border: 1px solid var(--bd-soft); border-radius: 8px; padding: 0.32rem 0.7rem; font: inherit; color: inherit; cursor: pointer; min-height: 32px; } .source-toolbar__btn:hover { border-color: color-mix(in srgb, var(--accent) 30%, var(--bd-soft)); } .source-toolbar__btn:focus-visible { outline: 1px solid color-mix(in srgb, var(--accent) 60%, transparent); outline-offset: 2px; } @media (max-width: 700px) { .source-toolbar { left: var(--sp-3); right: var(--sp-3); bottom: calc(env(safe-area-inset-bottom, 0px) + var(--sp-3)); justify-content: space-between; } .source-toolbar__btn { min-height: 44px; padding: 0.4rem 0.8rem; } } /* visually-hidden aria-live region for selection announcements. the toolbar count already carries aria-live="polite" for the running selection size; this one is for transient confirmations ("copied 3 lines", "link copied"), kept off-screen so the layout stays calm. */ .source-announcer { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); border: 0; white-space: nowrap; } /* section divider — single unified rhythm for every annotation block (author comments, structural tags, generated markers). one contract: margin-block: var(--source-rule-rise) var(--source-rule-fall) so 2.75rem rises above the rule, 1.25rem falls below the label, no matter where the marker came from. author / structure / tier variants adjust only opacity and letter-spacing — never the rhythm itself. */ .section-divider, .code-reader .section-divider { display: block; margin-block: var(--source-rule-rise) var(--source-rule-fall); padding-block: var(--source-gap-sm) 0; padding-inline: 0; border-top: 1px solid color-mix(in srgb, var(--bd-soft) 22%, transparent); font-family: var(--mono); font-size: 0.68rem; line-height: 1.3; font-weight: 400; letter-spacing: 0.10em; text-transform: uppercase; color: var(--fg3); user-select: none; } .section-divider:first-child { margin-block-start: 0; border-top: none; padding-block-start: 0; } /* contrast lifted ~7–10% so labels sit calmly visible rather than washed out, while the author/structure differentiation is preserved. paint colour stays the same fg3 token; only opacity shifts. */ .section-divider--author { opacity: 0.92; } .section-divider--structure { opacity: 0.78; } /* density tiers — three editorial weights, sharing the same rhythm. tier-1 (major structural anchors: head / main / footer / body / header / structured): wider letter-spacing and a touch more font size — reads as a chapter-marker register. tier-2 (architectural notes): the baseline section-marker register. tier-3 lives on the gloss span (italic serif, quieter still) — the inline semantic note that follows the marker. no tier changes the vertical rhythm: the rise/fall contract is one for all three so the system never gains a second spacing register. the .code-reader prefix lifts the tier selector above the hard size lock on .code-reader span so the tier font-size actually applies. */ .code-reader .section-divider--tier-1 { font-size: 0.78rem; letter-spacing: 0.18em; font-weight: 500; } .code-reader .section-divider--tier-2 { font-size: 0.68rem; letter-spacing: 0.10em; } .section-divider-label { /* the label itself — inherits all type metrics from the divider. content provenance: this text is sourced from the file itself (an html comment or a structural tag). it is the canonical layer. */ display: inline; } /* ── collapsible source-section disclosure ───────────────────────── each generated annotated section is a real