From a2e3ae6fc960bf9912a7c84c93d4a2bb8e44bb1f Mon Sep 17 00:00:00 2001 From: LeOu Date: Tue, 3 Nov 2026 07:46:42 -0500 Subject: [PATCH] Refine About with interactive clarinet and unified reading links --- .github/README.md | 2 +- architecture/README.md | 2 +- architecture/index.html | 2 +- architecture/site.json | 2 +- docs/design.md | 116 +++-- docs/maintenance.md | 8 +- website/README.md | 37 +- website/app/components/AboutContent.tsx | 24 +- .../clarinet-hero/ClarinetHero.module.css | 127 +++++ .../components/clarinet-hero/ClarinetHero.tsx | 78 +++ .../app/components/clarinet-hero/alignment.ts | 45 ++ .../app/components/clarinet-hero/physics.ts | 163 ++++++ .../app/components/clarinet-hero/pixels.ts | 15 + .../app/components/clarinet-hero/renderer.ts | 324 ++++++++++++ .../app/components/clarinet-hero/sampling.ts | 226 +++++++++ website/app/components/clarinet-hero/shape.ts | 102 ++++ .../DitheredEntrance.module.css | 200 -------- .../dithered-entrance/DitheredEntrance.tsx | 350 ------------- .../dithered-entrance/dither-mask.ts | 94 ---- .../dithered-entrance/dither-motion.ts | 29 -- .../dithered-entrance/dither-types.ts | 16 - website/app/globals.css | 4 +- website/app/home.module.css | 55 +- website/app/page.tsx | 6 +- website/content/protected-sources.json | 2 +- website/content/visible-copy-manifest.json | 22 +- website/tests/clarinet-alignment.test.mjs | 114 +++++ website/tests/clarinet-lifecycle.test.mjs | 470 ++++++++++++++++++ website/tests/clarinet-physics.test.mjs | 207 ++++++++ website/tests/dither-motion.test.mjs | 135 ----- website/tests/rendered-html.test.mjs | 109 +++- 31 files changed, 2149 insertions(+), 937 deletions(-) create mode 100644 website/app/components/clarinet-hero/ClarinetHero.module.css create mode 100644 website/app/components/clarinet-hero/ClarinetHero.tsx create mode 100644 website/app/components/clarinet-hero/alignment.ts create mode 100644 website/app/components/clarinet-hero/physics.ts create mode 100644 website/app/components/clarinet-hero/pixels.ts create mode 100644 website/app/components/clarinet-hero/renderer.ts create mode 100644 website/app/components/clarinet-hero/sampling.ts create mode 100644 website/app/components/clarinet-hero/shape.ts delete mode 100644 website/app/components/dithered-entrance/DitheredEntrance.module.css delete mode 100644 website/app/components/dithered-entrance/DitheredEntrance.tsx delete mode 100644 website/app/components/dithered-entrance/dither-mask.ts delete mode 100644 website/app/components/dithered-entrance/dither-motion.ts delete mode 100644 website/app/components/dithered-entrance/dither-types.ts create mode 100644 website/tests/clarinet-alignment.test.mjs create mode 100644 website/tests/clarinet-lifecycle.test.mjs create mode 100644 website/tests/clarinet-physics.test.mjs delete mode 100644 website/tests/dither-motion.test.mjs diff --git a/.github/README.md b/.github/README.md index 53cc0af..a2c1fee 100644 --- a/.github/README.md +++ b/.github/README.md @@ -12,7 +12,7 @@ I am a Duke University graduate and a Sequoia Fellow whose work spans AI researc ## A personal introduction -The homepage opens with my name drawn as an interactive field of dots. Move the pointer across the letters and the particles spring away, then settle back into place; a click sends a ripple through the wordmark. It gives the site a distinctive entrance before introducing my background and contact details. +The homepage pairs my name and background with a clarinet drawn from separated particles. Moving the pointer gently displaces nearby points, which spring back into place; scrolling disperses the composition and returning restores it. Four musical notes move continuously beside the instrument, while reduced-motion settings show a complete static illustration. The rest of the design is deliberately quiet: warm paper, clear typography, and generous space for the work itself. diff --git a/architecture/README.md b/architecture/README.md index cc3a14c..f6a7efd 100644 --- a/architecture/README.md +++ b/architecture/README.md @@ -29,7 +29,7 @@ describes content ownership, import commands, and extension checks. Next.js exports HTML, CSS, and JavaScript to `website/out/`. Build assembly includes this viewer and applies production CSP before GitHub Actions deploys the artifact to GitHub Pages. There is no request-time API, database, authentication, or analytics. -The main site's browser interactions are the Canvas2D entrance, top-level navigation menu, +The main site's browser interactions are the Canvas 2D clarinet hero, top-level navigation menu, article contents, and legal-note previews. Reading and ordinary links remain available without JavaScript. diff --git a/architecture/index.html b/architecture/index.html index 127f3af..163d781 100644 --- a/architecture/index.html +++ b/architecture/index.html @@ -5638,7 +5638,7 @@

