Website for the Lede browser extension.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Redesign landing: section components, hero proof, install journey

+728 -530
+14
public/images/hero-illustration.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 750" fill="none" aria-hidden="true"> 2 + <rect width="1200" height="750" fill="#f5f0e8"/> 3 + <rect x="72" y="88" width="1056" height="574" rx="10" fill="#ede8de" stroke="#e0d8cc" stroke-width="1"/> 4 + <rect x="96" y="112" width="720" height="28" rx="6" fill="#e0d8cc"/> 5 + <rect x="96" y="156" width="1008" height="12" rx="4" fill="#e0d8cc" opacity="0.85"/> 6 + <rect x="96" y="184" width="880" height="12" rx="4" fill="#e0d8cc" opacity="0.7"/> 7 + <rect x="96" y="212" width="920" height="12" rx="4" fill="#e0d8cc" opacity="0.55"/> 8 + <rect x="620" y="320" width="460" height="300" rx="10" fill="#faf8f4" stroke="#F15B2F" stroke-width="1.5"/> 9 + <rect x="648" y="352" width="120" height="10" rx="3" fill="#F15B2F" opacity="0.35"/> 10 + <rect x="648" y="378" width="360" height="8" rx="3" fill="#c8c0b4"/> 11 + <rect x="648" y="396" width="320" height="8" rx="3" fill="#c8c0b4" opacity="0.85"/> 12 + <rect x="648" y="430" width="140" height="36" rx="8" fill="#F15B2F" opacity="0.18" stroke="#F15B2F" stroke-width="1"/> 13 + <rect x="668" y="444" width="72" height="8" rx="2" fill="#D94E27" opacity="0.9"/> 14 + </svg>
+21
src/components/SiteFooter.astro
··· 1 + --- 2 + interface Props { 3 + tangled: string; 4 + } 5 + 6 + const { tangled } = Astro.props; 7 + --- 8 + 9 + <footer class="site-footer"> 10 + <div class="wrap footer-inner"> 11 + <span class="font-display" style="font-weight: 700; color: var(--color-ink)">Lede</span> 12 + <div class="footer-links"> 13 + <a href={tangled} target="_blank" rel="noopener noreferrer">Repo on Tangled</a> 14 + <a href="#install">Install</a> 15 + <a href="#privacy">Privacy</a> 16 + <a href="#product">Product</a> 17 + <a href="#usage">Usage</a> 18 + <a href="#faq">FAQ</a> 19 + </div> 20 + </div> 21 + </footer>
+33 -31
src/components/SiteHeader.astro
··· 1 1 --- 2 - const tangled = "https://tangled.org/ellioth.co/summarizer-extension"; 2 + interface Props { 3 + tangled: string; 4 + } 5 + 6 + const { tangled } = Astro.props; 3 7 4 8 const links = [ 5 9 { href: "#privacy", label: "Privacy" }, 6 - { href: "#features", label: "Features" }, 10 + { href: "#product", label: "Product" }, 11 + { href: "#usage", label: "Usage" }, 7 12 { href: "#faq", label: "FAQ" }, 8 13 { href: tangled, label: "Repo", external: true }, 9 14 ] as const; 10 15 --- 11 16 12 17 <header class="site-header"> 13 - <div class="wrap site-header__inner"> 14 - <a class="brand-lockup" href="/"> 15 - <img 16 - class="brand-lockup__mark" 17 - src="/lightning.svg" 18 - width="28" 19 - height="48" 20 - alt="" 21 - /> 22 - <span>Lede</span> 23 - </a> 24 - <div class="site-header__actions"> 25 - <nav aria-label="Page"> 26 - <ul class="nav"> 27 - { 28 - links.map((link) => ( 29 - <li> 30 - <a 31 - href={link.href} 32 - {...(link.external ? { target: "_blank", rel: "noopener noreferrer" } : {})} 33 - > 34 - {link.label} 35 - </a> 36 - </li> 37 - )) 38 - } 39 - </ul> 40 - </nav> 41 - <a class="btn btn--primary btn--sm nav-install" href="#install">Install beta</a> 18 + <div class="wrap"> 19 + <div class="site-header__inner"> 20 + <a class="brand-lockup" href="/"> 21 + <img class="brand-lockup__mark" src="/lightning.svg" width="28" height="48" alt="" /> 22 + <span>Lede</span> 23 + </a> 24 + <div class="site-header__actions"> 25 + <nav aria-label="Page"> 26 + <ul class="nav"> 27 + { 28 + links.map((link) => ( 29 + <li> 30 + <a 31 + href={link.href} 32 + {...(link.external ? { target: "_blank", rel: "noopener noreferrer" } : {})} 33 + > 34 + {link.label} 35 + </a> 36 + </li> 37 + )) 38 + } 39 + </ul> 40 + </nav> 41 + <a class="btn btn--primary btn--sm nav-install" href="#install">Install beta</a> 42 + </div> 42 43 </div> 44 + <p class="site-header__tagline" aria-hidden="true">Beta · source install · Chrome and Firefox</p> 43 45 </div> 44 46 </header>
+24
src/components/sections/EditorialValue.astro
··· 1 + --- 2 + // Product story: editorial blocks, not a card grid. 3 + --- 4 + 5 + <section id="product" class="section section--editorial wrap" aria-labelledby="product-heading"> 6 + <header class="section__head"> 7 + <p class="eyebrow">Product</p> 8 + <h2 id="product-heading" class="heading-xl">Built for the tab in front of you</h2> 9 + </header> 10 + <div class="editorial-grid"> 11 + <article class="editorial-block"> 12 + <h3 class="heading-md">Verdict first</h3> 13 + <p>One action pulls the lede out of long articles, tickets, and threads so you can bail early or read with intent.</p> 14 + </article> 15 + <article class="editorial-block"> 16 + <h3 class="heading-md">Questions stay grounded</h3> 17 + <p>Follow-ups reference the page and the summary you already generated—no free-floating chat pretending it read for you.</p> 18 + </article> 19 + <article class="editorial-block"> 20 + <h3 class="heading-md">Your stack</h3> 21 + <p>Ollama native, OpenAI-compatible hosts, Groq, LM Studio—pick the model and base URL in settings instead of a locked vendor.</p> 22 + </article> 23 + </div> 24 + </section>
+27
src/components/sections/FAQCompact.astro
··· 1 + <section id="faq" class="section wrap" aria-labelledby="faq-heading"> 2 + <header class="section__head"> 3 + <p class="eyebrow">FAQ</p> 4 + <h2 id="faq-heading" class="heading-xl">When something blocks install</h2> 5 + </header> 6 + <div class="faq-stack"> 7 + <details class="faq-disclosure"> 8 + <summary>Cannot connect to localhost:11434</summary> 9 + <div class="faq-disclosure__body"> 10 + <p> 11 + Run <span class="kbd">ollama serve</span>, confirm models with <span class="kbd">ollama list</span>, and keep 12 + Native mode on <span class="kbd">http://localhost:11434</span> without <span class="kbd">/v1</span>. 13 + </p> 14 + </div> 15 + </details> 16 + <details class="faq-disclosure"> 17 + <summary>HTTP 403 or 405</summary> 18 + <div class="faq-disclosure__body"> 19 + <p> 20 + Wrong API mode for the URL: Native → no <span class="kbd">/v1</span>. OpenAI-compatible → include 21 + <span class="kbd">/v1</span> (for example <span class="kbd">http://localhost:11434/v1</span> for Ollama 22 + compatibility mode). 23 + </p> 24 + </div> 25 + </details> 26 + </div> 27 + </section>
+90
src/components/sections/HeroWithProof.astro
··· 1 + --- 2 + /** 3 + * Hero imagery: add optional optimized assets (recommended ~1600–2400px wide, 3:2 or 16:10). 4 + * Drop files as `public/images/hero.avif` and `public/images/hero.webp` to replace the illustration 5 + * for LCP; `<picture>` sources are wired below when those files exist. 6 + */ 7 + import { existsSync } from "node:fs"; 8 + import path from "node:path"; 9 + 10 + interface Props { 11 + tangled: string; 12 + } 13 + 14 + const { tangled } = Astro.props; 15 + 16 + const imagesDir = path.join(process.cwd(), "public", "images"); 17 + const hasAvif = existsSync(path.join(imagesDir, "hero.avif")); 18 + const hasWebp = existsSync(path.join(imagesDir, "hero.webp")); 19 + 20 + const fallbackSrc = "/images/hero-illustration.svg"; 21 + const alt = 22 + "Lede extension popup over a browser page: summarize and ask questions grounded in the active tab."; 23 + --- 24 + 25 + <section class="hero-mast" aria-labelledby="hero-heading"> 26 + <div class="wrap hero-mast__grid reveal-group"> 27 + <div class="hero-mast__copy"> 28 + <p class="eyebrow reveal-item" style="--i: 0">Page summarizer and chat · Chrome and Firefox</p> 29 + <h1 id="hero-heading" class="display-hero reveal-item" style="--i: 1">Don&apos;t bury the lede</h1> 30 + <p class="deck reveal-item" style="--i: 2"> 31 + Summarize and ask about <strong>this tab</strong> before you spend twenty minutes on noise. 32 + </p> 33 + <p class="lede lede--tight reveal-item" style="--i: 3"> 34 + Answers trace the page you have open—not a blank assistant waiting for a prompt. 35 + </p> 36 + <div class="actions reveal-item" style="--i: 4"> 37 + <a class="btn btn--primary" href="#install">Install (beta)</a> 38 + <a class="btn btn--secondary" href={tangled} target="_blank" rel="noopener noreferrer"> 39 + Extension repo on Tangled 40 + </a> 41 + </div> 42 + <p class="eyebrow reveal-item" style="--i: 5; margin-top: var(--space-md)">Keyboard</p> 43 + <p 44 + class="reveal-item" 45 + style="--i: 6; margin: 0; font-size: var(--text-sm); color: var(--color-ink-secondary)" 46 + > 47 + <span class="kbd">Ctrl</span> + <span class="kbd">Shift</span> + <span class="kbd">U</span> 48 + <span aria-hidden="true"> · </span> 49 + <span class="kbd">⌘</span> + <span class="kbd">Shift</span> + <span class="kbd">U</span> on macOS — 50 + <em>Summarize with Lede</em> 51 + </p> 52 + </div> 53 + <figure class="hero-proof reveal-item" style="--i: 3"> 54 + <div class="hero-proof__frame"> 55 + { 56 + hasAvif || hasWebp ? ( 57 + <picture> 58 + {hasAvif ? <source type="image/avif" srcset="/images/hero.avif" /> : null} 59 + {hasWebp ? <source type="image/webp" srcset="/images/hero.webp" /> : null} 60 + <img 61 + src={hasWebp ? "/images/hero.webp" : "/images/hero.avif"} 62 + width="1200" 63 + height="750" 64 + alt={alt} 65 + decoding="async" 66 + fetchpriority="high" 67 + /> 68 + </picture> 69 + ) : ( 70 + <img 71 + src={fallbackSrc} 72 + width="1200" 73 + height="750" 74 + alt={alt} 75 + decoding="async" 76 + fetchpriority="high" 77 + /> 78 + ) 79 + } 80 + </div> 81 + <figcaption class="hero-proof__caption"> 82 + <span class="beta-tag">Beta</span> 83 + <span class="hero-proof__capline"> 84 + Not in the Chrome Web Store or Firefox Add-ons yet—install from source, then wire Ollama or your API in 85 + settings. 86 + </span> 87 + </figcaption> 88 + </figure> 89 + </div> 90 + </section>
+84
src/components/sections/InstallJourney.astro
··· 1 + --- 2 + interface Props { 3 + tangled: string; 4 + } 5 + 6 + const { tangled } = Astro.props; 7 + --- 8 + 9 + <section id="install" class="section section--install reveal-group" aria-labelledby="install-heading"> 10 + <div class="wrap"> 11 + <header class="section__head section__head--install"> 12 + <p class="eyebrow reveal-item" style="--i: 0">Install</p> 13 + <h2 id="install-heading" class="heading-xl reveal-item" style="--i: 1">From repo to toolbar</h2> 14 + </header> 15 + 16 + <div class="journey-band journey-band--callout reveal-item" style="--i: 2"> 17 + <p class="journey-band__text"> 18 + <strong>Clone webai-summarizer on Tangled</strong>—not this marketing repo. You need a folder that contains 19 + <code class="kbd">manifest.json</code>. <code class="kbd">lede-website</code> is only this page. 20 + </p> 21 + </div> 22 + 23 + <div class="journey-strip reveal-item" style="--i: 3" aria-label="Three-step overview"> 24 + <div class="journey-strip__cell"> 25 + <span class="journey-strip__num" aria-hidden="true">1</span> 26 + <h3 class="journey-strip__title">Get the code</h3> 27 + <p class="journey-strip__body">Download or clone the extension repo so the project lives on disk.</p> 28 + <a href={tangled} target="_blank" rel="noopener noreferrer">Open Tangled repo</a> 29 + </div> 30 + <div class="journey-strip__cell"> 31 + <span class="journey-strip__num" aria-hidden="true">2</span> 32 + <h3 class="journey-strip__title">Choose a browser</h3> 33 + <p class="journey-strip__body">Chrome uses unpacked load; Firefox uses a temporary add-on.</p> 34 + </div> 35 + <div class="journey-strip__cell"> 36 + <span class="journey-strip__num" aria-hidden="true">3</span> 37 + <h3 class="journey-strip__title">Load and configure</h3> 38 + <p class="journey-strip__body">Point settings at Ollama native or an OpenAI-compatible endpoint, then try a tab.</p> 39 + </div> 40 + </div> 41 + 42 + <div class="install-wrap reveal-item" style="--i: 4"> 43 + <div class="install-panel"> 44 + <div> 45 + <h3 class="heading-lg">Chrome</h3> 46 + <ol> 47 + <li>Open <span class="kbd">chrome://extensions/</span></li> 48 + <li>Enable <strong>Developer mode</strong></li> 49 + <li>Click <strong>Load unpacked</strong></li> 50 + <li>Select the <strong>webai-summarizer</strong> folder that contains <code class="kbd">manifest.json</code></li> 51 + <li>Optional: shortcuts at <span class="kbd">chrome://extensions/shortcuts</span></li> 52 + </ol> 53 + </div> 54 + <div> 55 + <h3 class="heading-lg">Firefox</h3> 56 + <ol> 57 + <li>Open <span class="kbd">about:debugging#/runtime/this-firefox</span></li> 58 + <li>Click <strong>Load Temporary Add-on</strong></li> 59 + <li>Select <strong>manifest.json</strong> inside <strong>webai-summarizer</strong></li> 60 + <li>Add-ons → gear → <strong>Manage Extension Shortcuts</strong> for the hotkey</li> 61 + </ol> 62 + <p class="firefox-note"> 63 + Temporary add-ons clear when you fully quit Firefox—reload during active dev sessions. 64 + </p> 65 + </div> 66 + </div> 67 + </div> 68 + 69 + <details class="disclosure reveal-item" style="--i: 5"> 70 + <summary>Advanced: build scripts per browser</summary> 71 + <div class="disclosure__body"> 72 + <p> 73 + If you already use split manifests, <span class="kbd">./build.sh chrome</span> or 74 + <span class="kbd">./build.sh firefox</span> from the extension repo targets a specific build. 75 + </p> 76 + </div> 77 + </details> 78 + 79 + <p class="repo-note reveal-item" style="--i: 6"> 80 + Source: 81 + <a href={tangled} target="_blank" rel="noopener noreferrer">{tangled}</a> 82 + </p> 83 + </div> 84 + </section>
+24
src/components/sections/PrivacyBand.astro
··· 1 + <section id="privacy" class="section section--dark" aria-labelledby="privacy-heading"> 2 + <div class="wrap"> 3 + <header class="section__head"> 4 + <p class="eyebrow">Privacy and APIs</p> 5 + <h2 id="privacy-heading" class="heading-xl">Where text goes</h2> 6 + </header> 7 + <div class="split split--privacy"> 8 + <div class="prose prose--dark"> 9 + <h3 class="heading-md">Ollama native</h3> 10 + <p> 11 + <code class="kbd">/api/generate</code> on your machine. Base URL omits <span class="kbd">/v1</span>. 12 + </p> 13 + </div> 14 + <div class="prose prose--dark"> 15 + <h3 class="heading-md">OpenAI-compatible</h3> 16 + <p> 17 + <code class="kbd">/v1/chat/completions</code> to the endpoint you configure. Content leaves the browser only when 18 + you summarize or chat—not via a script on every site. 19 + </p> 20 + </div> 21 + </div> 22 + <p class="privacy-kicker prose prose--dark">Extraction is on-demand when you ask.</p> 23 + </div> 24 + </section>
+29
src/components/sections/UsageMicro.astro
··· 1 + <section id="usage" class="section section--tight-top wrap" aria-labelledby="usage-heading"> 2 + <header class="section__head"> 3 + <p class="eyebrow">In the popup</p> 4 + <h2 id="usage-heading" class="heading-xl">Three beats</h2> 5 + </header> 6 + <ol class="usage-flow"> 7 + <li> 8 + <span class="usage-flow__label">01</span> 9 + <div> 10 + <h3 class="heading-md">Land on the page</h3> 11 + <p>Article, doc, issue—whatever you need to judge quickly.</p> 12 + </div> 13 + </li> 14 + <li> 15 + <span class="usage-flow__label">02</span> 16 + <div> 17 + <h3 class="heading-md">Open Lede</h3> 18 + <p>Toolbar icon, context menu, or the keyboard shortcut.</p> 19 + </div> 20 + </li> 21 + <li> 22 + <span class="usage-flow__label">03</span> 23 + <div> 24 + <h3 class="heading-md">Summarize or ask</h3> 25 + <p>Summarize for the lede, or type a question about this tab and press Enter.</p> 26 + </div> 27 + </li> 28 + </ol> 29 + </section>
+2
src/data/site.ts
··· 1 + /** Canonical extension source (Tangled). */ 2 + export const TANGLED_REPO_URL = "https://tangled.org/ellioth.co/summarizer-extension";
+7
src/layouts/BaseLayout.astro
··· 5 5 title?: string; 6 6 description?: string; 7 7 ogUrl?: string; 8 + /** Absolute URL for Open Graph / Twitter image */ 9 + ogImage?: string; 8 10 } 9 11 10 12 const { ··· 12 14 description = 13 15 "Lede — page summarizer & chat. Don't bury the main point—summarize and discuss any article.", 14 16 ogUrl = "https://lede.app", 17 + ogImage, 15 18 } = Astro.props; 19 + 20 + const resolvedOgImage = ogImage ?? `${String(ogUrl).replace(/\/$/, "")}/images/hero-illustration.svg`; 16 21 --- 17 22 18 23 <!doctype html> ··· 27 32 <meta property="og:description" content={description} /> 28 33 <meta property="og:type" content="website" /> 29 34 <meta property="og:url" content={ogUrl} /> 35 + <meta property="og:image" content={resolvedOgImage} /> 30 36 <meta name="twitter:card" content="summary_large_image" /> 37 + <meta name="twitter:image" content={resolvedOgImage} /> 31 38 <link rel="icon" href="/favicon.svg" type="image/svg+xml" /> 32 39 <link rel="preconnect" href="https://fonts.googleapis.com" /> 33 40 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
+27 -256
src/pages/index.astro
··· 1 1 --- 2 + import { existsSync } from "node:fs"; 3 + import path from "node:path"; 2 4 import BaseLayout from "../layouts/BaseLayout.astro"; 3 5 import SiteHeader from "../components/SiteHeader.astro"; 6 + import SiteFooter from "../components/SiteFooter.astro"; 7 + import HeroWithProof from "../components/sections/HeroWithProof.astro"; 8 + import InstallJourney from "../components/sections/InstallJourney.astro"; 9 + import EditorialValue from "../components/sections/EditorialValue.astro"; 10 + import UsageMicro from "../components/sections/UsageMicro.astro"; 11 + import PrivacyBand from "../components/sections/PrivacyBand.astro"; 12 + import FAQCompact from "../components/sections/FAQCompact.astro"; 13 + import { TANGLED_REPO_URL } from "../data/site"; 4 14 5 - const tangled = "https://tangled.org/ellioth.co/summarizer-extension"; 15 + const ogUrl = "https://lede.app"; 16 + const base = ogUrl.replace(/\/$/, ""); 17 + const imagesDir = path.join(process.cwd(), "public", "images"); 18 + const ogImage = existsSync(path.join(imagesDir, "hero.webp")) 19 + ? `${base}/images/hero.webp` 20 + : existsSync(path.join(imagesDir, "hero.avif")) 21 + ? `${base}/images/hero.avif` 22 + : `${base}/images/hero-illustration.svg`; 6 23 --- 7 24 8 - <BaseLayout> 25 + <BaseLayout ogUrl={ogUrl} ogImage={ogImage}> 9 26 <div class="page"> 10 - <SiteHeader /> 27 + <SiteHeader tangled={TANGLED_REPO_URL} /> 11 28 <main id="main" class="page-main"> 12 - <section class="hero wrap reveal-group" aria-labelledby="hero-heading"> 13 - <div class="hero__grid"> 14 - <div> 15 - <p class="eyebrow reveal-item" style="--i: 0"> 16 - Page summarizer &amp; chat · Chrome &amp; Firefox 17 - </p> 18 - <h1 id="hero-heading" class="display-hero reveal-item" style="--i: 1"> 19 - Don&apos;t bury the lede 20 - </h1> 21 - <p class="lede reveal-item" style="--i: 2"> 22 - Put the main point first—then ask anything about the page—with AI. Answers stay tied to 23 - <strong>the tab you have open</strong>, not a generic chat shell. 24 - </p> 25 - <div class="actions reveal-item" style="--i: 3"> 26 - <a class="btn btn--primary" href="#install">Install (beta)</a> 27 - <a 28 - class="btn btn--secondary" 29 - href={tangled} 30 - target="_blank" 31 - rel="noopener noreferrer">Extension repo on Tangled</a 32 - > 33 - </div> 34 - <p class="eyebrow reveal-item" style="--i: 4; margin-top: var(--space-md)"> 35 - Keyboard 36 - </p> 37 - <p 38 - class="reveal-item" 39 - style="--i: 5; margin: 0; font-size: var(--text-sm); color: var(--color-ink-secondary)" 40 - > 41 - <span class="kbd">Ctrl</span> + <span class="kbd">Shift</span> + <span class="kbd">U</span> 42 - <span aria-hidden="true"> · </span> 43 - <span class="kbd">⌘</span> + <span class="kbd">Shift</span> + <span class="kbd">U</span> on macOS 44 - — <em>Summarize with Lede</em> 45 - </p> 46 - </div> 47 - <aside class="hero__aside reveal-item" style="--i: 3" aria-label="Release status"> 48 - <span class="beta-tag">Beta</span> 49 - <p class="beta-note"> 50 - Not in the Chrome Web Store or Firefox Add-ons yet. Grab the source, load unpacked (Chrome) 51 - or a temporary add-on (Firefox), then wire up Ollama or your API in settings. 52 - </p> 53 - </aside> 54 - </div> 55 - </section> 56 - 57 - <section id="install" class="section wrap" aria-labelledby="install-heading"> 58 - <header class="section__head"> 59 - <p class="eyebrow">Install</p> 60 - <h2 id="install-heading" class="heading-xl">Get the beta running</h2> 61 - </header> 62 - 63 - <p class="repo-callout"> 64 - <strong>Clone the extension, not this site.</strong> Lede&apos;s source and <code class="kbd" 65 - >manifest.json</code 66 - > 67 - live in the <strong>webai-summarizer</strong> repo on Tangled. This repository (<code class="kbd" 68 - >lede-website</code 69 - >) is only the marketing page. 70 - </p> 71 - 72 - <div class="install-section__quick" aria-labelledby="quick-heading"> 73 - <p id="quick-heading" class="eyebrow" style="margin-bottom: var(--space-sm)">Start here</p> 74 - <ol class="quickstart-path"> 75 - <li> 76 - <strong><span class="step-num">1</span> Open the repo</strong> 77 - <p> 78 - Download or clone <strong>webai-summarizer</strong> from Tangled so you have a local folder 79 - with <code class="kbd">manifest.json</code>. 80 - </p> 81 - <p style="margin-top: var(--space-2xs)"> 82 - <a href={tangled} target="_blank" rel="noopener noreferrer">Open extension repo →</a> 83 - </p> 84 - </li> 85 - <li> 86 - <strong><span class="step-num">2</span> Pick a browser</strong> 87 - <p>Chrome (unpacked) or Firefox (temporary add-on). Detailed clicks are in the panels below.</p> 88 - </li> 89 - <li> 90 - <strong><span class="step-num">3</span> Load &amp; configure</strong> 91 - <p> 92 - Point settings at Ollama native or an OpenAI-compatible endpoint, then summarize any tab. 93 - </p> 94 - </li> 95 - </ol> 96 - </div> 97 - 98 - <div class="install-wrap"> 99 - <div class="install-panel"> 100 - <div> 101 - <h3 class="heading-lg">Chrome</h3> 102 - <ol> 103 - <li>Open <span class="kbd">chrome://extensions/</span></li> 104 - <li>Enable <strong>Developer mode</strong></li> 105 - <li>Click <strong>Load unpacked</strong></li> 106 - <li>Select your local <strong>webai-summarizer</strong> folder (the one with <code class="kbd">manifest.json</code>)</li> 107 - <li>Optional: shortcuts at <span class="kbd">chrome://extensions/shortcuts</span></li> 108 - </ol> 109 - </div> 110 - <div> 111 - <h3 class="heading-lg">Firefox</h3> 112 - <ol> 113 - <li>Open <span class="kbd">about:debugging#/runtime/this-firefox</span></li> 114 - <li>Click <strong>Load Temporary Add-on</strong></li> 115 - <li>Choose <strong>manifest.json</strong> inside <strong>webai-summarizer</strong></li> 116 - <li>Enable the shortcut via Add-ons → gear → <strong>Manage Extension Shortcuts</strong></li> 117 - </ol> 118 - <p class="firefox-note"> 119 - Temporary add-ons last until you fully quit Firefox—plan to reload during active development. 120 - </p> 121 - </div> 122 - </div> 123 - </div> 124 - 125 - <details class="disclosure"> 126 - <summary>Advanced: build per browser</summary> 127 - <div class="disclosure__body"> 128 - <p> 129 - In the extension repo, you can target a browser with <span class="kbd">./build.sh chrome</span> or 130 - <span class="kbd">./build.sh firefox</span> if you maintain separate manifests—only if you already 131 - use that workflow. 132 - </p> 133 - </div> 134 - </details> 135 - 136 - <p class="repo-note" style="margin-top: var(--space-md)"> 137 - Open source: 138 - <a href={tangled} target="_blank" rel="noopener noreferrer">{tangled}</a> 139 - </p> 140 - </section> 141 - 142 - <div class="wrap"> 143 - <blockquote class="pull-quote reveal-item" style="--i: 0" cite={tangled}> 144 - Worth your time? Lede reads the open tab on demand—then puts the verdict up front. 145 - </blockquote> 146 - </div> 147 - 148 - <section id="features" class="section wrap" aria-labelledby="features-heading"> 149 - <header class="section__head"> 150 - <p class="eyebrow">Product</p> 151 - <h2 id="features-heading" class="heading-xl">What happens on your tab</h2> 152 - </header> 153 - <ul class="value-list"> 154 - <li> 155 - <strong>One-click summaries</strong> 156 - <span>Pull the lede from long articles, docs, and threads without leaving the page.</span> 157 - </li> 158 - <li> 159 - <strong>Follow-up questions</strong> 160 - <span>Ask about the page or the summary; answers trace what you were reading.</span> 161 - </li> 162 - <li> 163 - <strong>OpenAI-compatible + Ollama</strong> 164 - <span>Ollama, OpenAI, Groq, LM Studio—bring the endpoint you trust.</span> 165 - </li> 166 - <li> 167 - <strong>Your model, your URL</strong> 168 - <span>Pick the model and base URL in settings instead of a locked-in vendor.</span> 169 - </li> 170 - <li> 171 - <strong>Quick access</strong> 172 - <span>Toolbar icon, context menu, or the keyboard shortcut—whichever fits your flow.</span> 173 - </li> 174 - </ul> 175 - </section> 176 - 177 - <section class="section section--tight-top wrap" aria-labelledby="how-heading"> 178 - <header class="section__head"> 179 - <p class="eyebrow">In the popup</p> 180 - <h2 id="how-heading" class="heading-xl">Three beats</h2> 181 - </header> 182 - <ol class="steps"> 183 - <li> 184 - <h3 class="heading-md">Open the page</h3> 185 - <p>Whatever you need to understand—article, ticket, README.</p> 186 - </li> 187 - <li> 188 - <h3 class="heading-md">Open Lede</h3> 189 - <p>Toolbar icon or shortcut.</p> 190 - </li> 191 - <li> 192 - <h3 class="heading-md">Summarize or ask</h3> 193 - <p>Summarize for the lede, or type a question about this tab and press Enter.</p> 194 - </li> 195 - </ol> 196 - </section> 197 - 198 - <section 199 - id="privacy" 200 - class="section section--dark" 201 - aria-labelledby="privacy-heading" 202 - > 203 - <div class="wrap"> 204 - <header class="section__head"> 205 - <p class="eyebrow">Privacy &amp; APIs</p> 206 - <h2 id="privacy-heading" class="heading-xl">Where the words go</h2> 207 - </header> 208 - <div class="split"> 209 - <div class="prose"> 210 - <h3 class="heading-md">Ollama native</h3> 211 - <p> 212 - Uses <code class="kbd">/api/generate</code>. When the model runs locally, content stays on your 213 - machine. Base URL should <strong>not</strong> include <span class="kbd">/v1</span>. 214 - </p> 215 - </div> 216 - <div class="prose"> 217 - <h3 class="heading-md">OpenAI-compatible</h3> 218 - <p> 219 - Uses <code class="kbd">/v1/chat/completions</code> for cloud or self-hosted endpoints you configure. 220 - Page text is sent only when you summarize or chat—not from a script that runs on every site. 221 - </p> 222 - </div> 223 - </div> 224 - <p class="prose" style="margin-top: var(--space-lg)"> 225 - Extraction is <strong>on-demand</strong> when you ask—nothing leaves the tab until you trigger it. 226 - </p> 227 - </div> 228 - </section> 229 - 230 - <section id="faq" class="section wrap" aria-labelledby="faq-heading"> 231 - <header class="section__head"> 232 - <p class="eyebrow">FAQ</p> 233 - <h2 id="faq-heading" class="heading-xl">If something blocks you</h2> 234 - </header> 235 - <div class="faq"> 236 - <div class="faq-item"> 237 - <h3 class="heading-md">Cannot connect to localhost:11434</h3> 238 - <p> 239 - Run <span class="kbd">ollama serve</span>, check <span class="kbd">ollama list</span>, and keep Native 240 - mode on <span class="kbd">http://localhost:11434</span> without <span class="kbd">/v1</span>. 241 - </p> 242 - </div> 243 - <div class="faq-item"> 244 - <h3 class="heading-md">HTTP 403 or 405</h3> 245 - <p> 246 - Wrong API mode for the URL: Native → no <span class="kbd">/v1</span>. OpenAI-compatible → include 247 - <span class="kbd">/v1</span> (for example <span class="kbd">http://localhost:11434/v1</span> for Ollama 248 - compatibility mode). 249 - </p> 250 - </div> 251 - </div> 252 - </section> 29 + <HeroWithProof tangled={TANGLED_REPO_URL} /> 30 + <InstallJourney tangled={TANGLED_REPO_URL} /> 31 + <EditorialValue /> 32 + <UsageMicro /> 33 + <PrivacyBand /> 34 + <FAQCompact /> 253 35 </main> 254 - 255 - <footer class="site-footer"> 256 - <div class="wrap footer-inner"> 257 - <span class="font-display" style="font-weight: 700; color: var(--color-ink)">Lede</span> 258 - <div style="display: flex; flex-wrap: wrap; gap: var(--space-sm) var(--space-lg)"> 259 - <a href={tangled} target="_blank" rel="noopener noreferrer">Repo on Tangled</a> 260 - <a href="#install">Install</a> 261 - <a href="#privacy">Privacy</a> 262 - <a href="#faq">FAQ</a> 263 - </div> 264 - </div> 265 - </footer> 36 + <SiteFooter tangled={TANGLED_REPO_URL} /> 266 37 </div> 267 38 </BaseLayout>
+346 -243
src/styles/global.css
··· 327 327 color: var(--color-brand); 328 328 } 329 329 330 - /* ——— Start here (install-first) ——— */ 331 - .install-section__quick { 332 - margin-bottom: var(--space-xl); 333 - padding-block: var(--space-lg); 334 - padding-inline: var(--space-lg); 335 - background: color-mix(in oklch, var(--color-subtle) 50%, var(--color-canvas)); 336 - border: 1px solid var(--color-border); 337 - border-radius: var(--radius-md); 338 - } 339 - 340 - .quickstart-path { 341 - list-style: none; 342 - margin: 0; 343 - padding: 0; 344 - display: grid; 345 - gap: var(--space-md); 346 - } 347 - 348 - @media (min-width: 720px) { 349 - .quickstart-path { 350 - grid-template-columns: repeat(3, 1fr); 351 - gap: var(--space-lg); 352 - } 353 - } 354 - 355 - .quickstart-path li { 356 - display: flex; 357 - flex-direction: column; 358 - gap: var(--space-2xs); 359 - } 360 - 361 - .quickstart-path .step-num { 362 - font-family: var(--font-display); 363 - font-weight: 700; 364 - font-size: var(--text-md); 365 - color: var(--color-brand); 366 - margin-right: 0.25em; 367 - } 368 - 369 - .quickstart-path strong { 370 - font-family: var(--font-display); 371 - font-weight: 600; 372 - font-size: var(--text-sm); 373 - color: var(--color-ink); 374 - display: block; 375 - margin-bottom: var(--space-3xs); 376 - } 377 - 378 - .quickstart-path p { 379 - margin: 0; 380 - font-size: var(--text-sm); 381 - color: var(--color-ink-secondary); 382 - line-height: var(--leading-prose); 383 - max-width: 36ch; 384 - } 385 - 386 - /* Repo + install callouts — full border only (no side-stripe accent) */ 387 - .repo-callout { 388 - margin-bottom: var(--space-lg); 389 - padding: var(--space-md) var(--space-lg); 390 - background: var(--color-surface); 391 - border: 1px solid var(--color-border); 392 - border-radius: var(--radius-md); 393 - font-size: var(--text-sm); 394 - line-height: var(--leading-prose); 395 - color: var(--color-ink-secondary); 396 - max-width: 72ch; 397 - } 398 - 399 - .repo-callout strong { 400 - color: var(--color-ink); 401 - } 402 - 403 330 .firefox-note { 404 331 margin-top: var(--space-md); 405 332 font-size: var(--text-sm); ··· 462 389 margin-top: var(--space-sm); 463 390 } 464 391 465 - /* ——— Hero ——— */ 466 - .hero { 467 - padding-block: var(--space-3xl) var(--space-2xl); 468 - } 469 - 470 - .hero__grid { 471 - display: grid; 472 - gap: var(--space-xl); 473 - align-items: start; 474 - } 475 - 476 - @media (min-width: 880px) { 477 - .hero__grid { 478 - grid-template-columns: minmax(0, 1.15fr) minmax(220px, 0.55fr); 479 - gap: var(--space-2xl); 480 - } 481 - } 482 - 483 - .hero__aside { 484 - display: flex; 485 - flex-direction: column; 486 - gap: var(--space-md); 487 - padding: var(--space-lg); 488 - background: var(--color-surface); 489 - border: 1px solid var(--color-border); 490 - border-radius: var(--radius-md); 491 - box-shadow: 0 1px 0 color-mix(in oklch, var(--color-ink) 4%, transparent); 492 - } 493 - 494 - @media (min-width: 880px) { 495 - .hero__aside { 496 - margin-top: var(--space-2xl); 497 - transform: translateX(4%); 498 - } 499 - } 500 - 501 392 .beta-tag { 502 393 font-family: var(--font-display); 503 394 font-weight: 700; ··· 505 396 letter-spacing: var(--tracking-label); 506 397 text-transform: uppercase; 507 398 color: var(--color-brand); 508 - } 509 - 510 - .beta-note { 511 - font-size: var(--text-sm); 512 - line-height: var(--leading-ui); 513 - color: var(--color-ink-secondary); 514 - margin: 0; 515 399 } 516 400 517 401 /* ——— Buttons ——— */ ··· 593 477 margin-bottom: var(--space-lg); 594 478 } 595 479 596 - /* Asymmetric pull — full-width rules, no side-stripe accent */ 597 - .pull-quote { 598 - margin: var(--space-2xl) 0; 599 - padding: var(--space-lg) 0; 600 - border-top: 1px solid color-mix(in oklch, var(--color-brand) 45%, var(--color-border)); 601 - border-bottom: 1px solid color-mix(in oklch, var(--color-brand) 45%, var(--color-border)); 602 - font-family: var(--font-display); 603 - font-size: var(--text-lg); 604 - font-weight: 600; 605 - line-height: var(--leading-tight); 606 - color: var(--color-ink); 607 - max-width: min(22em, 100%); 608 - } 609 - 610 - @media (min-width: 720px) { 611 - .pull-quote { 612 - margin-inline-start: 8%; 613 - } 614 - } 615 - 616 - /* Value: typographic list, not icon cards */ 617 - .value-list { 618 - list-style: none; 619 - margin: 0; 620 - padding: 0; 621 - display: flex; 622 - flex-direction: column; 623 - gap: var(--space-lg); 624 - } 625 - 626 - .value-list li { 627 - display: grid; 628 - gap: var(--space-2xs); 629 - padding-bottom: var(--space-lg); 630 - border-bottom: 1px solid var(--color-border); 631 - } 632 - 633 - .value-list li:last-child { 634 - border-bottom: none; 635 - padding-bottom: 0; 636 - } 637 - 638 - .value-list strong { 639 - font-family: var(--font-display); 640 - font-weight: 700; 641 - font-size: var(--text-md); 642 - color: var(--color-ink); 643 - } 644 - 645 - .value-list span { 646 - color: var(--color-ink-secondary); 647 - font-size: var(--text-sm); 648 - line-height: var(--leading-prose); 649 - max-width: var(--content-max); 650 - } 651 - 652 - /* How it works: numbered */ 653 - .steps { 654 - counter-reset: step; 655 - list-style: none; 656 - margin: 0; 657 - padding: 0; 658 - display: grid; 659 - gap: var(--space-md); 660 - } 661 - 662 - @media (min-width: 720px) { 663 - .steps { 664 - grid-template-columns: repeat(3, 1fr); 665 - gap: var(--space-lg); 666 - } 667 - } 668 - 669 - .steps li { 670 - position: relative; 671 - padding: var(--space-lg); 672 - background: var(--color-surface); 673 - border: 1px solid var(--color-border); 674 - border-radius: var(--radius-md); 675 - } 676 - 677 - .steps li::before { 678 - counter-increment: step; 679 - content: counter(step); 680 - font-family: var(--font-display); 681 - font-weight: 700; 682 - font-size: var(--text-lg); 683 - color: var(--color-brand); 684 - display: block; 685 - margin-bottom: var(--space-xs); 686 - } 687 - 688 - .steps p { 689 - margin: 0; 690 - font-size: var(--text-sm); 691 - color: var(--color-ink-secondary); 692 - line-height: var(--leading-prose); 693 - } 694 - 695 480 /* Privacy dark band */ 696 481 .section--dark { 697 482 background: var(--color-dark-canvas); ··· 705 490 } 706 491 707 492 .section--dark .heading-xl { 493 + color: var(--color-dark-ink); 494 + } 495 + 496 + .section--dark .heading-md { 708 497 color: var(--color-dark-ink); 709 498 } 710 499 ··· 782 571 max-width: 65ch; 783 572 } 784 573 785 - /* FAQ: definition-style, no side stripes */ 786 - .faq { 787 - display: flex; 788 - flex-direction: column; 789 - gap: var(--space-lg); 790 - } 791 - 792 - .faq-item { 793 - padding-bottom: var(--space-lg); 794 - border-bottom: 1px solid var(--color-border); 795 - } 796 - 797 - .faq-item:last-child { 798 - border-bottom: none; 799 - padding-bottom: 0; 800 - } 801 - 802 - .faq-item h3 { 803 - margin: 0 0 var(--space-xs); 804 - } 805 - 806 - .faq-item p { 807 - margin: 0; 808 - font-size: var(--text-sm); 809 - color: var(--color-ink-secondary); 810 - line-height: var(--leading-prose); 811 - } 812 - 813 574 /* Footer */ 814 575 .site-footer { 815 576 padding-block: var(--space-xl); ··· 853 614 transform: none !important; 854 615 } 855 616 } 617 + 618 + /* ========= Redesign: masthead hero + proof, install journey, editorial ========= */ 619 + 620 + .site-header__tagline { 621 + display: none; 622 + margin: 0; 623 + padding-bottom: var(--space-xs); 624 + font-size: var(--text-xs); 625 + font-family: var(--font-display); 626 + font-weight: 600; 627 + letter-spacing: var(--tracking-label); 628 + text-transform: uppercase; 629 + color: var(--color-ink-muted); 630 + border-top: 1px solid color-mix(in oklch, var(--color-border) 70%, transparent); 631 + padding-top: var(--space-2xs); 632 + } 633 + 634 + @media (min-width: 900px) { 635 + .site-header__tagline { 636 + display: block; 637 + } 638 + } 639 + 640 + .deck { 641 + font-family: var(--font-display); 642 + font-size: clamp(1.125rem, 1.5vw + 0.75rem, 1.45rem); 643 + font-weight: 600; 644 + line-height: var(--leading-ui); 645 + letter-spacing: -0.015em; 646 + color: var(--color-ink); 647 + max-width: 28em; 648 + margin: 0 0 var(--space-sm); 649 + } 650 + 651 + .lede--tight { 652 + max-width: 32em; 653 + font-size: var(--text-sm); 654 + } 655 + 656 + .hero-mast { 657 + padding-block: var(--space-2xl) var(--space-3xl); 658 + border-bottom: 1px solid var(--color-border); 659 + } 660 + 661 + .hero-mast__grid { 662 + display: grid; 663 + gap: var(--space-xl); 664 + align-items: stretch; 665 + } 666 + 667 + @media (min-width: 960px) { 668 + .hero-mast__grid { 669 + grid-template-columns: minmax(0, 1fr) minmax(320px, 1.05fr); 670 + gap: var(--space-2xl); 671 + align-items: center; 672 + } 673 + } 674 + 675 + .hero-proof { 676 + margin: 0; 677 + display: flex; 678 + flex-direction: column; 679 + gap: var(--space-sm); 680 + } 681 + 682 + .hero-proof__frame { 683 + border-radius: var(--radius-md); 684 + border: 1px solid var(--color-border); 685 + background: var(--color-surface); 686 + overflow: hidden; 687 + box-shadow: 688 + 0 1px 0 color-mix(in oklch, var(--color-ink) 5%, transparent), 689 + 0 18px 48px color-mix(in oklch, var(--color-ink) 6%, transparent); 690 + } 691 + 692 + .hero-proof__frame img { 693 + width: 100%; 694 + height: auto; 695 + display: block; 696 + } 697 + 698 + @media (prefers-reduced-motion: no-preference) { 699 + .hero-proof__frame { 700 + animation: hero-frame-in 0.85s var(--ease-out) both; 701 + animation-delay: 0.12s; 702 + } 703 + 704 + @keyframes hero-frame-in { 705 + from { 706 + opacity: 0; 707 + transform: translateY(16px) scale(0.985); 708 + } 709 + to { 710 + opacity: 1; 711 + transform: translateY(0) scale(1); 712 + } 713 + } 714 + } 715 + 716 + @media (prefers-reduced-motion: reduce) { 717 + .hero-proof__frame { 718 + animation: none; 719 + } 720 + } 721 + 722 + .hero-proof__caption { 723 + display: flex; 724 + flex-wrap: wrap; 725 + align-items: baseline; 726 + gap: var(--space-xs) var(--space-sm); 727 + max-width: 52ch; 728 + } 729 + 730 + .hero-proof__capline { 731 + font-size: var(--text-sm); 732 + line-height: var(--leading-ui); 733 + color: var(--color-ink-secondary); 734 + } 735 + 736 + .section--install { 737 + padding-top: var(--space-3xl); 738 + } 739 + 740 + .section__head--install { 741 + margin-bottom: var(--space-md); 742 + } 743 + 744 + .journey-band { 745 + padding: var(--space-lg); 746 + border: 1px solid var(--color-border); 747 + border-radius: var(--radius-md); 748 + margin-bottom: var(--space-md); 749 + } 750 + 751 + .journey-band--callout { 752 + background: color-mix(in oklch, var(--color-subtle) 65%, var(--color-canvas)); 753 + } 754 + 755 + .journey-band__text { 756 + margin: 0; 757 + font-size: var(--text-sm); 758 + line-height: var(--leading-prose); 759 + color: var(--color-ink-secondary); 760 + max-width: 75ch; 761 + } 762 + 763 + .journey-band__text strong { 764 + color: var(--color-ink); 765 + } 766 + 767 + .journey-strip { 768 + display: grid; 769 + gap: var(--space-md); 770 + margin-bottom: var(--space-xl); 771 + } 772 + 773 + @media (min-width: 880px) { 774 + .journey-strip { 775 + grid-template-columns: repeat(3, 1fr); 776 + gap: var(--space-lg); 777 + } 778 + } 779 + 780 + .journey-strip__cell { 781 + padding: var(--space-lg); 782 + background: var(--color-surface); 783 + border: 1px solid var(--color-border); 784 + border-radius: var(--radius-md); 785 + display: flex; 786 + flex-direction: column; 787 + gap: var(--space-2xs); 788 + } 789 + 790 + .journey-strip__num { 791 + font-family: var(--font-display); 792 + font-weight: 700; 793 + font-size: var(--text-xl); 794 + color: var(--color-brand); 795 + line-height: 1; 796 + } 797 + 798 + .journey-strip__title { 799 + margin: 0; 800 + font-family: var(--font-display); 801 + font-weight: 600; 802 + font-size: var(--text-sm); 803 + color: var(--color-ink); 804 + } 805 + 806 + .journey-strip__body { 807 + margin: 0; 808 + font-size: var(--text-sm); 809 + color: var(--color-ink-secondary); 810 + line-height: var(--leading-prose); 811 + flex: 1; 812 + } 813 + 814 + .journey-strip__cell a { 815 + font-family: var(--font-display); 816 + font-weight: 600; 817 + font-size: 0.8125rem; 818 + margin-top: var(--space-xs); 819 + } 820 + 821 + .section--editorial { 822 + padding-block: var(--space-3xl); 823 + } 824 + 825 + .editorial-grid { 826 + display: grid; 827 + gap: var(--space-xl); 828 + } 829 + 830 + @media (min-width: 800px) { 831 + .editorial-grid { 832 + grid-template-columns: repeat(3, 1fr); 833 + gap: var(--space-lg); 834 + } 835 + } 836 + 837 + .editorial-block { 838 + padding-top: var(--space-md); 839 + border-top: 2px solid color-mix(in oklch, var(--color-brand) 35%, var(--color-border)); 840 + } 841 + 842 + .editorial-block p { 843 + margin: var(--space-xs) 0 0; 844 + font-size: var(--text-sm); 845 + line-height: var(--leading-prose); 846 + color: var(--color-ink-secondary); 847 + } 848 + 849 + .usage-flow { 850 + list-style: none; 851 + margin: 0; 852 + padding: 0; 853 + display: flex; 854 + flex-direction: column; 855 + gap: 0; 856 + } 857 + 858 + .usage-flow li { 859 + display: grid; 860 + grid-template-columns: auto 1fr; 861 + gap: var(--space-md); 862 + padding: var(--space-lg) 0; 863 + border-top: 1px solid var(--color-border); 864 + } 865 + 866 + .usage-flow li:last-child { 867 + border-bottom: 1px solid var(--color-border); 868 + } 869 + 870 + .usage-flow__label { 871 + font-family: var(--font-display); 872 + font-weight: 700; 873 + font-size: var(--text-sm); 874 + color: var(--color-brand); 875 + letter-spacing: 0.04em; 876 + } 877 + 878 + .usage-flow p { 879 + margin: var(--space-2xs) 0 0; 880 + font-size: var(--text-sm); 881 + color: var(--color-ink-secondary); 882 + line-height: var(--leading-prose); 883 + } 884 + 885 + .split--privacy { 886 + margin-top: var(--space-sm); 887 + } 888 + 889 + .prose--dark p { 890 + color: var(--color-dark-ink-secondary); 891 + } 892 + 893 + .privacy-kicker { 894 + margin: var(--space-lg) 0 0; 895 + font-weight: 600; 896 + font-size: var(--text-sm); 897 + max-width: 42ch; 898 + } 899 + 900 + .faq-stack { 901 + display: flex; 902 + flex-direction: column; 903 + gap: var(--space-sm); 904 + } 905 + 906 + .faq-disclosure { 907 + border: 1px solid var(--color-border); 908 + border-radius: var(--radius-md); 909 + background: var(--color-surface); 910 + overflow: hidden; 911 + } 912 + 913 + .faq-disclosure summary { 914 + cursor: pointer; 915 + list-style: none; 916 + padding: var(--space-md) var(--space-lg); 917 + font-family: var(--font-display); 918 + font-weight: 600; 919 + font-size: var(--text-md); 920 + color: var(--color-ink); 921 + display: flex; 922 + align-items: center; 923 + justify-content: space-between; 924 + gap: var(--space-sm); 925 + } 926 + 927 + .faq-disclosure summary::-webkit-details-marker { 928 + display: none; 929 + } 930 + 931 + .faq-disclosure summary::after { 932 + content: "+"; 933 + font-weight: 700; 934 + color: var(--color-brand); 935 + flex-shrink: 0; 936 + } 937 + 938 + .faq-disclosure[open] summary::after { 939 + content: "–"; 940 + } 941 + 942 + .faq-disclosure__body { 943 + padding: 0 var(--space-lg) var(--space-md); 944 + border-top: 1px solid var(--color-border); 945 + } 946 + 947 + .faq-disclosure__body p { 948 + margin: var(--space-sm) 0 0; 949 + font-size: var(--text-sm); 950 + color: var(--color-ink-secondary); 951 + line-height: var(--leading-prose); 952 + } 953 + 954 + .footer-links { 955 + display: flex; 956 + flex-wrap: wrap; 957 + gap: var(--space-sm) var(--space-lg); 958 + }