motionsites中文站 / Nightmast Studio
Nightmast Studio
aadmin2026-11-05 发布未分类13 阅读

Nightmast Studio

类型: · 收录:2026-11-05 01:13:02 · 源码:会员可见

[hidecontent type="vip1"]
Build a single standalone index.html (no build step, no external images, no video, no generated assets) for a fictional creative studio called "Nightmast Studio" (Hobart, Australia). Everything visual must be produced in code: Three.js for the 3D hero, Canvas 2D for all textures, inline SVG for illustrations, WebAudio for sound. There is NO background video and NO CloudFront/remote media of any kind; do not add any.

=====================================================================
1. LIBRARIES & FONTS
=====================================================================
- Three.js r160 via import map (ES modules):
  "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js"
  "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
  Use EffectComposer, RenderPass, UnrealBloomPass, OutputPass from addons.
- Google Fonts (one link, display=swap):
  Anton; DM Mono 400/500; Instrument Serif regular + italic; Inter 400/500/600; Yellowtail.
- Font roles: --sans Inter; --serif "Instrument Serif"; --mono "DM Mono"; --script Yellowtail. Anton is only used on canvas textures.
- Before drawing any canvas textures, await document.fonts.load() for: '230px Yellowtail', '170px Anton', '500 76px Inter', '600 78px Inter', '500 15px "DM Mono"', 'italic 40px "Instrument Serif"' (race with a 3.5s timeout).

Title: "Nightmast Studio — Open After Dark". theme-color #050819. Viewport meta with viewport-fit=cover.