Static Delivery

Focused Interactions

diff --git a/architecture/site.json b/architecture/site.json index acc7edb..8710ad8 100644 --- a/architecture/site.json +++ b/architecture/site.json @@ -250,7 +250,7 @@ "dot": "violet", "title": "Focused Interactions", "items": [ - "Name entrance, navigation, contents, and footnote previews", + "Clarinet hero, navigation, contents, and footnote previews", "Architecture search, themes, tracing, and export" ] } diff --git a/docs/design.md b/docs/design.md index f05c3df..ae6a11a 100644 --- a/docs/design.md +++ b/docs/design.md @@ -8,10 +8,21 @@ The site pairs self-hosted Inter with Source Serif 4 and its own content hierarc ## Identity The compact LO mark appears in the navigation and browser icons. About opens with -the interactive full-screen THEODORE OUYANG particle wordmark, followed by the -profile and contact information. This signature entrance is an intentional part -of the owner's identity; preserve its pointer and keyboard interaction, scroll -link, static fallback, and reduced-motion behavior. The mark's editable master is +Theodore Ouyang's name and the identities Duke alumnus and Sequoia Fellow on the +left, with a recognizable clarinet particle field on the right. The clarinet +follows the selected reference's slender silhouette, reflective mouthpiece face, +six silver ring keys, joint bands and pads, two long rods, and a flared +bell with an elliptical rim. The body, fittings, ring-key centers, and bell cavity +are all composed of separated points; the cavity uses denser ink points. +Its open arc uses larger, more closely spaced blue points, with occlusion where +the instrument passes in front; it remains an open curve. A single solid blue oval +and restrained trails retain the composition's graphic character. Four +short blue vertical marks define the stage corners; four solid, clearly recognizable +musical notes accompany the particle field. The hero has no call-to-action or +playback buttons. The same-shape SVG fallback keeps the instrument's separated dot +patterns, so loading and no-JavaScript states have no solid instrument surface underneath. Preserve +the reduced-motion behavior. +The mark's editable master is `website/source-assets/brand/lo-mark.svg`. ## Color and type @@ -30,16 +41,35 @@ link, static fallback, and reduced-motion behavior. The mark's editable master i Inter 4.1 remains the typeface for headings, navigation, directory labels, controls, metadata, and the architecture viewer. Source Serif 4 supplies narrative prose and the small section and directory indices. This sans-serif/serif pairing follows -Cognition's distinction between headings and reading text while retaining the site's -original Inter lettering and content hierarchy. +Cognition's distinction between headings and reading text within the site's +content hierarchy. `website/public/assets/fonts/inter.css` owns `--font-text`, the fixed optical sizing, lining numerals, and the Inter faces. `source-serif.css` owns `--font-prose` and the Source Serif 4 faces. Both families use self-hosted variable WOFF2 files with true italics and bundled licenses. Inter retains automatic optical sizing disabled and the `opsz` axis fixed at 14. Prose numerals use proportional widths; section indices and numeric tables can use tabular widths. Shared bold emphasis uses weight 600. -The LO mark uses independent artwork, and the particle wordmark retains Inter and -its original composition and interaction. +The LO mark uses independent artwork. The hero name and the institution names +Duke and Sequoia retain Inter; the supporting roles alumnus and Fellow use Source +Serif 4, preserving the site's heading/prose pairing. +The decorative clarinet uses opaque ink, silver-gray, and blue point samples in +Canvas 2D, with transparent gaps between the points. Clean mother-shape paths are +rasterized into temporary construction masks only when the field is built or +resized. Those masks identify the body, fittings, and outlines; they never become +visible filled surfaces. Silver fittings use separated dark outline points and +sparse gray interior points. The four musical notes and the single blue oval are +the illustration's solid accents: they use filled `Path2D` shapes rather than +point sampling. The instrument itself has no solid bitmap, continuous metal fill, +or gradient surface. No decorative typeface is loaded, and the reference image's +existing texture is not converted into the particle field. + +Settled points align to device pixels. Their transparent cached layer copies at +native resolution without another scaling pass. `alignCanvas` derives responsive +dimensions from the stage, aligns the displayed canvas origin to device pixels, +and matches its CSS box to the backing store. Canvas resolution follows device +pixel ratio, capped at 3, independently of the CSS coordinates used for physics. +Moving points use subpixel edge coverage rather than a blur filter; they return +to the exact pixel-aligned cached positions when they settle. Typography follows semantic roles. At any viewport, text with the same role uses the same size across every section and page. Weight, color, spacing, and italics @@ -176,8 +206,9 @@ viewport height. Its minimum track width follows the interface type size, naviga indent, padding, and scrollbar allowance. The reading field and structural guides reflow together when enlarged text needs more rail space. Long reading content has emergency wrapping when an unbroken word would exceed its container. -Coursework opens at `/coursework/` from the View Coursework link below the Education -records. The link uses a compact dark gray button with visible keyboard focus; +Coursework opens at `/coursework/` from the Read More link below the Education +records. It uses the same underlined text and decorative right arrow as Experience, +with visible keyboard focus and a descriptive accessible name; the About page keeps its `#coursework` anchor around this entrance. The dedicated page has four subject sections, indexed 01 through 04, and uses the same numbered, ruled directory and View interaction as Experience. @@ -195,29 +226,51 @@ Parent links have 44px targets; coarse pointers also receive 44px primary naviga and standalone link targets. About uses compact spacing inside each research or education record and larger -breaks between its numbered sections. Each Research entry is one manuscript link -containing its title and complete summary, separated from the next entry by a thin -rule. Titles use weight 600 without underlines; titles and summaries share black -ink at rest and turn blue together on hover or keyboard focus. The title supplies -the link's accessible name. The list omits dates. Paragraphs wrap naturally +breaks between its numbered sections. Each Research entry contains its title, +complete summary, and a separate Read More manuscript link, separated from the +next entry by a thin rule. Titles use weight 600 without underlines. The action +matches Experience's underlined text and decorative right arrow, with blue hover +and keyboard-focus feedback. Its accessible name identifies the manuscript. +The list omits dates. Paragraphs wrap naturally without forced line breaks. Section headings, entry headings, and reading text use the shared heading, 18px subheading, and 14px reading hierarchy; scoped spacing overrides do not affect other pages. -Page titles own the H1 position. About's two-line "Thinking mathematically / Working in AI" -supplies its main heading; the particle wordmark retains the full name artwork. +Page titles own the H1 position. About's two-line "Theodore / Ouyang" supplies its +main heading. The "Thinking mathematically / Working in AI" introduction +is an H2 below the hero; it remains the H1 on the `/education/` compatibility page. Experience domains use `01`–`05` indices; individual entries have no leading markers. Bullet markers occupy an 18px gutter beside the text. Narrative lines stay within the shared 720px reading measure and reflow with the viewport. ## Interaction -About opens with its Canvas2D name entrance and direct scroll link to the profile. Routine links -use color and underline feedback. +About's Canvas 2D clarinet aggregates from seeded peripheral positions. Fine-pointer +movement gently displaces nearby points, which spring back to their +sampled positions; the bell remains the most stable part of the instrument. +Scrolling away disperses the points in response to actual +scroll progress, and returning restores the composition; ordinary document +scrolling remains native. Four musical notes move continuously while the hero is +visible, with independent periods for restrained drift, rotation, scale, and opacity. +The body breathes subtly through opacity while its settled point positions +remain fixed and sharp. Phones reframe the surrounding whitespace to give the +instrument more space. Physics advances at a fixed 60 Hz using the discrete spring +rule, with interpolation between simulation states for rendering. Rendering pauses +offscreen and in hidden tabs, preserving the active animation +time so it resumes without a jump. Reduced motion displays the +complete static field immediately and disables decorative movement and scroll +dispersion. Resize preserves the shape's aspect ratio and does not replay the +entrance. The body cache contains points and transparent gaps. A spatial index +activates nearby particles for pointer interaction, and only affected regions are +repainted while those particles return; the other points remain in the native +cache. Construction masks are released after instrument sampling. Reusable filled +paths draw the notes and blue node with the same continuous decorative movement. +These choices reduce repeated work; +actual frame time still depends on the device and browser. Routine links use color and underline feedback. Anchor navigation uses native scrolling. The mobile menu uses a short opening transition, with reduced-motion support. Focus outlines remain visible. The mobile menu traps keyboard focus, closes on Escape or navigation, and releases the page when the viewport reaches desktop width. -Detail pages use one native parent link beside the retained wordmark. Its visible +Detail pages use one native parent link beside the wordmark. Its visible label is Back to Education, Back to Selected Research, or Back to Experience, with a small decorative left arrow. The destination is the originating section or entry, so it works after opening a detail page directly as well as after @@ -228,7 +281,7 @@ existing article and experience footer links remain available after long content ## Content and extension Page, domain, institution, project, and course titles use title case. Main-site -navigation and action labels use title case, including View Coursework, Read More, +navigation and action labels use title case, including Read More, Back to Education, Back to Selected Research, Back to Experience, Back to Top, Full Manuscript, Author Note, and View in Footnotes. Function words such as "to" and "in" stay lowercase. @@ -241,7 +294,7 @@ lowercase proper names. Use "and" in editorial category labels. Preserve official course and organization names, including their ampersands. The fellowship name is `Sequoia Fellow`, and its cohort is owned by the identity configuration in `app/lib/content/site.ts`. -Site metadata uses this identity configuration; About omits the fellowship. +Site metadata uses this identity configuration; the About hero displays Sequoia Fellow. Use English punctuation in public copy and metadata, including the architecture viewer. Straight and curly apostrophes and quotation marks are both valid. @@ -258,14 +311,14 @@ pages replace those primary links with parent navigation while retaining the hom wordmark: Coursework, three Selected Research manuscripts, eighteen Experience detail pages, and five Experience domain compatibility pages. About at `/` combines the introduction, Selected Research, Education with a coursework -entrance, and contact details in sections 01 through 04. The supplied first-person -introduction leads the page; both -Duke degrees, the advisor, scholarships, and all 49 courses remain -available. The canonical `/coursework/` page keeps the four subject groups, +entrance, and contact details in sections 01 through 04. The first-person +introduction opens the reading content beneath the hero; both +Duke degrees, the advisor, scholarships, and all 49 courses are +available. The canonical `/coursework/` page presents the four subject groups, returns to Education through its parent navigation, and appears in the sitemap. The homepage's `#coursework` anchor remains a valid entrance to this page. The `/education/` compatibility -page shares the About content without repeating the particle entrance, uses `/` +page shares the About content without repeating the clarinet hero, uses `/` as its canonical URL, and remains outside primary navigation and the sitemap. Experience contains all five domains and eighteen entries. The five category URLs provide compatibility pages with `/experience/` as their canonical URL and a @@ -276,10 +329,11 @@ has four project reading URLs. The other ten entries open dedicated detail pages under `/experience/`. About's Selected Research section presents three on-site manuscripts: Critical Dependence Stress for Portfolio Diversification, followed by the Asian-options and -Rough Heston papers. Each title and summary form one link whose text turns blue on -hover or keyboard focus. Summaries retain selected bold emphasis. Numerical guarantees retain their +Rough Heston papers. Each entry has a separate Read More manuscript link with blue +hover and keyboard-focus feedback. Titles and complete summaries remain outside +the link. Summaries retain selected bold emphasis. Numerical guarantees retain their model, horizon, payoff, and comparison scope. -It does not repeat the abstracts or add a separate action row. Titles use the same +It does not repeat the abstracts. Titles use the same 18px subheading role; supporting paragraphs use the 14px reading role. Thin horizontal rules separate entries. Typography, spacing, and wrapping adapt within the shared reading frame without introducing cards or changing the palette. @@ -341,7 +395,7 @@ the static export; article pages publish the text and its citation structure. Use shared tokens in `website/app/globals.css`, focused CSS Modules for local surfaces, and semantic HTML for new content. Match the existing type, reading measure, and spacing before adding a new component pattern. Keep interactions focused on navigation -and reading while preserving the signature interactive name entrance. Visual +and reading while preserving the approved interactive clarinet composition. Visual identity may be expressive; biographical and research claims remain measured. Review widths from 320 through 2560 pixels, including both sides of the 768, 1024, diff --git a/docs/maintenance.md b/docs/maintenance.md index 82f67ca..7cb448f 100644 --- a/docs/maintenance.md +++ b/docs/maintenance.md @@ -49,8 +49,8 @@ Paths below are relative to `website/`. `experience-domains.ts` defines the five Experience categories. All eighteen Experience entries have dedicated reading pages: four AI manuscripts, four legal manuscripts, and ten ordinary detail pages. -About's Education section links to the dedicated Coursework page through View -Coursework. The page preserves all 49 courses in four subject groups. +About's Education section links to the dedicated Coursework page through Read More. +The page presents all 49 courses in four subject groups. Compatibility routes remain functional: `/education/` renders shared About content; `/past-experience/` and `/now/` render Experience; the five @@ -143,7 +143,7 @@ navigation. `ExperienceSection` and `ExperienceDetails` keep all categories and detail pages consistent. `SectionDirectory` owns the shared numbered anchor directory for Experience and Coursework, including row rules, decorative indices, and View feedback for pointer, keyboard, and touch input. Browser interaction stays in focused client components; -the About entrance is isolated in `app/components/dithered-entrance/`. +the About hero is isolated in `app/components/clarinet-hero/`. `ResearchArticle` serves AI and Selected Research. The Markdown parser supports typed prose, emphasis, links, headings, tables, and formulas rather than arbitrary @@ -185,7 +185,7 @@ The Inter and Source Serif 4 variable fonts and their SIL Open Font Licenses liv in `public/assets/fonts/`. The site and architecture viewer share `inter.css`; Inter retains automatic optical sizing disabled, the optical axis fixed at 14, and lining numerals. The main site also loads `source-serif.css` for prose and -small indices. Keep the architecture tool UI and original particle wordmark in +small indices. The architecture tool UI, hero name, and institution names use Inter. Retain both font licenses when updating or redistributing the assets. `npm run build` runs Next.js, then `scripts/assemble-static.mjs`. Assembly includes diff --git a/website/README.md b/website/README.md index 5dd215d..b881734 100644 --- a/website/README.md +++ b/website/README.md @@ -7,8 +7,18 @@ across five fields. Dedicated readers present coursework and research articles with mathematical notation, chapter navigation, and legal footnotes. The visual system pairs Inter headings with Source Serif 4 prose, a warm paper -background, and a restrained editorial grid. The original interactive name entrance -opens About. Experience and Coursework share a numbered section directory. +background, and a restrained editorial grid. About opens with a two-column name +and recognizable clarinet hero. The dense Canvas 2D particle field aggregates on entry, +responds locally to the pointer, disperses with scrolling, and returns to its +shape, with six silver ring keys, joint bands and pads, two long rods, and +a dark elliptical bell cavity. All these features use separated points with +transparent gaps, including the darker ring-key centers and bell cavity. +The name and institutions use Inter; the credential roles use Source Serif 4. +Blue corner marks, a single solid blue oval, and four solid, continuously breathing +musical notes accompany the field. The open blue arc has larger, more closely +spaced points and retains its occlusion behind the instrument. +Selected Research and Education use the same Read More link pattern as Experience. +Experience and Coursework share a numbered section directory. ## Local development @@ -51,10 +61,27 @@ loaders. AI and Selected Research articles have deterministic importers; their generated JSON records the corresponding source version. Read the content ownership table in the maintenance guide before editing generated article data. -The main site's browser interactions are the name entrance, top-level navigation +The main site's browser interactions are the clarinet particle field, top-level navigation menu, article contents, and legal-note previews. Page content, ordinary navigation, -chapter links, and endnotes also work without JavaScript. The build includes the -standalone architecture viewer. +chapter links, and endnotes also work without JavaScript. The clarinet has a +same-shape SVG dot-pattern fallback and a complete static field for reduced motion. +The fallback retains transparent gaps during loading and without JavaScript. +Clean paths supply temporary sampling masks for the point field, which are released +after construction. The body, silver fittings, and bell use opaque point samples; +four musical notes and one blue oval use reusable filled `Path2D` shapes. + +Settled points share a transparent native-resolution cache. A spatial index +activates nearby particles for pointer input and repaints only affected regions. +Physics advances at 60 Hz, with rendering interpolated between simulation steps. +Moving points use subpixel coverage without blur filters; settled points return +to exact device-pixel positions. `alignCanvas` derives dimensions from the stage +and aligns the canvas box and origin with its backing store, including at +fractional pixel ratios. The body retains its subtle opacity breathing. +Notes continue their independent movement for as long as the hero is visible; +hidden tabs and offscreen heroes pause the active animation clock without a jump +on return. Reduced motion keeps the entire composition static. +Its implementation lives in `app/components/clarinet-hero/`. The build includes +the standalone architecture viewer. ## Maintained references diff --git a/website/app/components/AboutContent.tsx b/website/app/components/AboutContent.tsx index b5e80ae..cadf761 100644 --- a/website/app/components/AboutContent.tsx +++ b/website/app/components/AboutContent.tsx @@ -19,16 +19,21 @@ const researchSummaries: Record = { ], }; -export function AboutContent() { +function ReadingArrow() { + return ; +} + +export function AboutContent({ hasHero = false }: { hasHero?: boolean }) { + const Heading = hasHero ? "h2" : "h1"; return (
-

+ Thinking mathematically{" "} Working in AI -

+

Mathematics is how I make sense of things. I want to know which @@ -51,11 +56,12 @@ export function AboutContent() {

{selectedResearchProjects.map((project) => (
- -

{project.title}

- {researchSummaries[project.slug].map((paragraph, index) => ( -

{paragraph}

- ))} +

{project.title}

+ {researchSummaries[project.slug].map((paragraph, index) => ( +

{paragraph}

+ ))} + + Read More
))} @@ -87,7 +93,7 @@ export function AboutContent() {
- View Coursework + Read More
diff --git a/website/app/components/clarinet-hero/ClarinetHero.module.css b/website/app/components/clarinet-hero/ClarinetHero.module.css new file mode 100644 index 0000000..35a4487 --- /dev/null +++ b/website/app/components/clarinet-hero/ClarinetHero.module.css @@ -0,0 +1,127 @@ +.hero { + position: relative; + z-index: 1; + min-height: 100svh; + display: grid; + align-items: center; + background: var(--page); + padding: 48px 64px; +} + +.frame { + position: relative; + display: grid; + grid-template-columns: 44% 56%; + align-items: center; + width: min(100%, 1312px); + margin: 0 auto; + min-height: min(760px, calc(100svh - 96px)); + border-block: 1px solid var(--rule-strong); +} + +.frame::before, .frame::after { + content: ""; + position: absolute; + top: -48px; + bottom: -48px; + border-left: 1px solid var(--rule); + pointer-events: none; +} +.frame::before { left: 0; } +.frame::after { right: 0; } + +.copy { + position: relative; + z-index: 2; + padding: 64px 0 80px 28px; + opacity: var(--hero-copy-opacity, 1); + transform: translate3d(0, var(--hero-copy-y, 0px), 0); +} +.copy h1 { + margin: 0; + font-family: var(--font-text); + font-size: clamp(3.5rem, 6.7vw, 6.5rem); + font-weight: 500; + letter-spacing: -.065em; + line-height: 1.02; +} +.copy h1 span { display: block; } +.credentials { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 24px; + width: min(100%, 330px); + margin: 36px 0 0; + padding: 18px 0 0; + border-top: 1px solid var(--rule-strong); + list-style: none; + font-family: var(--font-prose); + font-size: clamp(18px, 1.5vw, 22px); + line-height: 1.45; + color: var(--supporting-ink); +} +.credentials li { display: grid; gap: 3px; } +.credentials strong { + color: var(--ink); + font-family: var(--font-text); + font-size: 16px; + font-weight: 500; + letter-spacing: -.01em; +} +.visual { position: relative; min-width: 0; padding: 26px 0; } +.stage { + --canvas-scale: 1; + --canvas-left: 0; + position: relative; + width: 100%; + aspect-ratio: 1286 / 1223; + touch-action: pan-y; +} +.canvas, .fallback { + display: block; + position: absolute; + inset: 0; + left: calc(100% * var(--canvas-left)); + width: calc(100% * var(--canvas-scale)); + height: 100%; +} +.canvas { right: auto; bottom: auto; } +.fallback { pointer-events: none; } +.canvas[data-rendered="true"] + .fallback { display: none; } +.markers { + position: absolute; + inset: 0; + pointer-events: none; + opacity: var(--hero-frame-opacity, 1); +} +.markers i { position: absolute; width: 2px; height: 11px; background: var(--accent); } +.cornerTL { left: 5.1%; top: 4.6%; } +.cornerTR { right: 5.1%; top: 4.6%; } +.cornerBL { left: 5.1%; bottom: 4.9%; } +.cornerBR { right: 5.1%; bottom: 4.9%; } +@media (768px <= width < 1024px) { + .hero { padding: 32px; } + .frame { min-height: calc(100svh - 64px); grid-template-columns: 43% 57%; } + .copy { padding-left: 20px; } + .copy h1 { font-size: clamp(3rem, 6.5vw, 4.5rem); } +} +@media (width < 768px) { + .hero { min-height: 100svh; padding: 24px 20px; } + .frame { grid-template-columns: 1fr; min-height: 0; padding: 44px 0 22px; } + .frame::before, .frame::after { top: -24px; bottom: -24px; } + .copy { padding: 0 12px; } + .copy h1 { font-size: clamp(3.25rem, 13.5vw, 5.5rem); line-height: 1.03; } + .credentials { margin-top: 24px; padding-top: 14px; width: min(100%, 290px); font-size: 18px; } + .visual { width: min(100%, 480px); margin: 24px auto 0; padding: 0; } + /* Reframe the surrounding whitespace on phones, without changing the mother shape. */ + .stage { + --canvas-scale: 1.3; + --canvas-left: -.25; + aspect-ratio: 1286 / 1590; + overflow: hidden; + } +} +@media (prefers-reduced-motion: reduce) { + .copy { opacity: 1 !important; transform: none !important; } + .markers { opacity: 1 !important; } +} diff --git a/website/app/components/clarinet-hero/ClarinetHero.tsx b/website/app/components/clarinet-hero/ClarinetHero.tsx new file mode 100644 index 0000000..1a16323 --- /dev/null +++ b/website/app/components/clarinet-hero/ClarinetHero.tsx @@ -0,0 +1,78 @@ +"use client"; + +import { useEffect, useRef } from "react"; +import { ARC, BANDS, BEAMED_NOTE, BELL_OPENING, BELL_RIM, BODY, DETAILS, NODE, NOTE, PADS, PAD_SHADOWS, RINGS, RODS, TIP_OPENING, TRAILS, VIEW_HEIGHT, VIEW_WIDTH, cubicPoint } from "./shape"; +import { mountClarinet } from "./renderer"; +import styles from "./ClarinetHero.module.css"; + +export function ClarinetHero() { + const heroRef = useRef(null); + const canvasRef = useRef(null); + + useEffect(() => { + if (!canvasRef.current || !heroRef.current) return; + const controller = mountClarinet(canvasRef.current, heroRef.current); + return () => controller.destroy(); + }, []); + + return ( +
+
+
+

Theodore{" "}Ouyang

+
    +
  • Duke{" "}alumnus
  • +
  • Sequoia{" "}Fellow
  • +
+
+
+
+