=====================================================================
2. PAGE STRUCTURE (DOM)
=====================================================================
body (overflow hidden, bg #050819, text #e9ecff, Inter, antialiased) contains:
1. <canvas id="gl"> fixed full-screen, touch-action:none, tabindex 0, aria-label "A radio tower at night with a spinning ring of menu panels".
2. #loader: fixed full-screen, z 50, background radial-gradient(120% 90% at 50% 100%, #1b1650 0%, #070b22 55%, #03050f 100%). Centered column: big percentage (Instrument Serif italic 54px, counts 0→100), 160×2px bar (fill gradient #9fb0ff→#ffd84a), and the text "Raising the mast…" (DM Mono 12px, uppercase, letter-spacing .14em, muted). Fades out (opacity .9s) when ready.
3. #hud (fixed overlay, pointer-events none except controls; class "hide" fades it out with opacity .5s):
   - top-left round glass button (38px circle, bg rgba(160,176,255,.12), 1px border rgba(190,205,255,.22), backdrop blur 10px) with a hamburger icon (3 lines) → opens menu.
   - top-center logo: a 12×12 pixel-art crescent moon SVG (pink #ff9ad5 pixels plus a small yellow #ffe27a pixel star), 44px, drop-shadow 0 0 10px rgba(255,140,210,.55). Pixel map, rows top→bottom (X = pink, * = yellow):
     "....XXXX....","..XXXX......",".XXXX.......",".XXX......*.","XXXX.....***","XXXX......*.","XXXX........","XXXXX.......",".XXXXX....XX",".XXXXXXXXXX.","..XXXXXXXX..","....XXXX...."
     Clicking the logo gives the ring a spin boost (spinVel += 2.4).
   - top-right: plain icon button for sound (muted speaker icon / speaker-with-waves when on; on = yellow-tinted bg rgba(255,216,74,.2), border rgba(255,216,74,.5)), and a "make a wish" button (sparkle + diagonal line icon) that spawns a shooting star and plays a chime.
   - bottom-left mono label: "Nightmast Studio" (bold) / "42.88°S · 147.33°E".
   - bottom-right mono label: "Hobart · <live time>" / "Open after dark". Time via Intl.DateTimeFormat('en-AU',{hour:'numeric',minute:'2-digit',timeZone:'Australia/Hobart'}), refreshed every 15s.
   - bottom-center two pills: "Scroll to spin" + "Click to explore" (touch devices: "Swipe to spin" + "Tap to explore"). Pill: 11px, padding 7px 12px, radius 99px, bg rgba(10,16,48,.55), glass border, blur 8px.
   - Corner labels hidden ≤820px.
4. #tip: white pill tooltip (11.5px, weight 500, text #141414, shadow) that follows the cursor 34px above it while hovering a 3D panel, e.g. "Explore About Us". Hidden on touch.
5. #menu: dropdown sheet under the menu button (width min(300px, 100vw-36px), radius 22px, bg rgba(12,18,52,.82), blur 16px, scale/fade in). Header "Tune in to" (mono uppercase). Items in order: About Us, Our Work, Services, Pricing, Contact. Each has a 14px rounded colour swatch with glow in the page colour and a right-aligned mono number 01–05. Clicking an item opens that page. Clicking outside or pressing Esc closes the menu.
6. #card (the page "sheet"), role dialog: fixed, z 30, left 50% translateX(-50%), top var(--gutter); width min(960px, calc(100vw - var(--side)*2)); height calc(100dvh - var(--gutter)*2); border-radius var(--radius); background var(--pg); colour var(--ink); shadow 0 30px 90px rgba(0,0,0,.45). Hidden by default (opacity 0 / visibility hidden; class "vis" shows it; class "fade" adds a .16s opacity transition).
   Tokens: --gutter 22px, --side 56px, --radius 30px. At ≤820px: --gutter 12px, --side 14px, --radius 24px.
   Inside:
   - .card-head: absolute top, height 64px, gradient from var(--pg) to transparent. Left: menu button (in page colours) + crumb label (mono 10.5px uppercase, letter-spacing .18em, opacity .75; hidden on mobile). Center: the same pixel logo (no glow; acts as "back to tower"). Right: sound button + "Back to tower" pill button (mono 10.5px uppercase, bg rgba(255,255,255,.24), 1px border in ink at 20% alpha; on hover inverts to ink bg / page-colour text). On mobile the sound and star buttons are hidden in the card header.
   - .scroller: absolute inset 0, overflow-y auto, overscroll contain, padding 96px clamp(20px,5vw,64px) 64px (mobile 84px 20px 48px), thin 4px scrollbar in ink colour.

=====================================================================
3. PAGES (colours) — panel colour = page colour, identical hex
=====================================================================
about:    label "About Us", bg #a4d65e, ink #1f3d10, tooltip "Explore About Us"
pricing:  label "Pricing",  bg #ffd84a, ink #2a1d00, tooltip "Explore Pricing"
work:     label "Our Work", bg #ff5c8d, ink #4a0019, tooltip "Explore Our Work"
services: label "Services", bg #94e6cc, ink #0b4a44, tooltip "Explore Services"
contact:  label "Contact",  bg #ff7a3d, ink #3a1200, tooltip "Say hello"
Per page set CSS vars on #card: --pg = bg, --ink = ink, --pg-line = ink at 20% alpha, --pg-soft = rgba(255,255,255,.24).

Page typography/components:
- h1.big: Instrument Serif italic, clamp(44px,6vw,76px), line-height 1.
- .kick: mono 11px uppercase, letter-spacing .16em, opacity .75, margin-bottom 14px.
- h2: Inter 400, clamp(28px,3.4vw,44px), line-height 1.08, letter-spacing -.02em. A second line is wrapped in <em> → Instrument Serif italic at 1.08em.
- h3: 500, 19px. p: 15px / 1.6, max-width 62ch. p.sm: 13px, opacity .82.
- .sec: padding 44px 0 (mobile 34px), top border 1px var(--pg-line) (none on the first).
- .row2: grid 1.05fr 1fr, gap 40px. .g2 / .g3: 2 or 3 equal columns, gap 14px. All collapse to 1 column ≤820px.
- .tile: bg var(--pg-soft), 1px border var(--pg-line), radius 18px, padding 22px.
- .btn: ink background, page-colour text, 13px 20px padding, radius 12px, 14px/500, lifts 2px on hover. .btn.round: radius 99px.
- .lnk: underlined, offset 4px.
- .art: radius 18px, aspect 4/5, holds an SVG. .cover: radius 14px, aspect 16/10, holds an SVG. .cap: 12.5px caption, opacity .8.
- Reveal animation: every block has class .rv (opacity 0, translateY 22px, transition .7s cubic-bezier(.2,.8,.2,1), delay calc(var(--d)*70ms)). Add class "show" on #card after it opens → visible. Class "hide-content" fades .rv out quickly (.25s, translateY -8px) before closing.
- Respect prefers-reduced-motion (no translate; shorter flights: open .6s, close .5s).

=====================================================================
4. PAGE TEMPLATES (render from a CONTENT object; HTML-escape all strings)
=====================================================================
ABOUT:
- Sec 1: h1 "About Us"; row2: h2 (h1a / em h1b) | p1 + p2; button cta (opens contact).
- Sec 2: h2 (h2a / em h2b); g2 grid of 4 tiles (h3 t, p d, link l → opens work, optional small note n under it); link "explore" → opens services.
- Sec 3: h2 (h3a / em h3b); g3 of steps (h3 t + p.sm d).
- Sec 4: h2 peopleT; row2: illustration (kind 3, seed 7, accent #ffd84a, alt #a4d65e) + caption | h3 em names + bio1 + bio2.
- Sec 5: row2: h2 (endA / em endB), endP, button cta2 (contact), link estimate (pricing) | illustration (kind 2, seed 21, #ffd84a, #ff5c8d) + caption "after".
PRICING:
- kicker, h1 "Pricing", h2 title, p sub, p.sm note.
- Tab row of 6 pills (category names; selected = ink bg / page-colour text).
- Category body: h3 title (26px/400), p.sm sub, g3 of price tiles: h3 name, price (22px), p.sm description, bottom row button "Choose this +" (top border). Clicking toggles selection: tile inverts to ink bg, "+" rotates 45°. Selections persist across tabs.
- p.sm pick. Sec: h3 mindT, p.sm mindP, chips list of chosen items "Name · Price" (or "Nothing picked yet.").
- Sec: flex row: h2 fitT + p fitP | round button talk (contact); p.sm costs.
SERVICES:
- kicker, h1 "Services"; row2: h2 (h2a / em h2b) | intro.
- Accordion of 5 rows (first open): button grid [56px number "01".."05" mono | title clamp(22px,2.6vw,32px) | "+" that rotates 45° when open]; body animates grid-template-rows 0fr→1fr (.45s). Body grid [56px spacer | cover SVG + label "Good for" + good | desc + label "What we cover" + cover]. Cover kinds per row: 2,0,3,1,0; seeds 40–44; accent #0b4a44, alt #ff5c8d.
- Sec: h2 wayT; g3 tiles with label "Step 01..03", h3 t, p.sm d.
- Sec: h2 seeT; g3 links (open work) with cover (seeds 60–62, kinds 2,1,0), h3 n, p.sm t.
- Sec: h2 (endA / em endB), p endP, button cta (contact).
WORK:
- kicker, h1 "Our Work"; row2: h2 sub | intro.
- g2 grid of 7 articles: cover (seeds 80–86, kinds 3,2,1,2,0,1,3, accent #ffd84a, alt #94e6cc), h3 n, p.sm d. Then p.sm foot, button cta (contact).
CONTACT (fixed copy, not in CONTENT):
- kicker "NIGHTMAST STUDIO / SAY HELLO", h1 "Contact"; row2:
  left: h2 "The lights are on." / em "Come say hello."; p "Tell us a little about what you’re making. We read every note and reply within a day or two — usually after dark."; p.sm "hello@nightmast.studio" / "Studio 4, 12 Salamanca Lane, Hobart TAS".
  right: form (inputs: "Your name", "Email address" (type email), textarea "What are you working on?" 5 rows, button "Send it into the night"). Inputs: page-soft bg, 1px line border, radius 12px, padding 14px 16px; focus border = ink. On submit preventDefault, hide form, show "Message received — talk soon." (Instrument Serif italic 30px). No network request.

Links/buttons with data-open="key" open that page (if a page is already open: close back to the tower first, then open the new one). Elements with data-close go back to the tower.

=====================================================================
5. CONTENT (use EXACTLY these strings)
=====================================================================
const CONTENT = {
  about: {
    h1a: "Stand out after", h1b: "the sun goes down",
    p1: "Nightmast Studio is an independent creative studio in Hobart. We shape brands, websites, packaging and content that help people remember your business and give them a reason to drop back in.",
    p2: "Opening a venue, launching a new range or outgrowing your site? Bring us the whole project or the piece that needs care.",
    cta: "Send us your next big idea",
    h2a: "Where we shine", h2b: "for growing teams.",
    services: [
      { t: "Packaging", d: "Give your product a glow-up on the shelf. Boxes, labels and print that join the brand with the details a buyer wants to know.", l: "View Lantern & Wick Co", n: "Work made in-house with L&W Co." },
      { t: "Websites", d: "Make the next click easy to take. Website design, development and ecommerce, with clean content and a path to book, visit or order.", l: "View the Moonshiner website project" },
      { t: "Branding", d: "Give your business a sharp direction and an unmistakable identity. Brand strategy, naming, logos and guidelines that hold up across every surface.", l: "View The Ember Parlor" },
      { t: "Photography & video", d: "Show people what makes your offer worth a second look. Art direction, product photography, films and content for premieres, websites and social.", l: "View the Night Owls project" }
    ],
    explore: "See all of our services.",
    h3a: "A clear path.", h3b: "Humans you can call on.",
    steps: [
      { t: "Begin with your business.", d: "We ask what has to change, who you want to reach and what people should do after that. It gives the creative work a clear goal." },
      { t: "Know what’s expected.", d: "We agree the scope, deliverables, timing and fee before work starts. You can begin with one focused piece or a joined-up launch." },
      { t: "Create it as one.", d: "You work directly with the people designing and building your project. We explain each choice, work through your notes and prepare the final files to launch." }
    ],
    peopleT: "The people behind the glow.",
    caption: "Maeve & Tomasz, at 2am.",
    names: "Maeve & Tomasz.",
    bio1: "We’re Maeve Calder and Tomasz Wren, the co-founders of Nightmast Studio. We stay close from the first conversation to the final pixels, so the thinking behind your project carries all the way into the work.",
    bio2: "We’ve been friends for 19 years. There’s a rhythm to how we work together: talking ideas through, poking at the obvious and being honest about what could be sharper. We bring you into that conversation.",
    endA: "What are you", endB: "dreaming up now?",
    endP: "You don’t need a polished brief. Tell us what you’re making, what needs to work smarter and when you need it. We’ll talk through where we can help and what comes after.",
    cta2: "Send us your next big idea",
    estimate: "Get a ballpark price estimate",
    after: "Long after dark."
  },
  pricing: {
    kicker: "A PRICE GUIDE", title: "An honest look at prices.",
    sub: "Select a service, find a starting point, then let’s chat.",
    note: "Estimates only · AUD, excluding GST. Your final quote depends on the brief. We’ll agree it with you before work begins.",
    cats: [
      { name: "Websites", title: "Websites", sub: "From one standout page to a complete web store.", items: [
        { n: "Starter page", p: "2,400–4,200", d: "One simple page for your brand, an event or a new campaign." },
        { n: "Showcase website", p: "5,200–7,800", d: "A fuller website with space for your services, story and bookings." },
        { n: "Online shop build", p: "6,800–9,600", d: "An online store that weaves your products, browsing and checkout together." } ] },
      { name: "Branding", title: "Branding", sub: "A new logo, or a more complete style for your business.", items: [
        { n: "Logo design or tidy-up", p: "600–1,400", d: "Polish what you have or draw a fresh logo with versions for everyday use." },
        { n: "Brand identity", p: "2,200–3,600", d: "Your logo, colours and typeface, with a guide that keeps everything consistent." } ] },
      { name: "Packaging", title: "Packaging", sub: "Give your product a bold, well-considered spot on the shelf.", items: [
        { n: "One product", p: "700–1,400", d: "A label, tin or pouch design for a single product." },
        { n: "A product suite", p: "3,200–4,200", d: "A matching packaging style across a little family of products." },
        { n: "Packaging + launch", p: "3,600–5,200", d: "Packaging design with extra campaign artwork to help launch the line." } ] },
      { name: "Graphic Design", title: "Graphic Design", sub: "The handy design pieces that keep your business humming.", items: [
        { n: "Everyday essentials", p: "100–400", d: "Business cards, letterhead and simple branded materials." },
        { n: "Print, signage & menus", p: "200–700", d: "A poster, sign or menu that gets your point across." },
        { n: "Social & digital", p: "100–900", d: "A single graphic or a small kit of assets for your online channels." } ] },
      { name: "Photography", title: "Photography", sub: "Show your products, people or venue at their best.", items: [
        { n: "Evening shoot", p: "950–1,600", d: "A short session for a small collection of products or one simple idea." },
        { n: "Half-day shoot", p: "1,600–2,600", d: "More time for different products, setups or a fresh pile of content." },
        { n: "Extended shoot", p: "2,600–4,200", d: "A longer shoot for a larger range, campaign or several setups." } ] },
      { name: "Videography", title: "Videography", sub: "Short clips, product stories and films with a little more time.", items: [
        { n: "Social reels", p: "1,100–2,400", d: "Quick vertical videos built for your feeds." },
        { n: "Product or promo video", p: "1,600–2,700", d: "A focused video to introduce a product, service or event." },
        { n: "Brand film", p: "3,200–4,400", d: "A longer piece to tell your story and show what makes your business tick." } ] }
    ],
    pick: "Grab the closest fit. You can choose anything from more than one category.",
    mindT: "What you have in view", mindP: "These are starting points. We’ll shape the entire project together.",
    fitT: "Let’s find your best fit.", fitP: "Have a small brief, a bigger dream or a budget in mind? Tell us what you’re picturing.",
    talk: "Let’s chat",
    costs: "Printing, production, hosting and any outside costs are separate. We’ll flag anything your project needs in our quote."
  },
  services: {
    kicker: "NIGHTMAST STUDIO / WHAT WE DO",
    h2a: "Shape the brand.", h2b: "Switch it on now.",
    intro: "Branding, websites, packaging and photography, made together in Hobart. Five ways we help businesses of any size take shape, launch and keep glowing. Bring us a whole project or one part that needs some love.",
    items: [
      { name: "Brand", good: "New businesses, product launches and brands ready for a reboot.", d: "Get clear on what you stand for, then give it a name, a look and a voice people can remember. We connect the strategy with the identity, so your brand feels consistent wherever it turns up.", cover: "Brand strategy, positioning, naming, visual identity and brand guidebooks." },
      { name: "Digital", good: "Businesses that need a new website, a sharper store or a smoother buyer journey.", d: "Make it simpler for people to understand your offer and take the next step. We plan, design and build websites around what your visitors need to find, do or buy.", cover: "UX/UI, website design and development, ecommerce and content hierarchy." },
      { name: "Content", good: "Brands introducing products, telling their story or keeping feeds buzzing.", d: "Show what makes your business worth a second look. We develop the ideas, shape the shoot and create imagery and content that carries the same personality across your site, campaigns and social channels.", cover: "Art direction, photography, video, campaign concepts and social assets." },
      { name: "Graphic & Packaging", good: "Product ranges, hospitality venues and teams with a distinct piece to make.", d: "Give the things people hold, read and notice the same care as your brand. From a product on a shelf to a menu on the table, we balance character with information that is simple to use.", cover: "Packaging, labels, menus, signage, prints, pitch decks and focussed design projects." },
      { name: "Strategy & Growth", good: "Businesses planning a launch or choosing where to focus next.", d: "Turn a bold ambition into a realistic plan. We connect your audience, message and channels, then use feedback and performance to decide what to sharpen as the work goes live.", cover: "Campaign strategy, digital strategy, launch planning and constant fine-tuning." }
    ],
    wayT: "A calm, clear route.",
    ways: [
      { t: "Learn more.", d: "Your business, your audience and what needs to evolve." },
      { t: "Draft.", d: "Agree the scope and fee, then explore a direction together." },
      { t: "Ship.", d: "Design, refine and hand over, with space for notes along the way." }
    ],
    seeT: "See it in our work.",
    see: [
      { n: "The Ember Parlor", t: "Brand & website" },
      { n: "Lantern & Wick Co", t: "Packaging & imagery · in-house work" },
      { n: "Night Owls", t: "Website, menus & late-night content" }
    ],
    endA: "Begin with the thing", endB: "you want to switch.",
    endP: "You don’t need to choose the perfect service. Tell us what you’re working towards and we’ll work out where to begin.",
    cta: "Let’s chat about your project"
  },
  work: {
    kicker: "NIGHTMAST STUDIO / HOBART, AUSTRALIA",
    sub: "The stories behind the things we light.",
    intro: "Explore the client context, creative decisions and finished work behind our hospitality, lifestyle and product projects, then enter the studio gallery for films and photography.",
    items: [
      { n: "Nightmast Gallery", d: "A gallery wall of social films, candid photography and campaign imagery." },
      { n: "Night Owls", d: "A connected presentation of the food, the people and the practical information that turns curiosity into a seat." },
      { n: "Lantern & Wick Co", d: "Emberleaf’s brand refresh and Moonwater’s range extension connect product development with the way customers discover and choose a new candle at night." },
      { n: "The Ember Parlor", d: "One brand story, carried from the very first glance to the small print on the box." },
      { n: "Moonshiner", d: "A no-nonsense website build, with search visibility, after-hours bookings and visits to the Salamanca wine bar at its heart." },
      { n: "Southern Lights Lamp House", d: "Brand direction, logo refinement, research-led copywriting, photo direction and a website built around direct bookings." },
      { n: "Inkwell Co", d: "A colourful world for collectors, with a clear route from discovery to the detail of a poster. The project is still in the works and the business has not yet launched." }
    ],
    foot: "Lantern & Wick Co includes work developed in-house at L&W. Each project includes its own context and credits.",
    cta: "Send us your next big idea."
  }
};

=====================================================================
6. CODE-DRAWN ILLUSTRATIONS: nightArt(seed, accent, alt, kind)
=====================================================================
Returns an SVG with viewBox 0 0 400 500, preserveAspectRatio xMidYMid slice. Use a seeded LCG (s = s*1664525+1013904223, seed*997+13).
- Background: vertical gradient #050819 → #151d55 (60%) → #3a2468.
- 46 random stars (r .3–1.6, opacity .3–1).
- A crescent moon at a random spot (x 60–340, y 50–110): soft radial glow r70, a disc r20 #f6f1e2, and an offset dark disc r18 #0b1030 to make the crescent.
- kind 0: city skyline of #0a0f2e blocks with random lit windows (accent or #ffe9b0).
- kind 1: three rounded candle/bottle shapes (accent, alt, #94e6cc), each with a dark label, a yellow flame and a faint glow; dark floor strip.
- kind 2: neon sign: rounded rect stroke in alt (7px, plus a 22px stroke at 18% for glow) with the text "open late" in Yellowtail 74px in accent; dark floor.
- kind 3: rooftop at night: dark floor, a street lamp with a warm glow, two simple silhouette figures (rounded bodies + round heads in #121a46 / #161f55).

=====================================================================
7. THREE.JS SCENE
=====================================================================
Renderer: antialias, pixelRatio min(dpr, 2) (1.75 on touch devices), toneMapping NoToneMapping (so the 3D panel colour exactly matches the CSS page colour at the hand-off). Camera: PerspectiveCamera fov 36 (40 when width < 700), near .1, far 3000.
Post: EffectComposer on a HalfFloat render target with samples 4 → RenderPass → UnrealBloomPass(strength .55 (.45 on touch), radius .5, threshold .9) → OutputPass.

SKY (skyGroup follows the camera position each frame; on portrait screens, aspect < .8, rotate skyGroup.rotation.y = -.26 to bring the moon into frame):
- BackSide sphere r900, ShaderMaterial using d = normalize(position), e = d.y:
  col = mix(#2c2468, #0e1748, smoothstep(-.04,.24,e)); col = mix(col, #03061a, smoothstep(.2,.8,e)); col = mix(col, #8a3f74, (1-smoothstep(-.12,.1,e))*.75) for a warm city glow at the horizon.
  Faint Milky Way: bd = dot(d, normalize(vec3(.55,.35,.76))); band = exp(-bd*bd*14)*smoothstep(0,.35,e); col += vec3(.10,.09,.22)*band*(.4+fbm(d*4+3.1))*.9; plus subtle fbm nebula tint vec3(.05,.02,.08).
  Moon halo: m = max(dot(d,moonDir),0); col += vec3(.55,.6,.8)*(pow(m,120)*.25 + pow(m,14)*.05).
  Use a 5-octave value-noise fbm.
- MOON_DIR = normalize(-0.4, 0.42, -1).
- Stars: 2400 points (1400 on touch) on a radius-800 upper hemisphere (y ≥ -0.02). Size = random^6*3.4+.7. Additive ShaderMaterial: twinkle 0.65+0.35*sin(time*(1.2+fract(phase)*2.5)+phase), fade near the horizon, soft round sprite, bluish white; stars bigger than 2.6 are warm white and 1.8× brighter (so they bloom).
- Moon: Sprite with a canvas texture (512px, disc r200, radial gradient #fffaf0 → #e9e6dc → #bfc3d6, 26 random grey craters + one large mare), colour (1.05,1.05,1.02), at MOON_DIR*700, scale 34. Halo sprite: additive radial gradient rgba(200,215,255,.55) → transparent, scale 200, opacity .7.
- Clouds (keep minimal): only 2 sprites in world space, canvas 512×256 with ctx.filter blur(10px), 18 overlapping radial puffs (x 110–400, r 50–96, colours rgba(170,182,230,.32) → rgba(92,104,160,.2) → transparent). Cloud A at (-15, 5.2, -26), scale 15×7.5, opacity .8; cloud B at (18, -2.4, -40), scale 13×5.5, opacity .5. Drift +x at .12–.2 units/s, wrap within ±14.
- Shooting stars: Points trail of 26 particles (head plus history at 12ms steps, fading size and alpha, additive, colour (1.4,1.4,1.6)). Spawn on the sky sphere (azimuth around -z ±.55 rad, elevation .22–.52, radius 600), velocity ±(380–580) on x and -(140–260) on y, life .9–1.3s, alpha = sin(progress*PI). Auto-spawn every 7–16s (first at 4s), and on the star button.
- Environment map: PMREMGenerator.fromScene of a small scene: sky sphere (same shader ×2.4), an emissive moon ball (9,9,10) at MOON_DIR*80, a warm horizon band cylinder (1.1,.45,.7) at y -12, and a bright key plane (1.6,1.7,2.4) at (-40,50,40). Set scene.environment.
- Lights: HemisphereLight(0x5a6ad0, 0x1a0c26, .9); DirectionalLight 0xc8d4ff 2.0 at (-9,11,10); PointLight 0xff8a4a (12, distance 9, decay 1.8) at (0,-2.4,4.2); PointLight 0x7a86ff (40, 20, 1.6) at (6.5,3.5,-3); PointLight 0xff6fb8 (18, 16, 1.6) at (-6.5,-1,-2).

TOWER (group at the origin). Metal = MeshStandardMaterial colour 0xd4dbef, metalness .88, roughness .3, envMapIntensity 1.15.
- Body: open cylinder r2.12, h1.92, 128 segments. Canvas stripe texture: 96 vertical ribs (white highlight to black shadow gradient per rib), 3 horizontal seams, 70 random small windows; matching emissive map with the windows lit warm #ffcf7a (some #9fd0ff), emissiveIntensity 1.2.
- Upper skirt lathe: (2.12,.94) (2.08,1.06) (1.86,1.22) (1.58,1.33) (1.5,1.36), roughness .25.
- Top text band: open cylinder r1.5, h.36, y 1.54. Canvas 2048×128 bg #efe8d6, text "NIGHTMAST STUDIO" in 600 78px Inter #14161f, repeated with thin vertical separators, repeat.x 3, emissive = map at .22. Rotates at -t*.12.
- Saucer cap lathe: (1.42,1.72) (1.64,1.78) (1.8,1.86) (1.83,1.93) (1.74,2.02) (1.36,2.12) (.86,2.2) (.42,2.26) (0,2.28); dark metal disc r1.45 underneath at y 1.72.
- Rim lights: InstancedMesh of 44 spheres r.028 on radius 1.84 at y 1.9, MeshBasicMaterial colour (3.2,3.0,2.6) (blooms). Chasing animation per instance: v = .25+.75*pow(.5+.5*sin(i*.55 - t*4), 6).
- Contact ring: open cylinder r2.5, h.44, y -1.17. Canvas bg #ff5a1f, text "CONTACT US" in 400 86px Anton, colour #2b0800, no separators, repeat.x 4, emissive = map at .55 (pulses ±.05; +.5 on hover). Texture offset scrolls at -t*.02. Orange torus lips (r2.5, tube .035, colour 0xff7a3d, emissive 0xff5a1f .8) at y -.95 and -1.39; a dark ring (inner 2.12, outer 2.5, #40263a) on top at y -.95. Clicking or hovering the ring = the "contact" target.
- Underside lathe (dark metal 0x5a6280, roughness .45): (.66,-2.0) (.95,-1.92) (1.56,-1.8) (2.16,-1.64) (2.44,-1.5) (2.5,-1.4). Underside lights: 28 spheres r.03 on radius 1.9 at y -1.66, cyan breathing.
- Shaft: open cylinder r .64 (top) → .82 (bottom), height 70, centre y -37, metal with vertical rib texture (repeat 2×18). Six tiny warm lights spiralling down it.
- ANTENNA (redesigned): stacked tapered segments (.42→.36 from 2.24–2.5; .2→.17 from 2.5–3.0; .1→.085 from 3.0–3.9; .05→.04 from 3.9–4.75) with torus collars at 2.5, 3.0, 3.42, 3.9, 4.3; four thin guy-wire lines (0x8792bb, opacity .55) from radius 1.0 at y 2.2 to (0,3.9,0).
  - Orbiting halo at y 3.42: thin torus r.52 (colour (.6,.7,2.2)) with three glowing beads (#7fe6ff, #ff9ad5, #ffe27a ×4) spaced 120°; rotates y = t*.9 and wobbles z = sin(t*.6)*.12.
  - Small radar dish at y 2.82 on an arm, sweeping rotation.y = sin(t*.35)*2.4, with a tiny glowing red feed.
  - Red aviation beacon sphere r.07 at y 4.8 plus an additive red glow sprite; blinks on when sin(t*3.2) > .55 (colour (6.4,.35,.25) on, ~8% when off); glow scale .9 + .5*blink.
  - Broadcast pulses: 3 thin horizontal tori (tube .006, additive, colour (.9,1.1,2.6)) at y 4.72, phase offsets 0/⅓/⅔; scale .12 + ph*2.1, opacity (1-ph)²*.5, with ph = (t*.38 + offset) % 1.

PANELS (the ring of menu cards):
- Ring group with 4 slots at angles 0, π/2, π, 3π/2 in order about, pricing, work, services. Slot position (sin θ·R, 0, cos θ·R), rotation.y θ.
- Panel geometry is custom (do NOT use ExtrudeGeometry): w 3.12, h 1.36, corner radius .2, thickness .1, grid 72×24. Build a flat grid; project grid points outside the rounded corners onto the corner arcs (clamp to the inner rect, then push to radius r). Front face at z = +t/2, back face at z = -t/2 (mirrored UVs), and side walls built from the grid's boundary loop (4 unique vertices per edge, wound outward). Two material groups: faces (0) and sides (1).
- Bending: for each flat vertex (x,y,z): a = x/R, rr = R+z → (rr·sin a, y, rr·cos a − R), with R = 2.3. Store both the flat and bent arrays; the geometry starts bent.
- Panel material: MeshStandardMaterial with map = emissiveMap = the canvas texture, emissive white at .42, roughness .5. Side material: page colour ×.7 with a little emissive. Inject via onBeforeCompile after #include <dithering_fragment>:
  gl_FragColor.rgb += gl_FragColor.rgb * uGlow * 0.35;
  gl_FragColor.rgb = mix(gl_FragColor.rgb, uSolid, uFlat);
  where uSolid = the page colour as a THREE.Color (linear), uFlat 0→1 during the flight, uGlow = hover.
- Panel textures: canvas 1024 × 1024·h/w. Common: rounded rect in the page colour, paper grain (2200 tiny dots), top sheen gradient, inner white stroke, small DM Mono 15px fine print at top ("NIGHTMAST STUDIO — EST. AFTER DARK" left, "CH. 0n / 42.88°S" right) and a footer ("NIGHTMAST STUDIO" · "TUNED IN SINCE 2007" (pricing: "SCOPE. PLAN. GLOW.") · "TAP TO OPEN →").
  - About: "About" (230px Yellowtail) + "Us" (190px, offset right and down), rotated -.07 rad, in ink, with a hand-drawn underline curve.
  - Pricing: "Pricing" in 250px Yellowtail, black with a pale-yellow offset shadow, rotated -.05, with a swash underline.
  - Our Work: left block in #8c0030 with a pale-pink crescent and diagonal pink beam lines; right "Our" / "Work" in 170px Anton #8c0030 with an offset #ffd9e5 outline.
  - Services: "Services" in 500 76px Inter, subline "Brand · Digital · Content · Packaging · Growth", line-art telescope on a tripod, a ringed planet and small cross stars, all in ink strokes.
  - Contact (hidden panel): "Contact" in 210px Yellowtail.
- The contact panel lives in a slot at (0,-1.17,2.52) on the tower, is invisible at rest and starts from scale .25 during its flight.

=====================================================================
8. CAMERA RIG & INTERACTION
=====================================================================
- target (0,1.1,0); dir normalize(0,-.2,1) (looking up at the tower). dist = max(13.4, 3.0/(tanHalfFov·aspect), (5.6/tanHalfFov)·.8). Recompute on resize.
- Intro: rig.intro goes 1→0 at .55/s: distance ×(1 + intro·.55), camera lowered by intro·3.5, look target lowered by intro·1.4, and the ring spins in from -2.2 rad. Clicks are ignored until the intro ends. If the URL has #about / #pricing / #work / #services / #contact, open that page when the intro finishes.
- Desktop mouse parallax: smoothed offset, camera ±1.1 x / ±.7 y, look ±.35 / ±.25.
- Pan (used during page flights): camera position and look target both shift right by pan·d·.62 and up by pan·d·.05 (the tower slides out to the left).
- Spin: wheel adds clamp(delta,-120,120)·.0016 to spinVel; dragging adds dx·.006 directly with inertia; spinVel eases back to an idle .09 rad/s. Arrow keys spin; Enter opens the front-most panel.
- Hover (mouse only): raycast the 4 panel meshes and the contact ring → cursor pointer, panel lifts +.16 along its local z, scales 1.035, uGlow → 1, tooltip shows. A tap/click (moved < 8px and < 450ms) opens the hit page.

=====================================================================
9. PAGE OPEN / CLOSE TRANSITION (the signature effect)
=====================================================================
OPEN (1.75s):
1. Render the page HTML into the hidden card; hide the HUD; push history state "#key"; play the whoosh sound.
2. Compute the target: final camera pose = pose at pan 1; place a rectangle D = 5 units in front of it. World units per px = (2·D·tan(fov/2)) / innerHeight. Measure #card.getBoundingClientRect() and its border-radius, and build a flat grid (same 72×24 vertex order) of the card's exact size and corner radius (thickness .015), centred to match the card's screen offset. Target rotation = camera quaternion.
3. Reparent the panel mesh to the scene (keep its world transform). Each frame with s = flight progress and a = easeInOutCubic(s):
   - position: cubic Bézier home → c1 = home + outward·3.2 + (0,1.3,0) → c2 = target + camRight·2.4 + camUp·.9 + camForward·2.2 → target.
   - rotation: slerp(home quat, target quat, smoothstep(.05,.85,s)), times a z-twist of sin(π·s)·.35·(random ±1).
   - vertices: pos = lerp(lerp(flat, bent, 1 − smoothstep(.06,.55,s)), cardFlat, easeInOutCubic(smoothstep(.55,1,s))); recompute normals and the bounding sphere.
   - uFlat = smoothstep(.38,.82,s) (texture melts into the solid, unlit page colour).
   - rig.pan = easeInOutCubic(smoothstep(.12,.95,s)).
4. At s = 1: show #card (class vis, no fade: it lines up pixel-perfectly with the 3D rectangle), hide the mesh, then add class "show" so the content staggers in.
CLOSE (1.45s):
1. Add "hide-content" to fade the content (~260ms), recompute the target, show the mesh at s = 1, fade the card out (.16s), play the reverse whoosh.
2. Run the same pose function backwards (s 1→0), recomputing the home pose every frame from the slot's live world transform (the ring keeps turning).
3. At s = 0: re-attach the mesh to its slot, reset the transform, restore the bent vertices, uFlat 0, hide it again if it's the contact panel, show the HUD, cursor back to grab, and clear any leftover #hash with history.replaceState.
Ways to close: the "Back to tower" button or the card logo (uses history.back() when a pushState exists, otherwise closes directly), Esc, the browser Back button (popstate), and CLICKING ANYWHERE OUTSIDE THE CARD (document click while open, when the target isn't inside #card or #menu; if the menu was open, that first click only closes the menu). While open, the canvas cursor is zoom-out.
Opening another page from inside an open page: close to the tower first, then fly the new panel out.

=====================================================================
10. SOUND (WebAudio, off by default, toggled by the speaker buttons)
=====================================================================
- Ambient pad: oscillators at 55, 82.4, 110.2, 164.8 Hz (sine/triangle alternating, slight detune, gain .06/(i+1)) → lowpass 520Hz Q .6, with an LFO at .05Hz ±260Hz on the cutoff → master (fades to .55 over .8s).
- Crickets: every .6–2.4s, three quick 4.3–4.6kHz chirps (10ms attack, 50ms each) with a random stereo pan.
- Whoosh on open/close: 1.1s white noise through a bandpass sweeping 260→1800Hz (reversed on close), peak gain .22.
- Chime on the star button: 1318, 1760, 2637 Hz, staggered 80ms, 1.2s exponential decay.

=====================================================================
11. LOADER & BOOT
=====================================================================
The counter animates to 60% while fonts load, then builds the tower details, light rings, antenna, shaft and panels, runs resize, animates to 100%, starts the render loop, and fades the loader 150ms later. Cap the frame dt at 1/20s.

=====================================================================
12. RESPONSIVE
=====================================================================
- ≤820px: the card uses 12px top/bottom gutter and 14px side margin, radius 24px; all grids become 1 column; the services accordion becomes 1 column ([40px | 1fr | 24px] header); corner labels and the crumb are hidden; smaller back pill (8px 11px, 10px font); price tiles drop their min-height.
- ≤420px: smaller pills.
- Touch devices: no hover tooltip, no mouse parallax, "Swipe to spin / Tap to explore", lower star count, lower bloom, DPR cap 1.75.
- The camera distance formula keeps the ring fully inside narrow portrait screens; rotate the sky so the moon shows on portrait.

Deliver one complete, working index.html with all CSS and JS inline (apart from the CDN import map and Google Fonts).
[/hidecontent]
← 返回首页