Compare commits
2 Commits
develop
...
feat/nixcn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63b98f42b4 | ||
|
|
4f9099abb6 |
@@ -1,7 +0,0 @@
|
||||
:3000 {
|
||||
root * /srv
|
||||
file_server
|
||||
|
||||
@root path /
|
||||
redir @root /zh-CN/ 302
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
FROM node:22-alpine AS builder
|
||||
RUN corepack enable
|
||||
WORKDIR /app
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
RUN pnpm install --frozen-lockfile
|
||||
COPY . .
|
||||
RUN pnpm build
|
||||
|
||||
FROM caddy:2-alpine AS runtime
|
||||
COPY --from=builder /app/dist /srv
|
||||
COPY Caddyfile /etc/caddy/Caddyfile
|
||||
EXPOSE 3000
|
||||
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
|
||||
@@ -1,18 +1,13 @@
|
||||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: 'https://meetup.nixos-cn.org',
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
},
|
||||
server: {
|
||||
host: true,
|
||||
},
|
||||
i18n: {
|
||||
locales: ['zh-CN', 'en'],
|
||||
defaultLocale: 'zh-CN',
|
||||
redirects: {
|
||||
'/': '/zh-CN/',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
# Tailwind CSS Migration Design
|
||||
|
||||
**Date:** 2026-05-20
|
||||
**Scope:** Replace all hand-written CSS in the project with Tailwind CSS utility classes.
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
Eliminate all `<style>` blocks from every `.astro` file. All styling moves to Tailwind utility classes on HTML elements, with shared tokens and keyframes centralized in `src/styles/global.css`. Visual output must be identical to pre-migration.
|
||||
|
||||
---
|
||||
|
||||
## Approach
|
||||
|
||||
Global-first, then parallel component conversion.
|
||||
|
||||
1. **Phase 1 (sequential):** Finalize `global.css` and update `BaseLayout.astro`. Must complete before Phase 2 so sub-agents have a definitive token reference.
|
||||
2. **Phase 2 (parallel):** All 5 component files converted simultaneously by independent sub-agents.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: global.css
|
||||
|
||||
`src/styles/global.css` is already created with `@import "tailwindcss"`. Extend it with:
|
||||
|
||||
### `@theme` block
|
||||
|
||||
Register all project-specific design tokens as Tailwind CSS variables:
|
||||
|
||||
```css
|
||||
@theme {
|
||||
/* Brand colors */
|
||||
--color-brand-blue: #5277c3;
|
||||
--color-brand-dark: #030f20;
|
||||
--color-brand-light: #9bcef1;
|
||||
--color-surface: rgba(249, 251, 255, 0.9);
|
||||
--color-border: rgba(155, 206, 241, 0.8);
|
||||
|
||||
/* Typography */
|
||||
--font-family-sans: 'PingFang SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
|
||||
/* Easings */
|
||||
--ease-hero: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
--ease-modal: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
|
||||
/* Named animations (name maps to @keyframes below) */
|
||||
--animate-hero-fade-up: heroFadeUp 0.55s var(--ease-hero) both;
|
||||
--animate-hero-fade-down: heroFadeDown 0.45s var(--ease-hero) both;
|
||||
--animate-hero-fade-in: heroFadeIn 0.5s var(--ease-hero) both;
|
||||
--animate-hero-fade-in-70: heroFadeInTo70 0.5s var(--ease-hero) both;
|
||||
--animate-modal-in: modalDialogIn 0.3s var(--ease-modal) both;
|
||||
--animate-modal-overlay: modalOverlayIn 0.2s ease both;
|
||||
}
|
||||
```
|
||||
|
||||
The existing `--hero-ease` / `--modal-ease` CSS variables are renamed to `--ease-hero` / `--ease-modal` to follow Tailwind v4 token conventions. References in any remaining inline styles or JS must use the new names.
|
||||
|
||||
### `@keyframes`
|
||||
|
||||
Define all kept animations here:
|
||||
|
||||
- `heroFadeUp` — `opacity: 0; transform: translateY(10px)` → `opacity: 1; transform: none`
|
||||
- `heroFadeDown` — `opacity: 0; transform: translateY(-10px)` → `opacity: 1; transform: none`
|
||||
- `heroFadeIn` — `opacity: 0` → `opacity: 1`
|
||||
- `heroFadeInTo70` — `opacity: 0` → `opacity: 0.7`
|
||||
- `modalDialogIn` — scale + fade in
|
||||
- `modalOverlayIn` — fade in
|
||||
|
||||
Exact keyframe values must be read from current component source before writing.
|
||||
|
||||
### `@layer base`
|
||||
|
||||
Replaces the `<style is:global>` block in `BaseLayout.astro`:
|
||||
|
||||
```css
|
||||
@layer base {
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; min-height: 100%; }
|
||||
body {
|
||||
font-family: var(--font-family-sans);
|
||||
background: #ffffff;
|
||||
color: var(--color-brand-dark);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 16px;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: BaseLayout.astro
|
||||
|
||||
- Add `import '../styles/global.css'` to the frontmatter
|
||||
- Remove the `<style is:global>` block entirely
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Component files
|
||||
|
||||
Files: `Navbar.astro`, `HomePage.astro`, `CalendarPage.astro`, `CmsGuidePage.astro`, `SouvenirPage.astro`.
|
||||
|
||||
Each is converted by reading its current `<style>` block, mapping every rule to utility classes on the corresponding HTML element, then deleting the `<style>` block.
|
||||
|
||||
### Translation conventions
|
||||
|
||||
| CSS pattern | Tailwind equivalent |
|
||||
|---|---|
|
||||
| `@media (max-width: 1023px)` | `max-lg:` variant |
|
||||
| `@media (max-width: 639px)` | `max-sm:` variant |
|
||||
| `@media (prefers-reduced-motion: reduce)` | `motion-reduce:` variant |
|
||||
| `animation: heroFadeUp 0.55s ...` | `animate-hero-fade-up` |
|
||||
| `animation-delay: 0.28s` | `[animation-delay:0.28s]` |
|
||||
| `backdrop-filter: blur(4px)` | `backdrop-blur-sm` |
|
||||
| `backdrop-filter: blur(10px) saturate(1.4)` | `backdrop-blur-md backdrop-saturate-[140%]` |
|
||||
| `mix-blend-mode: screen` | `mix-blend-screen` |
|
||||
| Custom `clamp()` / `calc()` | `[clamp(...)]` / `[calc(...)]` arbitrary values |
|
||||
| Brand color `#5277c3` | `text-brand-blue` / `bg-brand-blue` / `border-brand-blue` |
|
||||
| Surface bg `rgba(249,251,255,0.9)` | `bg-surface` |
|
||||
| Border color `rgba(155,206,241,0.8)` | `border-border` |
|
||||
|
||||
### Edge cases
|
||||
|
||||
**`--step-delay` (CmsGuidePage):** Each `.step` element sets `--step-delay` as an inline style in the Astro frontmatter JSX. The `<style>` block then reads it via `animation-delay: var(--step-delay, 0.56s)`. After migration, the inline style attribute stays as-is; the Tailwind class uses the same variable: `[animation-delay:var(--step-delay,0.56s)]`.
|
||||
|
||||
**`--gloss-x` / `--gloss-y` (HomePage badge):** `badge-tilt.ts` updates these CSS custom properties on the badge element via `style.setProperty(...)`. The existing CSS uses them inside a `radial-gradient`. Because this background value depends on dynamically-changing CSS vars, it cannot be expressed as a static Tailwind arbitrary class. Move this single rule into `@layer components` in `global.css` rather than keeping it in a `<style>` block.
|
||||
|
||||
### Dropped patterns
|
||||
|
||||
- All `:global()` selectors (including navbar entrance animation)
|
||||
- `decoFadeIn` keyframe (was used only by removed `.deco-line` elements)
|
||||
|
||||
### `prefers-reduced-motion`
|
||||
|
||||
Every animated element gets `motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none` added alongside its `animate-*` class. This replaces the per-component `@media (prefers-reduced-motion)` blocks.
|
||||
|
||||
---
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Content, copy, or i18n changes
|
||||
- JS behavior changes
|
||||
- Adding new visual features or layout changes
|
||||
- Refactoring the i18n/translation system
|
||||
- Changing the dev server or build config beyond what's already done
|
||||
|
||||
---
|
||||
|
||||
## Verification checklist (per file)
|
||||
|
||||
- [ ] No `<style>` block remains in the file
|
||||
- [ ] No `:global()` selector remains
|
||||
- [ ] All breakpoint overrides use `max-lg:` / `max-sm:` variants
|
||||
- [ ] All animated elements have `motion-reduce:animate-none motion-reduce:opacity-100`
|
||||
- [ ] Brand colors use theme token classes, not arbitrary hex
|
||||
- [ ] Visual output matches pre-migration on desktop and mobile (dev server spot-check)
|
||||
@@ -14,11 +14,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.9",
|
||||
"@lucide/astro": "^1.16.0",
|
||||
"@tailwindcss/vite": "^4.3.0",
|
||||
"astro": "^5.6.1",
|
||||
"sharp": "^0.34.2",
|
||||
"tailwindcss": "^4.3.0",
|
||||
"typescript": "^6.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
413
pnpm-lock.yaml
generated
@@ -11,21 +11,12 @@ importers:
|
||||
'@astrojs/check':
|
||||
specifier: ^0.9.9
|
||||
version: 0.9.9(prettier-plugin-astro@0.14.1)(prettier@3.7.4)(typescript@6.0.3)
|
||||
'@lucide/astro':
|
||||
specifier: ^1.16.0
|
||||
version: 1.16.0(astro@5.16.4(@types/node@24.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.53.3)(typescript@6.0.3)(yaml@2.9.0))
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.3.0
|
||||
version: 4.3.0(vite@6.4.1(@types/node@24.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))
|
||||
astro:
|
||||
specifier: ^5.6.1
|
||||
version: 5.16.4(@types/node@24.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.53.3)(typescript@6.0.3)(yaml@2.9.0)
|
||||
version: 5.16.4(@types/node@24.10.1)(rollup@4.53.3)(typescript@6.0.3)(yaml@2.9.0)
|
||||
sharp:
|
||||
specifier: ^0.34.2
|
||||
version: 0.34.5
|
||||
tailwindcss:
|
||||
specifier: ^4.3.0
|
||||
version: 4.3.0
|
||||
typescript:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
@@ -542,183 +533,155 @@ packages:
|
||||
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.0.5':
|
||||
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-ppc64@1.2.4':
|
||||
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-riscv64@1.2.4':
|
||||
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-s390x@1.0.4':
|
||||
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-s390x@1.2.4':
|
||||
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.0.4':
|
||||
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
|
||||
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
|
||||
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-linux-arm64@0.33.5':
|
||||
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-arm@0.33.5':
|
||||
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-arm@0.34.5':
|
||||
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-ppc64@0.34.5':
|
||||
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-riscv64@0.34.5':
|
||||
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-s390x@0.33.5':
|
||||
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-s390x@0.34.5':
|
||||
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-x64@0.33.5':
|
||||
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-x64@0.34.5':
|
||||
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.33.5':
|
||||
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.33.5':
|
||||
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-wasm32@0.33.5':
|
||||
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
|
||||
@@ -760,12 +723,6 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
|
||||
|
||||
'@jridgewell/remapping@2.3.5':
|
||||
resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.2':
|
||||
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
@@ -773,17 +730,9 @@ packages:
|
||||
'@jridgewell/sourcemap-codec@1.5.5':
|
||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.9':
|
||||
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
|
||||
|
||||
'@lucide/astro@1.16.0':
|
||||
resolution: {integrity: sha512-RWT+9Y1UAMnz/oiJMssnQTSeBM0AbwzK1SsOeY2zQungN7PQxm4Pp4bNxIVp/Wu77TBXkpPoaIyxYqagZVjWGQ==}
|
||||
peerDependencies:
|
||||
astro: ^4 || ^5 || ^6
|
||||
|
||||
'@oslojs/encoding@1.1.0':
|
||||
resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==}
|
||||
|
||||
@@ -839,67 +788,56 @@ packages:
|
||||
resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.53.3':
|
||||
resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.53.3':
|
||||
resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.53.3':
|
||||
resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.53.3':
|
||||
resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.53.3':
|
||||
resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.53.3':
|
||||
resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.53.3':
|
||||
resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.53.3':
|
||||
resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.53.3':
|
||||
resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.53.3':
|
||||
resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-openharmony-arm64@4.53.3':
|
||||
resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==}
|
||||
@@ -957,100 +895,6 @@ packages:
|
||||
'@swc/helpers@0.5.17':
|
||||
resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==}
|
||||
|
||||
'@tailwindcss/node@4.3.0':
|
||||
resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==}
|
||||
|
||||
'@tailwindcss/oxide-android-arm64@4.3.0':
|
||||
resolution: {integrity: sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==}
|
||||
engines: {node: '>= 20'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@tailwindcss/oxide-darwin-arm64@4.3.0':
|
||||
resolution: {integrity: sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==}
|
||||
engines: {node: '>= 20'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@tailwindcss/oxide-darwin-x64@4.3.0':
|
||||
resolution: {integrity: sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==}
|
||||
engines: {node: '>= 20'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@tailwindcss/oxide-freebsd-x64@4.3.0':
|
||||
resolution: {integrity: sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==}
|
||||
engines: {node: '>= 20'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0':
|
||||
resolution: {integrity: sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==}
|
||||
engines: {node: '>= 20'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@tailwindcss/oxide-linux-arm64-gnu@4.3.0':
|
||||
resolution: {integrity: sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==}
|
||||
engines: {node: '>= 20'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@tailwindcss/oxide-linux-arm64-musl@4.3.0':
|
||||
resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==}
|
||||
engines: {node: '>= 20'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-gnu@4.3.0':
|
||||
resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==}
|
||||
engines: {node: '>= 20'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-musl@4.3.0':
|
||||
resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==}
|
||||
engines: {node: '>= 20'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@tailwindcss/oxide-wasm32-wasi@4.3.0':
|
||||
resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [wasm32]
|
||||
bundledDependencies:
|
||||
- '@napi-rs/wasm-runtime'
|
||||
- '@emnapi/core'
|
||||
- '@emnapi/runtime'
|
||||
- '@tybys/wasm-util'
|
||||
- '@emnapi/wasi-threads'
|
||||
- tslib
|
||||
|
||||
'@tailwindcss/oxide-win32-arm64-msvc@4.3.0':
|
||||
resolution: {integrity: sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==}
|
||||
engines: {node: '>= 20'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@tailwindcss/oxide-win32-x64-msvc@4.3.0':
|
||||
resolution: {integrity: sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==}
|
||||
engines: {node: '>= 20'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@tailwindcss/oxide@4.3.0':
|
||||
resolution: {integrity: sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==}
|
||||
engines: {node: '>= 20'}
|
||||
|
||||
'@tailwindcss/vite@4.3.0':
|
||||
resolution: {integrity: sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==}
|
||||
peerDependencies:
|
||||
vite: ^5.2.0 || ^6 || ^7 || ^8
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||
|
||||
@@ -1370,10 +1214,6 @@ packages:
|
||||
emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
|
||||
enhanced-resolve@5.21.5:
|
||||
resolution: {integrity: sha512-mLCNbrQli11K1ySUmuNt4ZUB3OpGIDq4q2vTBTf5cL2lpsRjI9QKqSD0ndjW8FyvcW/Jj46gMe9syyHAsvMa/A==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
@@ -1466,9 +1306,6 @@ packages:
|
||||
glob-to-regexp@0.4.1:
|
||||
resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
|
||||
|
||||
graceful-fs@4.2.11:
|
||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||
|
||||
h3@1.15.4:
|
||||
resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==}
|
||||
|
||||
@@ -1542,10 +1379,6 @@ packages:
|
||||
resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
jiti@2.7.0:
|
||||
resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
|
||||
hasBin: true
|
||||
|
||||
js-yaml@4.1.1:
|
||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||
hasBin: true
|
||||
@@ -1567,80 +1400,6 @@ packages:
|
||||
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
lightningcss-android-arm64@1.32.0:
|
||||
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
lightningcss-darwin-arm64@1.32.0:
|
||||
resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
lightningcss-darwin-x64@1.32.0:
|
||||
resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
lightningcss-freebsd-x64@1.32.0:
|
||||
resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.32.0:
|
||||
resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.32.0:
|
||||
resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-arm64-musl@1.32.0:
|
||||
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-linux-x64-gnu@1.32.0:
|
||||
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-x64-musl@1.32.0:
|
||||
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.32.0:
|
||||
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
lightningcss-win32-x64-msvc@1.32.0:
|
||||
resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
lightningcss@1.32.0:
|
||||
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
longest-streak@3.1.0:
|
||||
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
|
||||
|
||||
@@ -2072,13 +1831,6 @@ packages:
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
|
||||
tailwindcss@4.3.0:
|
||||
resolution: {integrity: sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==}
|
||||
|
||||
tapable@2.3.3:
|
||||
resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tiny-inflate@1.0.3:
|
||||
resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==}
|
||||
|
||||
@@ -3005,34 +2757,15 @@ snapshots:
|
||||
'@img/sharp-win32-x64@0.34.5':
|
||||
optional: true
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
|
||||
'@jridgewell/remapping@2.3.5':
|
||||
dependencies:
|
||||
'@jridgewell/gen-mapping': 0.3.13
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.2': {}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
dependencies:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.9':
|
||||
dependencies:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
'@lucide/astro@1.16.0(astro@5.16.4(@types/node@24.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.53.3)(typescript@6.0.3)(yaml@2.9.0))':
|
||||
dependencies:
|
||||
astro: 5.16.4(@types/node@24.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.53.3)(typescript@6.0.3)(yaml@2.9.0)
|
||||
|
||||
'@oslojs/encoding@1.1.0': {}
|
||||
|
||||
'@poppinss/colors@4.1.5':
|
||||
@@ -3162,74 +2895,6 @@ snapshots:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@tailwindcss/node@4.3.0':
|
||||
dependencies:
|
||||
'@jridgewell/remapping': 2.3.5
|
||||
enhanced-resolve: 5.21.5
|
||||
jiti: 2.7.0
|
||||
lightningcss: 1.32.0
|
||||
magic-string: 0.30.21
|
||||
source-map-js: 1.2.1
|
||||
tailwindcss: 4.3.0
|
||||
|
||||
'@tailwindcss/oxide-android-arm64@4.3.0':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-darwin-arm64@4.3.0':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-darwin-x64@4.3.0':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-freebsd-x64@4.3.0':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-arm64-gnu@4.3.0':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-arm64-musl@4.3.0':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-gnu@4.3.0':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-musl@4.3.0':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-wasm32-wasi@4.3.0':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-win32-arm64-msvc@4.3.0':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-win32-x64-msvc@4.3.0':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide@4.3.0':
|
||||
optionalDependencies:
|
||||
'@tailwindcss/oxide-android-arm64': 4.3.0
|
||||
'@tailwindcss/oxide-darwin-arm64': 4.3.0
|
||||
'@tailwindcss/oxide-darwin-x64': 4.3.0
|
||||
'@tailwindcss/oxide-freebsd-x64': 4.3.0
|
||||
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.0
|
||||
'@tailwindcss/oxide-linux-arm64-gnu': 4.3.0
|
||||
'@tailwindcss/oxide-linux-arm64-musl': 4.3.0
|
||||
'@tailwindcss/oxide-linux-x64-gnu': 4.3.0
|
||||
'@tailwindcss/oxide-linux-x64-musl': 4.3.0
|
||||
'@tailwindcss/oxide-wasm32-wasi': 4.3.0
|
||||
'@tailwindcss/oxide-win32-arm64-msvc': 4.3.0
|
||||
'@tailwindcss/oxide-win32-x64-msvc': 4.3.0
|
||||
|
||||
'@tailwindcss/vite@4.3.0(vite@6.4.1(@types/node@24.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))':
|
||||
dependencies:
|
||||
'@tailwindcss/node': 4.3.0
|
||||
'@tailwindcss/oxide': 4.3.0
|
||||
tailwindcss: 4.3.0
|
||||
vite: 6.4.1(@types/node@24.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
dependencies:
|
||||
'@types/ms': 2.1.0
|
||||
@@ -3354,7 +3019,7 @@ snapshots:
|
||||
|
||||
array-iterate@2.0.1: {}
|
||||
|
||||
astro@5.16.4(@types/node@24.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.53.3)(typescript@6.0.3)(yaml@2.9.0):
|
||||
astro@5.16.4(@types/node@24.10.1)(rollup@4.53.3)(typescript@6.0.3)(yaml@2.9.0):
|
||||
dependencies:
|
||||
'@astrojs/compiler': 2.13.0
|
||||
'@astrojs/internal-helpers': 0.7.5
|
||||
@@ -3411,8 +3076,8 @@ snapshots:
|
||||
unist-util-visit: 5.0.0
|
||||
unstorage: 1.17.3
|
||||
vfile: 6.0.3
|
||||
vite: 6.4.1(@types/node@24.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)
|
||||
vitefu: 1.1.1(vite@6.4.1(@types/node@24.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))
|
||||
vite: 6.4.1(@types/node@24.10.1)(yaml@2.9.0)
|
||||
vitefu: 1.1.1(vite@6.4.1(@types/node@24.10.1)(yaml@2.9.0))
|
||||
xxhash-wasm: 1.1.0
|
||||
yargs-parser: 21.1.1
|
||||
yocto-spinner: 0.2.3
|
||||
@@ -3630,11 +3295,6 @@ snapshots:
|
||||
|
||||
emoji-regex@8.0.0: {}
|
||||
|
||||
enhanced-resolve@5.21.5:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
tapable: 2.3.3
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
entities@6.0.1: {}
|
||||
@@ -3759,8 +3419,6 @@ snapshots:
|
||||
|
||||
glob-to-regexp@0.4.1: {}
|
||||
|
||||
graceful-fs@4.2.11: {}
|
||||
|
||||
h3@1.15.4:
|
||||
dependencies:
|
||||
cookie-es: 1.2.2
|
||||
@@ -3886,8 +3544,6 @@ snapshots:
|
||||
dependencies:
|
||||
is-inside-container: 1.0.0
|
||||
|
||||
jiti@2.7.0: {}
|
||||
|
||||
js-yaml@4.1.1:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
@@ -3902,55 +3558,6 @@ snapshots:
|
||||
|
||||
kleur@4.1.5: {}
|
||||
|
||||
lightningcss-android-arm64@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-darwin-arm64@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-darwin-x64@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-freebsd-x64@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm64-musl@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-x64-gnu@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-x64-musl@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss-win32-x64-msvc@1.32.0:
|
||||
optional: true
|
||||
|
||||
lightningcss@1.32.0:
|
||||
dependencies:
|
||||
detect-libc: 2.1.2
|
||||
optionalDependencies:
|
||||
lightningcss-android-arm64: 1.32.0
|
||||
lightningcss-darwin-arm64: 1.32.0
|
||||
lightningcss-darwin-x64: 1.32.0
|
||||
lightningcss-freebsd-x64: 1.32.0
|
||||
lightningcss-linux-arm-gnueabihf: 1.32.0
|
||||
lightningcss-linux-arm64-gnu: 1.32.0
|
||||
lightningcss-linux-arm64-musl: 1.32.0
|
||||
lightningcss-linux-x64-gnu: 1.32.0
|
||||
lightningcss-linux-x64-musl: 1.32.0
|
||||
lightningcss-win32-arm64-msvc: 1.32.0
|
||||
lightningcss-win32-x64-msvc: 1.32.0
|
||||
|
||||
longest-streak@3.1.0: {}
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
@@ -4685,10 +4292,6 @@ snapshots:
|
||||
picocolors: 1.1.1
|
||||
sax: 1.4.3
|
||||
|
||||
tailwindcss@4.3.0: {}
|
||||
|
||||
tapable@2.3.3: {}
|
||||
|
||||
tiny-inflate@1.0.3: {}
|
||||
|
||||
tinyexec@1.0.2: {}
|
||||
@@ -4831,7 +4434,7 @@ snapshots:
|
||||
'@types/unist': 3.0.3
|
||||
vfile-message: 4.0.3
|
||||
|
||||
vite@6.4.1(@types/node@24.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0):
|
||||
vite@6.4.1(@types/node@24.10.1)(yaml@2.9.0):
|
||||
dependencies:
|
||||
esbuild: 0.25.12
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
@@ -4842,13 +4445,11 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/node': 24.10.1
|
||||
fsevents: 2.3.3
|
||||
jiti: 2.7.0
|
||||
lightningcss: 1.32.0
|
||||
yaml: 2.9.0
|
||||
|
||||
vitefu@1.1.1(vite@6.4.1(@types/node@24.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)):
|
||||
vitefu@1.1.1(vite@6.4.1(@types/node@24.10.1)(yaml@2.9.0)):
|
||||
optionalDependencies:
|
||||
vite: 6.4.1(@types/node@24.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)
|
||||
vite: 6.4.1(@types/node@24.10.1)(yaml@2.9.0)
|
||||
|
||||
volar-service-css@0.0.70(@volar/language-service@2.4.28):
|
||||
dependencies:
|
||||
|
||||
BIN
public/images/calendar/food-map-en.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
public/images/calendar/food-map.png
Normal file
|
After Width: | Height: | Size: 491 KiB |
|
Before Width: | Height: | Size: 582 KiB |
|
Before Width: | Height: | Size: 526 KiB |
|
Before Width: | Height: | Size: 248 KiB |
BIN
public/images/souvenir/bottom-circles.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/images/souvenir/card-snowflake.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
public/images/souvenir/card-texture.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
public/images/souvenir/cta-arrow.png
Normal file
|
After Width: | Height: | Size: 434 B |
BIN
public/images/souvenir/hero-bg.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
580
src/components/CalendarPage.astro
Normal file
@@ -0,0 +1,580 @@
|
||||
---
|
||||
import Navbar from './Navbar.astro';
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import { type Locale, getTranslations } from '../i18n/config';
|
||||
|
||||
interface Props {
|
||||
locale: Locale;
|
||||
}
|
||||
|
||||
const { locale } = Astro.props;
|
||||
const t = getTranslations(locale);
|
||||
const tp = t.calendar;
|
||||
const foodMapSrc =
|
||||
locale === 'en' ? '/images/calendar/food-map-en.png' : '/images/calendar/food-map.png';
|
||||
---
|
||||
|
||||
<BaseLayout title={tp.meta.title} description={tp.meta.description} lang={tp.meta.htmlLang}>
|
||||
<Navbar locale={locale} activePage='calendar' />
|
||||
|
||||
<!-- ======= Page Heading ======= -->
|
||||
<section class='page-heading'>
|
||||
<h1 class='heading-title'>{tp.heading.title}</h1>
|
||||
<p class='heading-sub'>{tp.heading.sub}</p>
|
||||
</section>
|
||||
|
||||
<!-- ======= Guide Card ======= -->
|
||||
<section class='guide-card'>
|
||||
<div class='guide-inner'>
|
||||
<!-- 装饰竖线(分隔左右两栏,绝对定位贯穿全高) -->
|
||||
<span class='deco-line deco-v' aria-hidden='true'></span>
|
||||
|
||||
<div class='guide-grid'>
|
||||
<!-- ============ 左栏 ============ -->
|
||||
<div class='col col-left'>
|
||||
<!-- 活动议程要点 -->
|
||||
<div class='block agenda-points'>
|
||||
<p class='point-lead'>
|
||||
<span class='emoji'>{tp.agenda.leadEmoji}</span>
|
||||
<span class='point-text'><Fragment set:html={tp.agenda.leadHtml} /></span>
|
||||
</p>
|
||||
<ul class='bullet-list'>
|
||||
{
|
||||
tp.agenda.bullets.map((html) => (
|
||||
<li>
|
||||
<Fragment set:html={html} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 装饰横线(in-flow 居于两块之间,gap 自动给上下等距)-->
|
||||
<span class='deco-line deco-h' aria-hidden='true'></span>
|
||||
|
||||
<!-- 会议安排 -->
|
||||
<div class='block schedule-block'>
|
||||
<p class='section-title'>
|
||||
<span class='emoji'>{tp.schedule.emoji}</span>
|
||||
<span>{tp.schedule.title}</span>
|
||||
</p>
|
||||
|
||||
<div class='info-card'>
|
||||
<div class='info-row'>
|
||||
<span class='info-label'>{tp.schedule.timeLabel}</span>
|
||||
<span class='info-value'>{tp.schedule.timeValue}</span>
|
||||
</div>
|
||||
<div class='info-row info-row-stack'>
|
||||
<span class='info-label'>{tp.schedule.flowLabel}</span>
|
||||
<ul class='timeline-list'>
|
||||
{
|
||||
tp.schedule.flow.map((item) => (
|
||||
<li>
|
||||
<Fragment set:html={item} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ 右栏 ============ -->
|
||||
<div class='col col-right'>
|
||||
<div class='block food-block' id='food-block'>
|
||||
<p class='section-title'>
|
||||
<span class='emoji'>{tp.food.emoji}</span>
|
||||
<span>{tp.food.title}</span>
|
||||
</p>
|
||||
|
||||
<figure class='food-map'>
|
||||
<img
|
||||
src={foodMapSrc}
|
||||
alt={tp.food.mapAlt}
|
||||
class='food-map-img'
|
||||
loading='lazy'
|
||||
decoding='async'
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
:global(.page) {
|
||||
padding: 0 16px 16px;
|
||||
--hero-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
:global(.page) {
|
||||
padding: 0 8px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(16px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-12px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes decoFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 0.65;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.navbar) {
|
||||
animation: heroFadeDown 0.45s var(--hero-ease) both;
|
||||
}
|
||||
|
||||
/* ===== 页面标题区 ===== */
|
||||
.page-heading {
|
||||
padding: 48px 16px 24px;
|
||||
text-align: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.heading-title {
|
||||
font-size: clamp(2.25rem, 5vw, 4rem);
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.05em;
|
||||
color: #030f20;
|
||||
margin: 0 0 16px;
|
||||
line-height: 1.1;
|
||||
text-shadow: 0 4px 12px rgba(3, 15, 32, 0.06);
|
||||
animation: heroFadeUp 0.55s var(--hero-ease) both;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.heading-sub {
|
||||
font-size: 16px;
|
||||
color: #030f20;
|
||||
letter-spacing: -0.05em;
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
animation: heroFadeUp 0.5s var(--hero-ease) both;
|
||||
animation-delay: 0.18s;
|
||||
}
|
||||
|
||||
/* ===== 主卡片:双层圆角描边 ===== */
|
||||
.guide-card {
|
||||
position: relative;
|
||||
margin: 16px 0 0;
|
||||
border-radius: 24px;
|
||||
border: 2px solid rgba(155, 206, 241, 0.8);
|
||||
background: transparent;
|
||||
padding: 10px;
|
||||
overflow: hidden;
|
||||
animation: heroFadeIn 0.5s var(--hero-ease) both;
|
||||
animation-delay: 0.22s;
|
||||
}
|
||||
|
||||
.guide-inner {
|
||||
position: relative;
|
||||
border-radius: 16px;
|
||||
border: 2px solid rgba(155, 206, 241, 0.8);
|
||||
background: rgba(249, 251, 255, 0.85);
|
||||
backdrop-filter: blur(4px);
|
||||
-webkit-backdrop-filter: blur(4px);
|
||||
padding: 40px clamp(20px, 4vw, 56px);
|
||||
min-height: 600px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* === 装饰:用 nix-tile-100.svg 堆出来的横/竖分隔线 === */
|
||||
.deco-line {
|
||||
pointer-events: none;
|
||||
opacity: 0.65;
|
||||
background-image: url('/images/shared/nix-tile-100.svg');
|
||||
background-position: center;
|
||||
animation: decoFadeIn 0.6s var(--hero-ease) both;
|
||||
animation-delay: 0.36s;
|
||||
}
|
||||
|
||||
/* 横线:作为 .col-left 的 flex 成员位于两块之间,
|
||||
依赖 .col { gap } 自动获得上下等距的呼吸距离 */
|
||||
.deco-h {
|
||||
position: relative;
|
||||
display: block;
|
||||
height: 24px;
|
||||
background-size: 24px 24px;
|
||||
background-repeat: repeat-x;
|
||||
/* 左端:负 margin 反向延伸,让横线贴到卡片内描边(恢复原始 left:3px 的视觉位置) */
|
||||
margin-left: calc(3px - clamp(20px, 4vw, 56px));
|
||||
/* 右端:延伸到竖线 .deco-v 的左缘以补齐栏间 grid gap */
|
||||
margin-right: calc(12px - clamp(24px, 4vw, 56px) / 2);
|
||||
}
|
||||
|
||||
/* 竖线:仍保持绝对定位,贯穿整张卡片的高度 */
|
||||
.deco-v {
|
||||
position: absolute;
|
||||
left: calc(50% - 12px);
|
||||
top: 0;
|
||||
width: 24px;
|
||||
height: 100%;
|
||||
background-size: 24px 24px;
|
||||
background-repeat: repeat-y;
|
||||
}
|
||||
|
||||
/* ===== 两栏网格 ===== */
|
||||
.guide-grid {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
gap: clamp(24px, 4vw, 56px);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 36px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
/* 左栏「活动议程要点」整体限宽到 480,与下方 .info-card / 右栏 .food-block 三者同宽。
|
||||
这样 .bullet-list li 底部的浅蓝 underline 右缘也会停在 info-card 的右缘上,
|
||||
不再伸进中间分割线那条「走廊」里 */
|
||||
.agenda-points {
|
||||
max-width: 480px;
|
||||
animation: heroFadeUp 0.55s var(--hero-ease) both;
|
||||
animation-delay: 0.32s;
|
||||
}
|
||||
|
||||
.emoji {
|
||||
font-size: 20px;
|
||||
line-height: 1;
|
||||
display: inline-block;
|
||||
margin-right: 12px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.point-lead,
|
||||
.section-title {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.05em;
|
||||
color: #030f20;
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.point-lead .emoji {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
:global(.accent) {
|
||||
color: #5277c3;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.bullet-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0 0 0 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.bullet-list li {
|
||||
position: relative;
|
||||
/* 左侧 22 用于让位 ::before 的圆点;右侧同样补 22 以保持文案 + 下划线视觉居中,
|
||||
尤其是在移动端 li 宽度较大时,左右不对称会让整段看上去偏右 */
|
||||
padding: 0 22px 4px 22px;
|
||||
font-size: 20px;
|
||||
line-height: 1.6;
|
||||
color: #33466d;
|
||||
letter-spacing: -0.05em;
|
||||
font-weight: 500;
|
||||
background: linear-gradient(rgba(126, 186, 228, 0.31), rgba(126, 186, 228, 0.31)) no-repeat;
|
||||
background-position: 22px 100%;
|
||||
background-size: calc(100% - 44px) 8px;
|
||||
}
|
||||
|
||||
.bullet-list li::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
top: 0.7em;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: #33466d;
|
||||
}
|
||||
|
||||
.bullet-list li :global(strong) {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #33466d;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
background: #fff;
|
||||
border: 1px solid #5277c3;
|
||||
border-radius: 24px;
|
||||
padding: 24px 28px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 26px;
|
||||
box-shadow: 0 6px 22px rgba(82, 119, 195, 0.08);
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
.schedule-block {
|
||||
animation: heroFadeUp 0.55s var(--hero-ease) both;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.info-row-stack {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 16px;
|
||||
color: #030f20;
|
||||
letter-spacing: -0.05em;
|
||||
line-height: 1.6;
|
||||
flex-shrink: 0;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #030f20;
|
||||
letter-spacing: -0.05em;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.timeline-list {
|
||||
margin: 0;
|
||||
padding-left: 22px;
|
||||
list-style: disc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.timeline-list li {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #030f20;
|
||||
letter-spacing: -0.05em;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.timeline-list li::marker {
|
||||
color: #030f20;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/* 锚点跳转时给目标区块一点呼吸距离 */
|
||||
#food-block {
|
||||
scroll-margin-top: 24px;
|
||||
}
|
||||
|
||||
.food-block {
|
||||
gap: 20px;
|
||||
/* 与左栏 .info-card 镜像:max-width 480 + 整体靠 col-right 右缘对齐,
|
||||
这样「分割线 → food-map 左缘」的距离 = 「info-card 右缘 → 分割线」的距离 */
|
||||
align-self: flex-end;
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
animation: heroFadeUp 0.55s var(--hero-ease) both;
|
||||
animation-delay: 0.48s;
|
||||
}
|
||||
|
||||
.food-map {
|
||||
margin: 0;
|
||||
border-radius: 24px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #5277c3;
|
||||
background: #f6fafc;
|
||||
box-shadow: 0 8px 28px rgba(82, 119, 195, 0.1);
|
||||
}
|
||||
|
||||
.food-map-img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
.page-heading {
|
||||
padding: 32px 16px 20px;
|
||||
}
|
||||
|
||||
.guide-inner {
|
||||
padding: 32px 32px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.guide-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.deco-h,
|
||||
.deco-v {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.agenda-points {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.food-block {
|
||||
align-self: auto;
|
||||
max-width: none;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
.page {
|
||||
padding: 0 8px 8px;
|
||||
}
|
||||
|
||||
.page-heading {
|
||||
padding: 24px 8px 16px;
|
||||
}
|
||||
|
||||
.heading-title {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.heading-sub {
|
||||
font-size: 14px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.guide-card {
|
||||
border-radius: 16px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.guide-inner {
|
||||
border-radius: 10px;
|
||||
padding: 24px 18px;
|
||||
}
|
||||
|
||||
.col {
|
||||
gap: 28px;
|
||||
}
|
||||
|
||||
.point-lead,
|
||||
.section-title {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.bullet-list li {
|
||||
font-size: 16px;
|
||||
background-size: calc(100% - 44px) 6px;
|
||||
}
|
||||
|
||||
.bullet-list li :global(strong) {
|
||||
font-size: 19px;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
padding: 20px 18px;
|
||||
gap: 20px;
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.timeline-list li {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.food-map {
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:global(.navbar),
|
||||
.heading-title,
|
||||
.heading-sub,
|
||||
.guide-card,
|
||||
.deco-line,
|
||||
.agenda-points,
|
||||
.schedule-block,
|
||||
.food-block {
|
||||
animation: none !important;
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.deco-line {
|
||||
opacity: 0.65 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -10,70 +10,35 @@ interface Props {
|
||||
const { locale } = Astro.props;
|
||||
const t = getTranslations(locale);
|
||||
const tp = t.cmsGuide;
|
||||
|
||||
const subDelays = ['0.4s', '0.48s', '0.56s'];
|
||||
---
|
||||
|
||||
<BaseLayout title={tp.meta.title} description={tp.meta.description} lang={tp.meta.htmlLang}>
|
||||
<Navbar locale={locale} activePage='cmsGuide' />
|
||||
|
||||
<!-- ======= Hero Banner ======= -->
|
||||
<section
|
||||
class='relative pt-[38px] pb-12 px-6 text-center isolate overflow-hidden min-h-[215px] max-lg:pt-8 max-lg:pb-10 max-lg:px-5 max-sm:pt-6 max-sm:pb-8 max-sm:px-4'
|
||||
aria-labelledby='cms-title'
|
||||
>
|
||||
<div class='absolute inset-0 z-0 pointer-events-none overflow-hidden' aria-hidden='true'>
|
||||
<img
|
||||
class='absolute inset-0 w-full h-full object-cover object-top block animate-hero-fade-in motion-reduce:animate-none motion-reduce:opacity-100'
|
||||
src='/images/shared/hero-bg.png'
|
||||
alt=''
|
||||
/>
|
||||
<!-- ======= Hero Banner(Figma Desktop - 4 切图) ======= -->
|
||||
<section class='hero-banner' aria-labelledby='cms-title'>
|
||||
<div class='hero-bg' aria-hidden='true'>
|
||||
<img class='hero-bg-img' src='/images/cms-guide/hero-bg.png' alt='' />
|
||||
</div>
|
||||
<div class='relative z-[1] max-w-[720px] mx-auto'>
|
||||
<h1
|
||||
id='cms-title'
|
||||
class='text-[clamp(2.5rem,5.5vw,4.5rem)] font-semibold text-brand-dark m-0 mb-5 tracking-[-0.05em] leading-[1.1] [text-shadow:0_4px_14px_rgba(3,15,32,0.08)] animate-hero-fade-up [animation-delay:0.1s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-sm:mb-3'
|
||||
>
|
||||
{tp.hero.title}
|
||||
</h1>
|
||||
{
|
||||
tp.hero.subs.map((s, i) => (
|
||||
<p
|
||||
class='hero-sub text-[clamp(0.875rem,1vw,1rem)] text-[#1a2332] m-0 leading-[1.7] tracking-[-0.02em] animate-hero-fade-up motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-sm:text-[13px] max-sm:leading-[1.65]'
|
||||
style={`animation-delay:${subDelays[i] ?? '0.56s'}`}
|
||||
>
|
||||
{s}
|
||||
</p>
|
||||
))
|
||||
}
|
||||
<div class='hero-content'>
|
||||
<h1 class='hero-title' id='cms-title'>{tp.hero.title}</h1>
|
||||
{tp.hero.subs.map((s) => <p class='hero-sub'>{s}</p>)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ======= Guide content ======= -->
|
||||
<main
|
||||
class='relative z-[2] w-full max-w-[1120px] mx-auto px-10 pt-6 pb-[120px] flex flex-col gap-10 max-lg:px-7 max-lg:pt-4 max-lg:pb-[100px] max-lg:gap-9 max-sm:px-[18px] max-sm:pt-2 max-sm:pb-20 max-sm:gap-8'
|
||||
>
|
||||
<main class='guide-main'>
|
||||
{
|
||||
tp.steps.map((step, i) => (
|
||||
<article
|
||||
class='flex flex-col gap-4 animate-hero-fade-up [animation-delay:var(--step-delay,0.56s)] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-sm:gap-3'
|
||||
id={step.id}
|
||||
style={`--step-delay: ${0.56 + i * 0.06}s`}
|
||||
>
|
||||
<h2 class='flex items-baseline gap-0 m-0 font-semibold leading-[1.2] tracking-[-0.04em] text-brand-dark'>
|
||||
<span class='text-[clamp(1.75rem,2.4vw,2.25rem)] font-bold text-brand-dark [font-feature-settings:"tnum"] mr-2 max-sm:text-[28px]'>
|
||||
{step.num}
|
||||
</span>
|
||||
<span class='text-[clamp(1.5rem,2vw,1.875rem)] text-[#a7b8d0] mr-[6px] font-normal max-sm:text-[22px]'>
|
||||
|
|
||||
</span>
|
||||
<span class='text-[clamp(1.5rem,2vw,1.875rem)] text-brand-dark font-semibold max-sm:text-[22px]'>
|
||||
{step.name}
|
||||
</span>
|
||||
<article class='step' id={step.id} style={`--step-delay: ${0.56 + i * 0.06}s`}>
|
||||
<h2 class='step-title'>
|
||||
<span class='step-num'>{step.num}</span>
|
||||
<span class='step-divider'>|</span>
|
||||
<span class='step-name'>{step.name}</span>
|
||||
</h2>
|
||||
|
||||
{'bodyHtml' in step && step.bodyHtml !== undefined && (
|
||||
<p class='text-[15px] leading-[1.8] text-[#1a2332] m-0 tracking-[-0.01em] max-sm:text-sm max-sm:leading-[1.75]'>
|
||||
<p class='step-body'>
|
||||
<Fragment set:html={step.bodyHtml} />
|
||||
</p>
|
||||
)}
|
||||
@@ -81,32 +46,32 @@ const subDelays = ['0.4s', '0.48s', '0.56s'];
|
||||
{'bodyHtmls' in step &&
|
||||
step.bodyHtmls !== undefined &&
|
||||
step.bodyHtmls.map((html) => (
|
||||
<p class='text-[15px] leading-[1.8] text-[#1a2332] m-0 tracking-[-0.01em] max-sm:text-sm max-sm:leading-[1.75]'>
|
||||
<p class='step-body'>
|
||||
<Fragment set:html={html} />
|
||||
</p>
|
||||
))}
|
||||
|
||||
{'list' in step && step.list !== undefined && (
|
||||
<ul class='step-list m-0 pl-6 text-[15px] leading-[1.9] text-[#1a2332] max-sm:text-sm'>
|
||||
<ul class='step-list'>
|
||||
{step.list.map((item) => (
|
||||
<li class='pl-1'>{item}</li>
|
||||
<li>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{'afterListHtml' in step && step.afterListHtml !== undefined && (
|
||||
<p class='text-[15px] leading-[1.8] text-[#1a2332] m-0 tracking-[-0.01em] max-sm:text-sm max-sm:leading-[1.75]'>
|
||||
<p class='step-body'>
|
||||
<Fragment set:html={step.afterListHtml} />
|
||||
</p>
|
||||
)}
|
||||
|
||||
{step.tip?.text && (
|
||||
<div class='tip text-sm leading-[1.7] text-[#c66a2a] bg-[rgba(245,166,35,0.06)] border-l-[3px] border-[rgba(245,166,35,0.5)] rounded-r-lg py-3 px-4 mt-1 max-sm:text-[13px] max-sm:py-2.5 max-sm:px-3'>
|
||||
<p class='m-0'>
|
||||
<span class='inline-block mr-1' aria-hidden='true'>
|
||||
<div class='tip tip--inline'>
|
||||
<p>
|
||||
<span class='tip-bell' aria-hidden='true'>
|
||||
{tp.tipBell}
|
||||
</span>
|
||||
<span class='font-semibold mr-0.5'>{tp.tipLabel}</span>
|
||||
<span class='tip-label'>{tp.tipLabel}</span>
|
||||
{step.tip.text}
|
||||
</p>
|
||||
</div>
|
||||
@@ -115,4 +80,355 @@ const subDelays = ['0.4s', '0.48s', '0.56s'];
|
||||
))
|
||||
}
|
||||
</main>
|
||||
|
||||
<!-- ======= 底部同心圆装饰(Figma 切图) ======= -->
|
||||
<div class='bottom-circles' aria-hidden='true'>
|
||||
<img src='/images/cms-guide/bottom-circles.png' alt='' />
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
:global(.page) {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
--hero-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
@keyframes heroFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(16px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-12px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
:global(.navbar) {
|
||||
animation: heroFadeDown 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
}
|
||||
|
||||
/* ===== Hero Banner ===== */
|
||||
.hero-banner {
|
||||
position: relative;
|
||||
padding: 38px 24px 48px;
|
||||
text-align: center;
|
||||
isolation: isolate;
|
||||
overflow: hidden;
|
||||
min-height: 215px;
|
||||
}
|
||||
|
||||
.hero-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero-bg-img {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center top;
|
||||
display: block;
|
||||
animation: heroFadeIn 0.6s var(--hero-ease) both;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: clamp(2.5rem, 5.5vw, 4.5rem);
|
||||
font-weight: 600;
|
||||
color: #030f20;
|
||||
margin: 0 0 20px;
|
||||
letter-spacing: -0.05em;
|
||||
line-height: 1.1;
|
||||
text-shadow: 0 4px 14px rgba(3, 15, 32, 0.08);
|
||||
animation: heroFadeUp 0.45s var(--hero-ease) both;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.hero-sub {
|
||||
font-size: clamp(0.875rem, 1vw, 1rem);
|
||||
color: #1a2332;
|
||||
margin: 0;
|
||||
line-height: 1.7;
|
||||
letter-spacing: -0.02em;
|
||||
animation: heroFadeUp 0.45s var(--hero-ease) both;
|
||||
}
|
||||
|
||||
.hero-sub:nth-of-type(1) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.hero-sub:nth-of-type(2) {
|
||||
animation-delay: 0.48s;
|
||||
}
|
||||
|
||||
.hero-sub:nth-of-type(3) {
|
||||
animation-delay: 0.56s;
|
||||
}
|
||||
|
||||
.hero-sub + .hero-sub {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/* ===== Guide content ===== */
|
||||
.guide-main {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
max-width: 1120px;
|
||||
margin: 0 auto;
|
||||
padding: 24px 40px 120px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
animation: heroFadeUp 0.45s var(--hero-ease) both;
|
||||
animation-delay: var(--step-delay, 0.56s);
|
||||
}
|
||||
|
||||
.step-title {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 0;
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.04em;
|
||||
color: #030f20;
|
||||
}
|
||||
|
||||
.step-num {
|
||||
font-size: clamp(1.75rem, 2.4vw, 2.25rem);
|
||||
font-weight: 700;
|
||||
color: #030f20;
|
||||
font-feature-settings: 'tnum';
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.step-divider {
|
||||
font-size: clamp(1.5rem, 2vw, 1.875rem);
|
||||
color: #a7b8d0;
|
||||
margin: 0 6px 0 0;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.step-name {
|
||||
font-size: clamp(1.5rem, 2vw, 1.875rem);
|
||||
color: #030f20;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.step-body {
|
||||
font-size: 15px;
|
||||
line-height: 1.8;
|
||||
color: #1a2332;
|
||||
margin: 0;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.step-list {
|
||||
margin: 0;
|
||||
padding-left: 24px;
|
||||
font-size: 15px;
|
||||
line-height: 1.9;
|
||||
color: #1a2332;
|
||||
}
|
||||
|
||||
.step-list li {
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.step-list li::marker {
|
||||
color: #5277c3;
|
||||
}
|
||||
|
||||
/* "前两周" 高亮(red 胶囊)—— 通过 set:html 注入,需要 :global */
|
||||
:global(.hl-warn) {
|
||||
display: inline-block;
|
||||
padding: 0 8px;
|
||||
margin: 0 2px;
|
||||
color: #d94f4f;
|
||||
font-weight: 600;
|
||||
border: 1px solid rgba(217, 79, 79, 0.45);
|
||||
border-radius: 8px;
|
||||
background: rgba(217, 79, 79, 0.06);
|
||||
}
|
||||
|
||||
/* 提示框(🔔) */
|
||||
.tip {
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
color: #c66a2a;
|
||||
background: rgba(245, 166, 35, 0.06);
|
||||
border-left: 3px solid rgba(245, 166, 35, 0.5);
|
||||
border-radius: 0 8px 8px 0;
|
||||
padding: 12px 16px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.tip p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tip p + p {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.tip-head {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tip-bell {
|
||||
display: inline-block;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.tip--inline {
|
||||
padding: 8px 14px;
|
||||
}
|
||||
|
||||
.tip-label {
|
||||
font-weight: 600;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
/* ===== 底部同心圆装饰 ===== */
|
||||
.bottom-circles {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: min(710px, 100%);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
animation: heroFadeIn 0.8s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
animation-delay: 0.92s;
|
||||
}
|
||||
|
||||
.bottom-circles img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
.hero-banner {
|
||||
padding: 32px 20px 40px;
|
||||
}
|
||||
|
||||
.guide-main {
|
||||
padding: 16px 28px 100px;
|
||||
gap: 36px;
|
||||
}
|
||||
|
||||
.bottom-circles {
|
||||
width: min(560px, 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
.hero-banner {
|
||||
padding: 24px 16px 32px;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.hero-sub {
|
||||
font-size: 13px;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.guide-main {
|
||||
padding: 8px 18px 80px;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.step {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.step-num {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.step-divider,
|
||||
.step-name {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.step-body {
|
||||
font-size: 14px;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.step-list {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tip {
|
||||
font-size: 13px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.bottom-circles {
|
||||
width: min(380px, 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:global(.navbar),
|
||||
.hero-bg-img,
|
||||
.hero-title,
|
||||
.hero-sub,
|
||||
.step {
|
||||
animation: none !important;
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.bottom-circles {
|
||||
animation: none !important;
|
||||
opacity: 1 !important;
|
||||
transform: translateX(-50%) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
---
|
||||
import Navbar from './Navbar.astro';
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import { type Locale, getTranslations } from '../i18n/config';
|
||||
|
||||
interface Props {
|
||||
locale: Locale;
|
||||
}
|
||||
|
||||
const { locale } = Astro.props;
|
||||
const t = getTranslations(locale);
|
||||
const tp = t.eventGuide;
|
||||
const foodMapSrc = locale === 'en' ? '/images/event-guide/food-map-en.png' : '/images/event-guide/food-map.png';
|
||||
---
|
||||
|
||||
<BaseLayout title={tp.meta.title} description={tp.meta.description} lang={tp.meta.htmlLang}>
|
||||
<Navbar locale={locale} activePage='eventGuide' />
|
||||
|
||||
<!-- ======= Hero Banner ======= -->
|
||||
<section
|
||||
class='relative pt-[38px] pb-12 px-6 text-center isolate overflow-hidden min-h-[215px] max-lg:pt-8 max-lg:pb-10 max-lg:px-5 max-sm:pt-6 max-sm:pb-8 max-sm:px-4'
|
||||
aria-labelledby='souvenir-title'
|
||||
>
|
||||
<div class='absolute inset-0 z-0 pointer-events-none overflow-hidden' aria-hidden='true'>
|
||||
<img
|
||||
class='absolute inset-0 w-full h-full object-cover object-top block animate-hero-fade-in motion-reduce:animate-none motion-reduce:opacity-100'
|
||||
src='/images/shared/hero-bg.png'
|
||||
alt=''
|
||||
/>
|
||||
</div>
|
||||
<div class='relative z-[1] max-w-[720px] mx-auto'>
|
||||
<h1
|
||||
id='souvenir-title'
|
||||
class='text-[clamp(2.5rem,5.5vw,4.5rem)] font-semibold text-brand-dark m-0 mb-4 tracking-[-0.05em] leading-[1.1] [text-shadow:0_4px_14px_rgba(3,15,32,0.08)] animate-hero-fade-up [animation-delay:0.1s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-sm:mb-3'
|
||||
>
|
||||
{tp.heading.title}
|
||||
</h1>
|
||||
<p
|
||||
class='text-[clamp(0.875rem,1vw,1rem)] text-[#1a2332] m-0 leading-[1.7] tracking-[-0.02em] max-w-[640px] mx-auto animate-hero-fade-up [animation-delay:0.18s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-sm:text-[13px] max-sm:leading-[1.65]'
|
||||
>
|
||||
{tp.heading.sub}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ======= Bento Grid ======= -->
|
||||
<div
|
||||
class='mx-auto mt-4 grid grid-cols-[32rem_1fr] grid-rows-[auto_auto] gap-[10px] max-lg:grid-cols-1 max-lg:auto-rows-auto max-sm:gap-2'
|
||||
>
|
||||
<!-- Tile: Agenda -->
|
||||
<div
|
||||
class='col-start-1 row-start-1 flex flex-col gap-4 bg-surface border-2 border-border rounded-[20px] p-6 px-7 backdrop-blur-sm [-webkit-backdrop-filter:blur(4px)] shadow-[0_4px_16px_rgba(82,119,195,0.06)] animate-hero-fade-up [animation-delay:0.28s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-lg:col-auto max-lg:row-auto max-sm:rounded-2xl max-sm:p-5 max-sm:px-[18px]'
|
||||
>
|
||||
<p
|
||||
class='text-xl font-medium tracking-[-0.05em] text-brand-dark m-0 leading-[1.4] flex items-center max-sm:text-[17px]'
|
||||
>
|
||||
<span class='text-xl leading-none inline-block mr-2 align-middle'>{tp.agenda.leadEmoji}</span>
|
||||
<span><Fragment set:html={tp.agenda.leadHtml} /></span>
|
||||
</p>
|
||||
<ul class='bullet-list list-none m-0 pl-3 flex flex-col gap-1'>
|
||||
{
|
||||
tp.agenda.bullets.map((html) => (
|
||||
<li class='relative px-[22px] pb-1 text-xl leading-[1.6] text-[#33466d] tracking-[-0.05em] font-medium [background-position:22px_100%] [background-size:calc(100%-44px)_8px] max-sm:text-base max-sm:[background-size:calc(100%-44px)_6px]'>
|
||||
<Fragment set:html={html} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Tile: Schedule -->
|
||||
<div
|
||||
class='col-start-1 row-start-2 flex flex-col gap-4 bg-surface border-2 border-border rounded-[20px] p-6 px-7 backdrop-blur-sm [-webkit-backdrop-filter:blur(4px)] shadow-[0_4px_16px_rgba(82,119,195,0.06)] animate-hero-fade-up [animation-delay:0.38s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-lg:col-auto max-lg:row-auto max-sm:rounded-2xl max-sm:p-5 max-sm:px-[18px]'
|
||||
>
|
||||
<p
|
||||
class='text-xl font-medium tracking-[-0.05em] text-brand-dark m-0 leading-[1.4] flex items-center max-sm:text-[17px]'
|
||||
>
|
||||
<span class='text-xl leading-none inline-block mr-3 align-middle'>{tp.schedule.emoji}</span>
|
||||
<span>{tp.schedule.title}</span>
|
||||
</p>
|
||||
<div
|
||||
class='info-card bg-white border border-brand-blue rounded-2xl p-5 px-[22px] flex flex-col gap-[22px] shadow-[0_4px_16px_rgba(82,119,195,0.08)] max-sm:p-4 max-sm:gap-4 max-sm:rounded-[14px]'
|
||||
>
|
||||
<div class='flex items-start gap-3 flex-wrap max-sm:flex-col max-sm:gap-1'>
|
||||
<span class='text-base text-brand-dark leading-[1.6] flex-shrink-0 pt-px max-sm:text-sm'>
|
||||
{tp.schedule.timeLabel}
|
||||
</span>
|
||||
<span class='text-xl font-medium text-brand-dark leading-[1.4] max-sm:text-base'>
|
||||
{tp.schedule.timeValue}
|
||||
</span>
|
||||
</div>
|
||||
<div class='flex items-start gap-3 flex-wrap max-sm:flex-col max-sm:gap-1'>
|
||||
<span class='text-base text-brand-dark leading-[1.6] flex-shrink-0 pt-px max-sm:text-sm'>
|
||||
{tp.schedule.flowLabel}
|
||||
</span>
|
||||
<ul class='timeline-list m-0 pl-[22px] list-disc flex flex-col gap-1'>
|
||||
{
|
||||
tp.schedule.flow.map((item) => (
|
||||
<li class='text-xl font-medium text-brand-dark leading-[1.4] max-sm:text-base'>
|
||||
<Fragment set:html={item} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tile: Food Map (spans both rows) -->
|
||||
<div
|
||||
id='food-block'
|
||||
class='col-start-2 row-[1/span_2] flex flex-col gap-4 bg-surface border-2 border-border rounded-[20px] p-6 px-7 backdrop-blur-sm [-webkit-backdrop-filter:blur(4px)] shadow-[0_4px_16px_rgba(82,119,195,0.06)] scroll-mt-6 animate-hero-fade-up [animation-delay:0.34s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-lg:col-auto max-lg:row-auto max-sm:rounded-2xl max-sm:p-5 max-sm:px-[18px]'
|
||||
>
|
||||
<p
|
||||
class='text-xl font-medium tracking-[-0.05em] text-brand-dark m-0 leading-[1.4] flex items-center max-sm:text-[17px]'
|
||||
>
|
||||
<span class='text-xl leading-none inline-block mr-3 align-middle'>{tp.food.emoji}</span>
|
||||
<span>{tp.food.title}</span>
|
||||
</p>
|
||||
<figure
|
||||
class='food-map flex-1 m-0 min-h-40 rounded-[14px] overflow-hidden bg-[#f6fafc] flex max-lg:block max-sm:rounded-xl'
|
||||
>
|
||||
<img
|
||||
src={foodMapSrc}
|
||||
alt={tp.food.mapAlt}
|
||||
class='food-map-img block w-full object-contain object-top max-lg:h-auto'
|
||||
loading='lazy'
|
||||
decoding='async'
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
@@ -2,7 +2,7 @@
|
||||
import Navbar from './Navbar.astro';
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import { type Locale, getTranslations, getPageUrl } from '../i18n/config';
|
||||
import { ArrowRight } from '@lucide/astro';
|
||||
|
||||
interface Props {
|
||||
locale: Locale;
|
||||
}
|
||||
@@ -18,92 +18,46 @@ const url = (p: Parameters<typeof getPageUrl>[1]) => getPageUrl(locale, p);
|
||||
<Navbar locale={locale} activePage='home' />
|
||||
|
||||
<!-- ======= Hero Card ======= -->
|
||||
<div
|
||||
class='flex-1 relative flex flex-col rounded-3xl border-2 border-border bg-[#f9fbff] overflow-hidden min-h-[640px] animate-hero-fade-in motion-reduce:animate-none motion-reduce:opacity-100 max-sm:rounded-2xl max-sm:min-h-auto'
|
||||
>
|
||||
<!-- Layer 0: dot-grid texture -->
|
||||
<div
|
||||
class='absolute inset-0 z-0 [background-image:radial-gradient(ellipse_80%_70%_at_60%_48%,transparent_15%,rgba(249,251,255,0.85)_85%),radial-gradient(circle,rgba(126,186,228,0.28)_2.5px,transparent_2.5px)] [background-size:auto,20px_20px] pointer-events-none will-change-transform animate-hero-fade-in [animation-delay:0.06s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:will-change-auto'
|
||||
aria-hidden='true'
|
||||
>
|
||||
<div class='hero-card'>
|
||||
<!-- Layer 0: dot-grid texture (bottom-most) -->
|
||||
<div class='bg-texture' aria-hidden='true'></div>
|
||||
|
||||
<!-- Layer 1a: concentric circles (top-left) -->
|
||||
<div class='bg-circles' aria-hidden='true'>
|
||||
<div class='circle c1'></div>
|
||||
<div class='circle c2'></div>
|
||||
<div class='circle c3'></div>
|
||||
</div>
|
||||
|
||||
<!-- Layer 1a: concentric circles -->
|
||||
<div
|
||||
class='absolute inset-0 z-[1] pointer-events-none will-change-transform motion-reduce:will-change-auto'
|
||||
aria-hidden='true'
|
||||
>
|
||||
<div
|
||||
class='absolute rounded-full border-[1.5px] border-[rgba(126,186,228,0.28)] top-0 left-0 shadow-[0_2px_8px_rgba(126,186,228,0.12),0_10px_28px_rgba(126,186,228,0.2)] animate-hero-fade-in [animation-delay:0.1s] w-[502px] h-[502px] translate-x-[-269px] translate-y-[-241px] motion-reduce:animate-none motion-reduce:opacity-100 max-lg:w-[300px] max-lg:h-[300px] max-lg:translate-x-[-160px] max-lg:translate-y-[-144px] max-sm:w-[220px] max-sm:h-[220px] max-sm:translate-x-[-120px] max-sm:translate-y-[-108px]'
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class='absolute rounded-full border-[1.5px] border-[rgba(126,186,228,0.28)] top-0 left-0 shadow-[0_2px_8px_rgba(126,186,228,0.12),0_10px_28px_rgba(126,186,228,0.2)] animate-hero-fade-in [animation-delay:0.16s] w-[459px] h-[459px] translate-x-[-248px] translate-y-[-220px] motion-reduce:animate-none motion-reduce:opacity-100 max-lg:w-[275px] max-lg:h-[275px] max-lg:translate-x-[-148px] max-lg:translate-y-[-132px] max-sm:w-[200px] max-sm:h-[200px] max-sm:translate-x-[-108px] max-sm:translate-y-[-96px]'
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class='absolute rounded-full border-[1.5px] border-[rgba(126,186,228,0.28)] top-0 left-0 shadow-[0_2px_8px_rgba(126,186,228,0.12),0_10px_28px_rgba(126,186,228,0.2)] animate-hero-fade-in [animation-delay:0.22s] w-[408px] h-[408px] translate-x-[-222px] translate-y-[-199px] motion-reduce:animate-none motion-reduce:opacity-100 max-lg:w-[244px] max-lg:h-[244px] max-lg:translate-x-[-133px] max-lg:translate-y-[-119px] max-sm:w-[178px] max-sm:h-[178px] max-sm:translate-x-[-96px] max-sm:translate-y-[-86px]'
|
||||
>
|
||||
</div>
|
||||
<!-- Layer 1b: interactive NixOS snowflake — background decoration anchored to bottom-right -->
|
||||
<div class='hero-decoration' aria-hidden='true'>
|
||||
<canvas id='nixflake'></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Layer 1b: NixOS snowflake canvas -->
|
||||
<div
|
||||
class='absolute right-[-80px] bottom-[-120px] z-[2] pointer-events-none leading-none animate-hero-fade-in [animation-delay:0.18s] motion-reduce:animate-none motion-reduce:opacity-100 max-lg:right-[-120px] max-lg:bottom-[-140px] max-lg:scale-[0.7] max-lg:origin-bottom-right max-sm:block max-sm:right-[-90px] max-sm:bottom-[-100px] max-sm:scale-[0.7] max-sm:origin-bottom-right max-sm:animate-hero-fade-in-70 motion-reduce:max-sm:opacity-70 [@media(max-width:639px)_and_(max-height:683px)]:hidden'
|
||||
aria-hidden='true'
|
||||
>
|
||||
<canvas id='nixflake' class='block max-w-none pointer-events-none'></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Layer 2: foreground content -->
|
||||
<div
|
||||
class='flex-1 relative z-[3] flex items-center min-h-[640px] p-12 px-20 max-lg:p-10 max-lg:px-10 max-lg:pb-12 max-lg:min-h-[520px] max-sm:p-8 max-sm:px-6 max-sm:pb-11 max-sm:min-h-[560px]'
|
||||
>
|
||||
<div class='w-full max-w-[640px] will-change-transform motion-reduce:will-change-auto max-lg:max-w-full'>
|
||||
<p
|
||||
class='text-[clamp(1.5rem,3.33vw,3rem)] font-semibold tracking-[-0.05em] text-brand-dark m-0 mb-4 leading-[1.2] [text-shadow:0_4px_12px_rgba(3,15,32,0.1)] animate-hero-fade-up [animation-delay:0.12s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none'
|
||||
>
|
||||
<!-- Layer 2: foreground content (text floats above all background layers) -->
|
||||
<div class='hero-content'>
|
||||
<div class='hero-text'>
|
||||
<p class='conference-title'>
|
||||
{tp.hero.conferenceTitle}
|
||||
<span class='text-[#7ebae4]'>{tp.hero.year}</span>
|
||||
<span class='year'>{tp.hero.year}</span>
|
||||
</p>
|
||||
<h1
|
||||
class:list={[
|
||||
'headline text-[clamp(2rem,4.44vw,4rem)] font-semibold tracking-[-0.05em] text-brand-blue m-0 mb-7 leading-[1.15] [text-shadow:0_6px_18px_rgba(82,119,195,0.18)] [perspective:800px] [perspective-origin:50%_50%] animate-hero-fade-up [animation-delay:0.22s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-sm:leading-[1.35] max-sm:[perspective:none]',
|
||||
{ 'headline--split-mobile': locale === 'en' },
|
||||
]}
|
||||
>
|
||||
{tp.hero.headlineBefore}<span
|
||||
class='nix-highlight relative inline-block py-0.5 px-[14px] leading-[inherit] rounded-full [background:linear-gradient(135deg,rgba(196,226,247,0.65)_0%,rgba(126,186,228,0.42)_48%,rgba(82,140,200,0.34)_100%)] shadow-[inset_0_1px_0_rgba(255,255,255,0.7),inset_0_-1px_2px_rgba(82,119,195,0.2),0_6px_14px_rgba(82,119,195,0.22),0_14px_30px_rgba(82,119,195,0.14)] [transform-style:preserve-3d] will-change-transform translate-z-0 transition-shadow duration-300 [--gloss-x:30%] [--gloss-y:25%] motion-reduce:will-change-auto motion-reduce:transition-none max-sm:py-0 max-sm:px-[9px] max-sm:pb-0.5 max-sm:rounded-[999px] max-sm:bg-[rgba(197,219,245,0.78)] max-sm:text-brand-blue max-sm:text-[1.22em] max-sm:leading-[1.05] max-sm:align-baseline max-sm:shadow-none max-sm:[text-shadow:3px_4px_0_#9eb8e8] max-sm:![transform:none] max-sm:[transform-style:flat] max-sm:will-change-auto max-sm:transition-none'
|
||||
>
|
||||
{tp.hero.headlineBadge}
|
||||
</span><span class='headline-tail' set:html={tp.hero.headlineAfter} />
|
||||
<h1 class:list={['headline', { 'headline--split-mobile': locale === 'en' }]}>
|
||||
{tp.hero.headlineBefore}<span class='nix-highlight'>{tp.hero.headlineBadge}</span><span
|
||||
class='headline-tail'
|
||||
set:html={tp.hero.headlineAfter}
|
||||
/>
|
||||
</h1>
|
||||
<p
|
||||
class='text-[clamp(1rem,1.39vw,1.25rem)] text-[#1a2332] m-0 mb-10 leading-[1.65] tracking-[-0.02em] max-w-[480px] animate-hero-fade-up [animation-delay:0.32s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-sm:mb-8'
|
||||
>
|
||||
{tp.hero.lead}
|
||||
</p>
|
||||
<div class='flex gap-3 flex-wrap max-sm:flex-col max-sm:items-start max-sm:gap-[10px]'>
|
||||
<a
|
||||
class='inline-flex items-center px-5 py-3 rounded-3xl text-base font-medium tracking-[-0.05em] no-underline transition-[background,border-color,transform,box-shadow] duration-200 whitespace-nowrap cursor-pointer font-[inherit] animate-hero-fade-up [animation-delay:0.42s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none bg-brand-blue text-white border-[1.5px] border-brand-blue hover:bg-[#4269b8] hover:border-[#4269b8] hover:-translate-y-0.5 hover:shadow-[0_6px_20px_rgba(82,119,195,0.35)] max-sm:w-full max-sm:justify-center gap-1'
|
||||
href='https://nix.org.cn/app/'
|
||||
>
|
||||
{tp.hero.ctaPrimary}
|
||||
<ArrowRight size={24} />
|
||||
</a>
|
||||
<a
|
||||
class='inline-flex items-center px-5 py-3 rounded-3xl text-base font-medium tracking-[-0.05em] no-underline transition-[background,border-color,transform,box-shadow] duration-200 whitespace-nowrap cursor-pointer font-[inherit] animate-hero-fade-up [animation-delay:0.5s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none bg-[rgba(249,251,255,0.45)] text-brand-blue border-[1.5px] border-brand-blue backdrop-blur-[10px] backdrop-saturate-[140%] [-webkit-backdrop-filter:blur(10px)_saturate(1.4)] hover:bg-[rgba(82,119,195,0.12)] hover:-translate-y-0.5 hover:shadow-[0_4px_16px_rgba(82,119,195,0.15)] max-sm:w-full max-sm:justify-center'
|
||||
href={url('cmsGuide')}
|
||||
>
|
||||
{tp.hero.ctaSecondary}
|
||||
</a>
|
||||
<p class='lead'>{tp.hero.lead}</p>
|
||||
<div class='cta-group'>
|
||||
<a class='btn btn-primary' href={url('cmsGuide')}>{tp.hero.ctaPrimary}</a>
|
||||
<a class='btn btn-outline' href={url('cmsGuide')}>{tp.hero.ctaSecondary}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
<!-- ======= Scripts ======= -->
|
||||
<!-- ======= Interactive NixOS snowflake canvas + background parallax ======= -->
|
||||
<script>
|
||||
import { initNixflakeAnimation } from '../scripts/nixflake-animation';
|
||||
import { initBadgeTilt } from '../scripts/badge-tilt';
|
||||
@@ -113,3 +67,510 @@ const url = (p: Parameters<typeof getPageUrl>[1]) => getPageUrl(locale, p);
|
||||
initBadgeTilt();
|
||||
initBgParallax();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:global(.page) {
|
||||
padding: 0 16px 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
:global(.page) {
|
||||
padding: 0 8px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeInTo70 {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(16px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-12px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
:global(.navbar) {
|
||||
animation: heroFadeDown 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
}
|
||||
|
||||
.hero-card {
|
||||
--hero-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
flex: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 24px;
|
||||
border: 2px solid rgba(155, 206, 241, 0.8);
|
||||
background: #f9fbff;
|
||||
overflow: hidden;
|
||||
min-height: 640px;
|
||||
animation: heroFadeIn 0.5s var(--hero-ease) both;
|
||||
}
|
||||
|
||||
.bg-circles {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.circle {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid rgba(126, 186, 228, 0.28);
|
||||
top: 0;
|
||||
left: 0;
|
||||
box-shadow:
|
||||
0 2px 8px rgba(126, 186, 228, 0.12),
|
||||
0 10px 28px rgba(126, 186, 228, 0.2);
|
||||
animation: heroFadeIn 0.8s var(--hero-ease) both;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
width: 502px;
|
||||
height: 502px;
|
||||
transform: translate(-269px, -241px);
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.c2 {
|
||||
width: 459px;
|
||||
height: 459px;
|
||||
transform: translate(-248px, -220px);
|
||||
animation-delay: 0.16s;
|
||||
}
|
||||
|
||||
.c3 {
|
||||
width: 408px;
|
||||
height: 408px;
|
||||
transform: translate(-222px, -199px);
|
||||
animation-delay: 0.22s;
|
||||
}
|
||||
|
||||
.bg-texture {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
background-image:
|
||||
radial-gradient(ellipse 80% 70% at 60% 48%, transparent 15%, rgba(249, 251, 255, 0.85) 85%),
|
||||
radial-gradient(circle, rgba(126, 186, 228, 0.28) 2.5px, transparent 2.5px);
|
||||
background-size:
|
||||
auto,
|
||||
20px 20px;
|
||||
pointer-events: none;
|
||||
will-change: transform;
|
||||
animation: heroFadeIn 0.7s var(--hero-ease) both;
|
||||
animation-delay: 0.06s;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 640px;
|
||||
padding: 48px 80px;
|
||||
}
|
||||
|
||||
.hero-text {
|
||||
width: 100%;
|
||||
max-width: 640px;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.conference-title {
|
||||
font-size: clamp(1.5rem, 3.33vw, 3rem);
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.05em;
|
||||
color: #030f20;
|
||||
margin: 0 0 16px;
|
||||
line-height: 1.2;
|
||||
text-shadow: 0 4px 12px rgba(3, 15, 32, 0.1);
|
||||
animation: heroFadeUp 0.55s var(--hero-ease) both;
|
||||
animation-delay: 0.12s;
|
||||
}
|
||||
|
||||
.year {
|
||||
color: #7ebae4;
|
||||
}
|
||||
|
||||
.headline {
|
||||
font-size: clamp(2rem, 4.44vw, 4rem);
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.05em;
|
||||
color: #5277c3;
|
||||
margin: 0 0 28px;
|
||||
line-height: 1.15;
|
||||
text-shadow: 0 6px 18px rgba(82, 119, 195, 0.18);
|
||||
perspective: 800px;
|
||||
perspective-origin: 50% 50%;
|
||||
animation: heroFadeUp 0.6s var(--hero-ease) both;
|
||||
animation-delay: 0.22s;
|
||||
}
|
||||
|
||||
.nix-highlight {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding: 2px 14px;
|
||||
line-height: inherit;
|
||||
border-radius: 100px;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(196, 226, 247, 0.65) 0%,
|
||||
rgba(126, 186, 228, 0.42) 48%,
|
||||
rgba(82, 140, 200, 0.34) 100%
|
||||
);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.7),
|
||||
inset 0 -1px 2px rgba(82, 119, 195, 0.2),
|
||||
0 6px 14px rgba(82, 119, 195, 0.22),
|
||||
0 14px 30px rgba(82, 119, 195, 0.14);
|
||||
transform-style: preserve-3d;
|
||||
will-change: transform;
|
||||
transform: translateZ(0);
|
||||
transition: box-shadow 0.3s ease;
|
||||
--gloss-x: 30%;
|
||||
--gloss-y: 25%;
|
||||
}
|
||||
|
||||
.nix-highlight::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background: radial-gradient(
|
||||
circle at var(--gloss-x) var(--gloss-y),
|
||||
rgba(255, 255, 255, 0.55) 0%,
|
||||
rgba(255, 255, 255, 0.18) 28%,
|
||||
rgba(255, 255, 255, 0) 60%
|
||||
);
|
||||
mix-blend-mode: screen;
|
||||
pointer-events: none;
|
||||
transform: translateZ(1px);
|
||||
}
|
||||
|
||||
.nix-highlight::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
border: 1px solid rgba(255, 255, 255, 0.45);
|
||||
pointer-events: none;
|
||||
transform: translateZ(1px);
|
||||
}
|
||||
|
||||
/* 英文桌面:徽章与后半句间距(移动端由 block 换行,不需前导空格) */
|
||||
.headline--split-mobile .nix-highlight {
|
||||
margin-inline-end: 0.22em;
|
||||
}
|
||||
|
||||
.lead {
|
||||
font-size: clamp(1rem, 1.39vw, 1.25rem);
|
||||
color: #1a2332;
|
||||
margin: 0 0 40px;
|
||||
line-height: 1.65;
|
||||
letter-spacing: -0.02em;
|
||||
max-width: 480px;
|
||||
animation: heroFadeUp 0.55s var(--hero-ease) both;
|
||||
animation-delay: 0.32s;
|
||||
}
|
||||
|
||||
.cta-group {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 12px 20px;
|
||||
border-radius: 24px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.05em;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
background 0.2s,
|
||||
border-color 0.2s,
|
||||
transform 0.15s,
|
||||
box-shadow 0.2s;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
animation: heroFadeUp 0.5s var(--hero-ease) both;
|
||||
}
|
||||
|
||||
.cta-group .btn:nth-child(1) {
|
||||
animation-delay: 0.42s;
|
||||
}
|
||||
|
||||
.cta-group .btn:nth-child(2) {
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #5277c3;
|
||||
color: #fff;
|
||||
border: 1.5px solid #5277c3;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #4269b8;
|
||||
border-color: #4269b8;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(82, 119, 195, 0.35);
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: rgba(249, 251, 255, 0.45);
|
||||
color: #5277c3;
|
||||
border: 1.5px solid #5277c3;
|
||||
backdrop-filter: blur(10px) saturate(1.4);
|
||||
-webkit-backdrop-filter: blur(10px) saturate(1.4);
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
background: rgba(82, 119, 195, 0.12);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 16px rgba(82, 119, 195, 0.15);
|
||||
}
|
||||
|
||||
.hero-decoration {
|
||||
position: absolute;
|
||||
right: -80px;
|
||||
bottom: -120px;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
line-height: 0;
|
||||
animation: heroFadeIn 0.9s var(--hero-ease) both;
|
||||
animation-delay: 0.18s;
|
||||
}
|
||||
|
||||
.hero-decoration canvas {
|
||||
display: block;
|
||||
max-width: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
.hero-content {
|
||||
padding: 40px 40px 48px;
|
||||
min-height: 520px;
|
||||
}
|
||||
|
||||
.hero-text {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.hero-decoration {
|
||||
right: -120px;
|
||||
bottom: -140px;
|
||||
transform: scale(0.7);
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
transform: translate(-160px, -144px);
|
||||
}
|
||||
|
||||
.c2 {
|
||||
width: 275px;
|
||||
height: 275px;
|
||||
transform: translate(-148px, -132px);
|
||||
}
|
||||
|
||||
.c3 {
|
||||
width: 244px;
|
||||
height: 244px;
|
||||
transform: translate(-133px, -119px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
.headline :global(.headline-break) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.headline {
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
/* 英文:徽章后整段换行,第二行与第一行左缘对齐 */
|
||||
.headline--split-mobile .nix-highlight {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
.headline--split-mobile .headline-tail {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.hero-card {
|
||||
border-radius: 16px;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
padding: 32px 24px 44px;
|
||||
min-height: 560px;
|
||||
}
|
||||
|
||||
.lead {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.hero-decoration {
|
||||
display: block;
|
||||
right: -90px;
|
||||
bottom: -100px;
|
||||
transform: scale(0.7);
|
||||
transform-origin: bottom right;
|
||||
animation-name: heroFadeInTo70;
|
||||
}
|
||||
|
||||
.cta-group {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
width: 220px;
|
||||
height: 220px;
|
||||
transform: translate(-120px, -108px);
|
||||
}
|
||||
|
||||
.c2 {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
transform: translate(-108px, -96px);
|
||||
}
|
||||
|
||||
.c3 {
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
transform: translate(-96px, -86px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 639px) and (max-height: 683px) {
|
||||
.hero-decoration {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:global(.navbar),
|
||||
.hero-card,
|
||||
.bg-texture,
|
||||
.circle,
|
||||
.hero-decoration,
|
||||
.conference-title,
|
||||
.headline,
|
||||
.lead,
|
||||
.btn {
|
||||
animation: none !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.hero-decoration,
|
||||
.bg-circles,
|
||||
.bg-texture,
|
||||
.hero-text,
|
||||
.nix-highlight {
|
||||
will-change: auto;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.conference-title,
|
||||
.headline,
|
||||
.lead,
|
||||
.btn,
|
||||
:global(.navbar) {
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.nix-highlight {
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) and (max-width: 639px) {
|
||||
.hero-decoration {
|
||||
opacity: 0.7 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 移动端:设计稿静态徽章(浅蓝胶囊 + 叠字阴影) */
|
||||
@media (max-width: 639px) {
|
||||
.headline {
|
||||
perspective: none;
|
||||
}
|
||||
|
||||
.nix-highlight {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding: 0 9px 2px;
|
||||
margin-inline: 0;
|
||||
border-radius: 999px;
|
||||
background: rgba(197, 219, 245, 0.78);
|
||||
color: #5277c3;
|
||||
font-size: 1.22em;
|
||||
line-height: 1.05;
|
||||
vertical-align: baseline;
|
||||
box-shadow: none;
|
||||
/* 单层文字 + 右下偏移浅色影,避免 ::before 叠在主字上方 */
|
||||
text-shadow: 3px 4px 0 #9eb8e8;
|
||||
transform: none !important;
|
||||
transform-style: flat;
|
||||
will-change: auto;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.nix-highlight::before,
|
||||
.nix-highlight::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,61 +16,49 @@ const url = (l: Locale, p: Page) => getPageUrl(l, p);
|
||||
const switchHref = url(otherLocale, activePage);
|
||||
---
|
||||
|
||||
<header class='flex items-center px-5 h-14 flex-shrink-0 relative z-20 max-sm:px-4'>
|
||||
<a href={url(locale, 'home')} class='flex items-center flex-shrink-0 no-underline' aria-label={t.nav.ariaLogoHome}>
|
||||
<img src='/images/shared/nix-cn.svg' alt='NixCN' width='110' height='31' class='h-[31px] w-auto block' />
|
||||
<header class='navbar'>
|
||||
<a href={url(locale, 'home')} class='navbar-logo' aria-label={t.nav.ariaLogoHome}>
|
||||
<img src='/images/shared/nix-cn.svg' alt='NixCN' width='110' height='31' class='logo-img' />
|
||||
</a>
|
||||
|
||||
<nav class='flex items-center gap-8 absolute left-1/2 -translate-x-1/2 max-lg:hidden' aria-label={t.nav.ariaMain}>
|
||||
<nav class='navbar-links' aria-label={t.nav.ariaMain}>
|
||||
<a
|
||||
href={url(locale, 'home')}
|
||||
class:list={[
|
||||
'nav-link text-sm font-medium text-[#a7b8d0] no-underline tracking-[-0.05em] whitespace-nowrap transition-colors duration-200 relative pb-0.5 hover:text-brand-blue',
|
||||
activePage === 'home' && 'active text-brand-blue',
|
||||
]}
|
||||
class:list={['nav-link', activePage === 'home' && 'active']}
|
||||
aria-current={activePage === 'home' ? 'page' : undefined}
|
||||
>
|
||||
{t.nav.home}
|
||||
</a>
|
||||
<a
|
||||
href={url(locale, 'eventGuide')}
|
||||
class:list={[
|
||||
'nav-link text-sm font-medium text-[#a7b8d0] no-underline tracking-[-0.05em] whitespace-nowrap transition-colors duration-200 relative pb-0.5 hover:text-brand-blue',
|
||||
activePage === 'eventGuide' && 'active text-brand-blue',
|
||||
]}
|
||||
aria-current={activePage === 'eventGuide' ? 'page' : undefined}
|
||||
href={url(locale, 'calendar')}
|
||||
class:list={['nav-link', activePage === 'calendar' && 'active']}
|
||||
aria-current={activePage === 'calendar' ? 'page' : undefined}
|
||||
>
|
||||
{t.nav.eventGuide}
|
||||
{t.nav.calendar}
|
||||
</a>
|
||||
<a
|
||||
href={url(locale, 'cmsGuide')}
|
||||
class:list={[
|
||||
'nav-link text-sm font-medium text-[#a7b8d0] no-underline tracking-[-0.05em] whitespace-nowrap transition-colors duration-200 relative pb-0.5 hover:text-brand-blue',
|
||||
activePage === 'cmsGuide' && 'active text-brand-blue',
|
||||
]}
|
||||
class:list={['nav-link', activePage === 'cmsGuide' && 'active']}
|
||||
aria-current={activePage === 'cmsGuide' ? 'page' : undefined}
|
||||
>
|
||||
{t.nav.cmsGuide}
|
||||
</a>
|
||||
<a
|
||||
href={url(locale, 'souvenir')}
|
||||
class:list={[
|
||||
'nav-link text-sm font-medium text-[#a7b8d0] no-underline tracking-[-0.05em] whitespace-nowrap transition-colors duration-200 relative pb-0.5 hover:text-brand-blue',
|
||||
activePage === 'souvenir' && 'active text-brand-blue',
|
||||
]}
|
||||
class:list={['nav-link', activePage === 'souvenir' && 'active']}
|
||||
aria-current={activePage === 'souvenir' ? 'page' : undefined}
|
||||
>
|
||||
{t.nav.souvenir}
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class='flex items-center gap-4 ml-auto max-sm:hidden'>
|
||||
<div class='navbar-social'>
|
||||
<a
|
||||
href='https://t.me/nixcnmeetup'
|
||||
href='https://t.me/nixos_cn'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
aria-label={t.nav.ariaTelegram}
|
||||
class='text-[#a7b8d0] flex items-center no-underline transition-colors duration-200 hover:text-brand-blue'
|
||||
class='social-link'
|
||||
>
|
||||
<svg viewBox='0 0 24 24' fill='currentColor' width='22' height='22' aria-hidden='true'>
|
||||
<path
|
||||
@@ -80,11 +68,11 @@ const switchHref = url(otherLocale, activePage);
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href='https://matrix.to/#/#nix-cn-meetup:rebmit.moe'
|
||||
href='https://matrix.to/#/#nixos-cn:matrix.org'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
aria-label={t.nav.ariaMatrix}
|
||||
class='text-[#a7b8d0] flex items-center no-underline transition-colors duration-200 hover:text-brand-blue'
|
||||
class='social-link'
|
||||
>
|
||||
<svg viewBox='0 0 24 24' fill='currentColor' width='22' height='22' aria-hidden='true'>
|
||||
<path
|
||||
@@ -93,12 +81,8 @@ const switchHref = url(otherLocale, activePage);
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<span class='block w-px h-5 bg-[#a7b8d0] opacity-40' aria-hidden='true'></span>
|
||||
<a
|
||||
href={switchHref}
|
||||
aria-label={t.nav.ariaLangSwitch}
|
||||
class='text-[#a7b8d0] flex items-center no-underline transition-colors duration-200 hover:text-brand-blue'
|
||||
>
|
||||
<span class='social-divider' aria-hidden='true'></span>
|
||||
<a href={switchHref} aria-label={t.nav.ariaLangSwitch} class='social-link'>
|
||||
<svg
|
||||
viewBox='0 0 24 24'
|
||||
fill='none'
|
||||
@@ -117,70 +101,218 @@ const switchHref = url(otherLocale, activePage);
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<details class='hidden ml-2 max-lg:block'>
|
||||
<summary
|
||||
class='hamburger flex flex-col gap-[5px] p-[8px_4px] cursor-pointer list-none'
|
||||
aria-label={t.nav.ariaMenu}
|
||||
>
|
||||
<span class='block w-[22px] h-0.5 bg-brand-blue rounded-[2px] transition-all duration-200'></span>
|
||||
<span class='block w-[22px] h-0.5 bg-brand-blue rounded-[2px] transition-all duration-200'></span>
|
||||
<span class='block w-[22px] h-0.5 bg-brand-blue rounded-[2px] transition-all duration-200'></span>
|
||||
<details class='mobile-menu'>
|
||||
<summary class='hamburger' aria-label={t.nav.ariaMenu}>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</summary>
|
||||
<nav
|
||||
class='mobile-nav fixed top-14 left-2 right-2 bg-[rgba(249,251,255,0.97)] backdrop-blur-[10px] [-webkit-backdrop-filter:blur(10px)] border border-[rgba(155,206,241,0.5)] rounded-2xl py-4 px-5 flex flex-col gap-1 z-[100] shadow-[0_8px_32px_rgba(82,119,195,0.12)]'
|
||||
aria-label={t.nav.ariaMobile}
|
||||
>
|
||||
<a
|
||||
href={url(locale, 'home')}
|
||||
aria-current={activePage === 'home' ? 'page' : undefined}
|
||||
class='text-[15px] font-medium text-brand-blue no-underline py-2.5 border-b border-[rgba(155,206,241,0.3)] transition-opacity duration-150 hover:opacity-70 last:border-b-0'
|
||||
>
|
||||
{t.nav.home}
|
||||
<nav class='mobile-nav' aria-label={t.nav.ariaMobile}>
|
||||
<a href={url(locale, 'home')} aria-current={activePage === 'home' ? 'page' : undefined}>{t.nav.home}</a>
|
||||
<a href={url(locale, 'calendar')} aria-current={activePage === 'calendar' ? 'page' : undefined}>
|
||||
{t.nav.calendar}
|
||||
</a>
|
||||
<a
|
||||
href={url(locale, 'eventGuide')}
|
||||
aria-current={activePage === 'eventGuide' ? 'page' : undefined}
|
||||
class='text-[15px] font-medium text-brand-blue no-underline py-2.5 border-b border-[rgba(155,206,241,0.3)] transition-opacity duration-150 hover:opacity-70 last:border-b-0'
|
||||
>
|
||||
{t.nav.eventGuide}
|
||||
</a>
|
||||
<a
|
||||
href={url(locale, 'cmsGuide')}
|
||||
aria-current={activePage === 'cmsGuide' ? 'page' : undefined}
|
||||
class='text-[15px] font-medium text-brand-blue no-underline py-2.5 border-b border-[rgba(155,206,241,0.3)] transition-opacity duration-150 hover:opacity-70 last:border-b-0'
|
||||
>
|
||||
<a href={url(locale, 'cmsGuide')} aria-current={activePage === 'cmsGuide' ? 'page' : undefined}>
|
||||
{t.nav.cmsGuide}
|
||||
</a>
|
||||
<a
|
||||
href={url(locale, 'souvenir')}
|
||||
aria-current={activePage === 'souvenir' ? 'page' : undefined}
|
||||
class='text-[15px] font-medium text-brand-blue no-underline py-2.5 border-b border-[rgba(155,206,241,0.3)] transition-opacity duration-150 hover:opacity-70 last:border-b-0'
|
||||
>
|
||||
<a href={url(locale, 'souvenir')} aria-current={activePage === 'souvenir' ? 'page' : undefined}>
|
||||
{t.nav.souvenir}
|
||||
</a>
|
||||
<hr class='border-0 border-t border-[rgba(155,206,241,0.4)] my-1' />
|
||||
<a
|
||||
href='https://t.me/nixcnmeetup'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
class='text-[15px] font-medium text-brand-blue no-underline py-2.5 border-b border-[rgba(155,206,241,0.3)] transition-opacity duration-150 hover:opacity-70 last:border-b-0'
|
||||
>
|
||||
Telegram
|
||||
</a>
|
||||
<a
|
||||
href='https://matrix.to/#/#nix-cn-meetup:rebmit.moe'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
class='text-[15px] font-medium text-brand-blue no-underline py-2.5 border-b border-[rgba(155,206,241,0.3)] transition-opacity duration-150 hover:opacity-70 last:border-b-0'
|
||||
>
|
||||
Matrix
|
||||
</a>
|
||||
<a
|
||||
href={switchHref}
|
||||
class='text-[15px] font-medium text-brand-blue no-underline py-2.5 border-b border-[rgba(155,206,241,0.3)] transition-opacity duration-150 hover:opacity-70 last:border-b-0'
|
||||
>
|
||||
{t.nav.ariaLangSwitch}
|
||||
</a>
|
||||
<hr class='mobile-nav-divider' />
|
||||
<a href='https://t.me/nixos_cn' target='_blank' rel='noopener noreferrer'>Telegram</a>
|
||||
<a href='https://matrix.to/#/#nixos-cn:matrix.org' target='_blank' rel='noopener noreferrer'>Matrix</a>
|
||||
<a href={switchHref}>{t.nav.ariaLangSwitch}</a>
|
||||
</nav>
|
||||
</details>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
.navbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
height: 56px;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.navbar-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.logo-img {
|
||||
height: 31px;
|
||||
width: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navbar-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #a7b8d0;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.05em;
|
||||
white-space: nowrap;
|
||||
transition: color 0.2s;
|
||||
position: relative;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: #5277c3;
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
color: #5277c3;
|
||||
}
|
||||
|
||||
.nav-link.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -18px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 71px;
|
||||
height: 22px;
|
||||
background: url('/images/shared/tab-line.svg') center / contain no-repeat;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.navbar-social {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.social-link {
|
||||
color: #a7b8d0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.social-link:hover {
|
||||
color: #5277c3;
|
||||
}
|
||||
|
||||
.social-divider {
|
||||
display: block;
|
||||
width: 1px;
|
||||
height: 20px;
|
||||
background: #a7b8d0;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.mobile-menu {
|
||||
display: none;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.hamburger {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding: 8px 4px;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.hamburger::marker,
|
||||
.hamburger::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hamburger span {
|
||||
display: block;
|
||||
width: 22px;
|
||||
height: 2px;
|
||||
background: #5277c3;
|
||||
border-radius: 2px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.mobile-nav {
|
||||
position: fixed;
|
||||
top: 56px;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
background: rgba(249, 251, 255, 0.97);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(155, 206, 241, 0.5);
|
||||
border-radius: 16px;
|
||||
padding: 16px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
z-index: 100;
|
||||
box-shadow: 0 8px 32px rgba(82, 119, 195, 0.12);
|
||||
}
|
||||
|
||||
.mobile-nav a {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #5277c3;
|
||||
text-decoration: none;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid rgba(155, 206, 241, 0.3);
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.mobile-nav a:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.mobile-nav a:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.mobile-nav a[aria-current='page'] {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mobile-nav-divider {
|
||||
border: none;
|
||||
border-top: 1px solid rgba(155, 206, 241, 0.4);
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
.navbar-links {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navbar-social {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
.navbar {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.navbar-social {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -17,68 +17,577 @@ const ts = tp.comingSoon;
|
||||
<Navbar locale={locale} activePage='souvenir' />
|
||||
|
||||
<!-- ======= Hero Banner ======= -->
|
||||
<section
|
||||
class='relative pt-[38px] pb-12 px-6 text-center isolate overflow-hidden min-h-[215px] max-lg:pt-8 max-lg:pb-10 max-lg:px-5 max-sm:pt-6 max-sm:pb-8 max-sm:px-4'
|
||||
aria-labelledby='souvenir-title'
|
||||
>
|
||||
<div class='absolute inset-0 z-0 pointer-events-none overflow-hidden' aria-hidden='true'>
|
||||
<img
|
||||
class='absolute inset-0 w-full h-full object-cover object-top block animate-hero-fade-in motion-reduce:animate-none motion-reduce:opacity-100'
|
||||
src='/images/shared/hero-bg.png'
|
||||
alt=''
|
||||
/>
|
||||
<section class='hero-banner' aria-labelledby='souvenir-title'>
|
||||
<div class='hero-bg' aria-hidden='true'>
|
||||
<img class='hero-bg-img' src='/images/souvenir/hero-bg.png' alt='' />
|
||||
</div>
|
||||
<div class='relative z-[1] max-w-[720px] mx-auto'>
|
||||
<h1
|
||||
id='souvenir-title'
|
||||
class='text-[clamp(2.5rem,5.5vw,4.5rem)] font-semibold text-brand-dark m-0 mb-4 tracking-[-0.05em] leading-[1.1] [text-shadow:0_4px_14px_rgba(3,15,32,0.08)] animate-hero-fade-up [animation-delay:0.1s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-sm:mb-3'
|
||||
>
|
||||
{tp.hero.title}
|
||||
</h1>
|
||||
<p
|
||||
class='text-[clamp(0.875rem,1vw,1rem)] text-[#1a2332] m-0 leading-[1.7] tracking-[-0.02em] max-w-[640px] mx-auto animate-hero-fade-up [animation-delay:0.18s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-sm:text-[13px] max-sm:leading-[1.65]'
|
||||
>
|
||||
{tp.hero.sub}
|
||||
</p>
|
||||
<div class='hero-content'>
|
||||
<h1 class='hero-title' id='souvenir-title'>{tp.hero.title}</h1>
|
||||
<p class='hero-sub'>{tp.hero.sub}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ======= Feature Card ======= -->
|
||||
<main
|
||||
class='relative z-[2] w-full max-w-[1152px] mx-auto px-10 pt-4 pb-[120px] max-lg:px-7 max-lg:pt-3 max-lg:pb-[100px] max-sm:px-[18px] max-sm:pt-2 max-sm:pb-20'
|
||||
>
|
||||
<section
|
||||
class='relative min-h-[400px] rounded-3xl border-2 border-border overflow-hidden flex items-stretch animate-hero-fade-in [animation-delay:0.22s] motion-reduce:animate-none motion-reduce:opacity-100 max-lg:min-h-[360px] max-sm:min-h-0 [background-image:radial-gradient(ellipse_80%_70%_at_60%_48%,transparent_15%,rgba(249,251,255,0.85)_85%),radial-gradient(circle,rgba(126,186,228,0.28)_2.5px,transparent_2.5px)] [background-size:auto,20px_20px]'
|
||||
aria-labelledby='feature-title'
|
||||
>
|
||||
<div
|
||||
class='relative z-[2] flex flex-col justify-center gap-6 p-[60px_48px] max-w-[720px] max-lg:p-[48px_36px] max-sm:p-[32px_24px_40px] max-sm:gap-5'
|
||||
>
|
||||
<h2
|
||||
id='feature-title'
|
||||
class='text-[clamp(1.5rem,2.2vw,2rem)] font-semibold text-brand-dark m-0 tracking-[-0.04em] leading-[1.25] animate-hero-fade-up [animation-delay:0.32s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-sm:text-[22px]'
|
||||
>
|
||||
{tp.card.title}
|
||||
</h2>
|
||||
<p
|
||||
class='text-[15px] leading-[1.8] text-[#1a2332] m-0 tracking-[-0.01em] max-w-[560px] animate-hero-fade-up [animation-delay:0.4s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-sm:text-sm max-sm:leading-[1.75]'
|
||||
>
|
||||
{tp.card.body}
|
||||
</p>
|
||||
<!-- hover:bg-[#4269b8] hover:-translate-y-0.5 hover:shadow-[0_6px_20px_rgba(82,119,195,0.35)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-brand-blue focus-visible:outline-offset-[3px] -->
|
||||
<button
|
||||
type='button'
|
||||
class='inline-flex items-center gap-[10px] self-start py-[18px] px-8 border-none rounded-[36px] bg-gray-400 text-white text-base font-medium font-[inherit] tracking-[-0.05em] cursor-pointer transition-[background,transform,box-shadow] duration-200 whitespace-nowrap animate-hero-fade-up [animation-delay:0.48s] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:transform-none max-sm:w-full max-sm:justify-center max-sm:py-4 max-sm:px-6'
|
||||
id='souvenir-cta-btn'
|
||||
aria-haspopup='dialog'
|
||||
>
|
||||
<main class='souvenir-main'>
|
||||
<section class='feature-card' aria-labelledby='feature-title'>
|
||||
<div class='card-bg-texture' aria-hidden='true'>
|
||||
<img class='card-texture-img' src='/images/souvenir/card-texture.png' alt='' />
|
||||
</div>
|
||||
<div class='card-snowflake' aria-hidden='true'>
|
||||
<img src='/images/souvenir/card-snowflake.png' alt='' width='327' height='325' />
|
||||
</div>
|
||||
<div class='card-content'>
|
||||
<h2 class='card-title' id='feature-title'>{tp.card.title}</h2>
|
||||
<p class='card-body'>{tp.card.body}</p>
|
||||
<button type='button' class='cta-btn' id='souvenir-cta-btn' aria-haspopup='dialog'>
|
||||
{tp.card.cta}
|
||||
<img class='cta-arrow' src='/images/souvenir/cta-arrow.png' alt='' width='35' height='35' />
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- ======= 等待开放弹窗 ======= -->
|
||||
<div class='coming-soon-overlay' id='coming-soon-overlay' hidden aria-hidden='true'>
|
||||
<button
|
||||
type='button'
|
||||
class='coming-soon-backdrop'
|
||||
id='coming-soon-backdrop'
|
||||
tabindex='-1'
|
||||
aria-label={ts.ariaClose}></button>
|
||||
<div
|
||||
class='coming-soon-dialog'
|
||||
role='dialog'
|
||||
aria-modal='true'
|
||||
aria-labelledby='coming-soon-title'
|
||||
id='coming-soon-dialog'
|
||||
>
|
||||
<div class='coming-soon-icon' aria-hidden='true'>
|
||||
<svg width='48' height='48' viewBox='0 0 48 48' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
<circle cx='24' cy='24' r='22' stroke='currentColor' stroke-width='2' opacity='0.25'></circle>
|
||||
<path
|
||||
d='M24 14v12l8 4'
|
||||
stroke='currentColor'
|
||||
stroke-width='2.5'
|
||||
stroke-linecap='round'
|
||||
stroke-linejoin='round'></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class='coming-soon-title' id='coming-soon-title'>{ts.title}</h3>
|
||||
<p class='coming-soon-body'>{ts.body}</p>
|
||||
<button type='button' class='coming-soon-close' id='coming-soon-close'>
|
||||
{ts.close}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ======= 底部同心圆装饰(Figma 切图) ======= -->
|
||||
<div class='bottom-circles' aria-hidden='true'>
|
||||
<img src='/images/souvenir/bottom-circles.png' alt='' />
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
:global(.page) {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes heroFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(16px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-12px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
:global(.navbar) {
|
||||
animation: heroFadeDown 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
}
|
||||
|
||||
/* ===== Hero Banner ===== */
|
||||
.hero-banner {
|
||||
--hero-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
position: relative;
|
||||
padding: 38px 24px 48px;
|
||||
text-align: center;
|
||||
isolation: isolate;
|
||||
overflow: hidden;
|
||||
min-height: 215px;
|
||||
}
|
||||
|
||||
.hero-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero-bg-img {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center top;
|
||||
display: block;
|
||||
animation: heroFadeIn 0.6s var(--hero-ease) both;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: clamp(2.5rem, 5.5vw, 4.5rem);
|
||||
font-weight: 600;
|
||||
color: #030f20;
|
||||
margin: 0 0 16px;
|
||||
letter-spacing: -0.05em;
|
||||
line-height: 1.1;
|
||||
text-shadow: 0 4px 14px rgba(3, 15, 32, 0.08);
|
||||
animation: heroFadeUp 0.55s var(--hero-ease) both;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.hero-sub {
|
||||
font-size: clamp(0.875rem, 1vw, 1rem);
|
||||
color: #1a2332;
|
||||
margin: 0;
|
||||
line-height: 1.7;
|
||||
letter-spacing: -0.02em;
|
||||
max-width: 640px;
|
||||
margin-inline: auto;
|
||||
animation: heroFadeUp 0.5s var(--hero-ease) both;
|
||||
animation-delay: 0.18s;
|
||||
}
|
||||
|
||||
/* ===== Feature Card ===== */
|
||||
.souvenir-main {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
max-width: 1152px;
|
||||
margin: 0 auto;
|
||||
padding: 16px 40px 120px;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
position: relative;
|
||||
min-height: 400px;
|
||||
border-radius: 24px;
|
||||
border: 2px solid rgba(155, 206, 241, 0.8);
|
||||
background: #f9fbff;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
animation: heroFadeIn 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
animation-delay: 0.22s;
|
||||
}
|
||||
|
||||
.card-bg-texture {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-texture-img {
|
||||
position: absolute;
|
||||
/* Figma 198:294 — 底纹在卡片内偏移 */
|
||||
left: -15%;
|
||||
top: -35%;
|
||||
width: 130%;
|
||||
height: auto;
|
||||
min-height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.card-snowflake {
|
||||
position: absolute;
|
||||
/* Figma 202:325 — x=737 y=155 within 1120×400 card */
|
||||
right: 56px;
|
||||
top: 155px;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
animation: heroFadeIn 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
animation-delay: 0.36s;
|
||||
}
|
||||
|
||||
.card-snowflake img {
|
||||
display: block;
|
||||
width: 327px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 24px;
|
||||
padding: 60px 48px;
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: clamp(1.5rem, 2.2vw, 2rem);
|
||||
font-weight: 600;
|
||||
color: #030f20;
|
||||
margin: 0;
|
||||
letter-spacing: -0.04em;
|
||||
line-height: 1.25;
|
||||
animation: heroFadeUp 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
animation-delay: 0.32s;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
font-size: 15px;
|
||||
line-height: 1.8;
|
||||
color: #1a2332;
|
||||
margin: 0;
|
||||
letter-spacing: -0.01em;
|
||||
max-width: 560px;
|
||||
animation: heroFadeUp 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.cta-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
align-self: flex-start;
|
||||
padding: 18px 32px;
|
||||
border: none;
|
||||
border-radius: 36px;
|
||||
background: #5277c3;
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
letter-spacing: -0.05em;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background 0.2s,
|
||||
transform 0.15s,
|
||||
box-shadow 0.2s;
|
||||
white-space: nowrap;
|
||||
animation: heroFadeUp 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
animation-delay: 0.48s;
|
||||
}
|
||||
|
||||
.cta-btn:hover {
|
||||
background: #4269b8;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(82, 119, 195, 0.35);
|
||||
}
|
||||
|
||||
.cta-btn:focus-visible {
|
||||
outline: 2px solid #5277c3;
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
/* ===== Coming Soon Modal ===== */
|
||||
@keyframes modalOverlayIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes modalDialogIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(12px) scale(0.98);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.coming-soon-overlay {
|
||||
--modal-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1000;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.coming-soon-overlay[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.coming-soon-overlay.is-open {
|
||||
animation: modalOverlayIn 0.28s var(--modal-ease) both;
|
||||
}
|
||||
|
||||
.coming-soon-backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: rgba(3, 15, 32, 0.45);
|
||||
backdrop-filter: blur(4px);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.coming-soon-dialog {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: min(100%, 420px);
|
||||
padding: 36px 32px 28px;
|
||||
border-radius: 24px;
|
||||
border: 2px solid rgba(155, 206, 241, 0.8);
|
||||
background: #f9fbff;
|
||||
box-shadow:
|
||||
0 24px 48px rgba(3, 15, 32, 0.14),
|
||||
0 0 0 1px rgba(255, 255, 255, 0.6) inset;
|
||||
text-align: center;
|
||||
animation: modalDialogIn 0.35s var(--modal-ease) both;
|
||||
animation-delay: 0.04s;
|
||||
}
|
||||
|
||||
.coming-soon-icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 16px;
|
||||
color: #5277c3;
|
||||
}
|
||||
|
||||
.coming-soon-title {
|
||||
margin: 0 0 12px;
|
||||
font-size: clamp(1.25rem, 2.5vw, 1.5rem);
|
||||
font-weight: 600;
|
||||
color: #030f20;
|
||||
letter-spacing: -0.04em;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.coming-soon-body {
|
||||
margin: 0 0 28px;
|
||||
font-size: 15px;
|
||||
line-height: 1.75;
|
||||
color: #1a2332;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.coming-soon-close {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 140px;
|
||||
padding: 14px 28px;
|
||||
border: none;
|
||||
border-radius: 36px;
|
||||
background: #5277c3;
|
||||
color: #ffffff;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
letter-spacing: -0.03em;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background 0.2s,
|
||||
transform 0.15s,
|
||||
box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.coming-soon-close:hover {
|
||||
background: #4269b8;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 20px rgba(82, 119, 195, 0.35);
|
||||
}
|
||||
|
||||
.coming-soon-close:focus-visible {
|
||||
outline: 2px solid #5277c3;
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.cta-arrow {
|
||||
display: block;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ===== 底部同心圆装饰 ===== */
|
||||
.bottom-circles {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: min(710px, 100%);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
animation: heroFadeIn 0.8s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.bottom-circles img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
.hero-banner {
|
||||
padding: 32px 20px 40px;
|
||||
}
|
||||
|
||||
.souvenir-main {
|
||||
padding: 12px 28px 100px;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
min-height: 360px;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 48px 36px;
|
||||
}
|
||||
|
||||
.card-snowflake {
|
||||
right: 24px;
|
||||
top: auto;
|
||||
bottom: 16px;
|
||||
}
|
||||
|
||||
.card-snowflake img {
|
||||
width: min(260px, 32vw);
|
||||
}
|
||||
|
||||
.bottom-circles {
|
||||
width: min(560px, 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
.hero-banner {
|
||||
padding: 24px 16px 32px;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.hero-sub {
|
||||
font-size: 13px;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.souvenir-main {
|
||||
padding: 8px 18px 80px;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 32px 24px 40px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
font-size: 14px;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.card-snowflake {
|
||||
right: -12px;
|
||||
bottom: -8px;
|
||||
top: auto;
|
||||
}
|
||||
|
||||
.card-snowflake img {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.cta-btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
.cta-arrow {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.bottom-circles {
|
||||
width: min(380px, 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:global(.navbar),
|
||||
.hero-bg-img,
|
||||
.hero-title,
|
||||
.hero-sub,
|
||||
.feature-card,
|
||||
.card-snowflake,
|
||||
.card-title,
|
||||
.card-body,
|
||||
.cta-btn {
|
||||
animation: none !important;
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.bottom-circles {
|
||||
animation: none !important;
|
||||
opacity: 1 !important;
|
||||
transform: translateX(-50%) !important;
|
||||
}
|
||||
|
||||
.coming-soon-overlay.is-open,
|
||||
.coming-soon-dialog {
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function initComingSoonModal() {
|
||||
const overlay = document.getElementById('coming-soon-overlay');
|
||||
|
||||
@@ -14,7 +14,7 @@ export function getTranslations(locale: Locale) {
|
||||
|
||||
export const PAGE_SLUGS = {
|
||||
home: '',
|
||||
eventGuide: 'event-guide',
|
||||
calendar: 'calendar',
|
||||
cmsGuide: 'cms-guide',
|
||||
souvenir: 'souvenir',
|
||||
} as const;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"zh-CN": {
|
||||
"nav": {
|
||||
"home": "首页",
|
||||
"eventGuide": "活动指南",
|
||||
"home": "HOME",
|
||||
"calendar": "活动指南",
|
||||
"cmsGuide": "CMS 系统指引",
|
||||
"souvenir": "纪念品信息",
|
||||
"ariaLogoHome": "NixCN 首页",
|
||||
@@ -26,11 +26,11 @@
|
||||
"headlineBadge": "Nix",
|
||||
"headlineAfter": "\u00a0实践被看见",
|
||||
"lead": "欢迎回来相聚。这是又一次面向中文社区的 Nix / NixOS 线下聚会——更丰富,也更好玩。我们希望把正在使用 Nix、研究 Nix,或刚刚开始了解 Nix 的人聚在一起。让真实的实践、问题与探索,在现场被看见。",
|
||||
"ctaPrimary": "报名参会",
|
||||
"ctaSecondary": "CMS 系统指引"
|
||||
"ctaPrimary": "报名参会\u00a0→",
|
||||
"ctaSecondary": "CMS 系统指引\u00a0👈"
|
||||
}
|
||||
},
|
||||
"eventGuide": {
|
||||
"calendar": {
|
||||
"meta": {
|
||||
"htmlLang": "zh-CN",
|
||||
"title": "活动指南 · NixCN Conference 2605",
|
||||
@@ -38,7 +38,7 @@
|
||||
},
|
||||
"heading": {
|
||||
"title": "活动指南",
|
||||
"sub": "如果你对 Conference 有一点好奇,这里可能会给你一个更具体的答案。"
|
||||
"sub": "如果你对 Nix 有一点好奇,这里可能会给你一个更具体的答案。"
|
||||
},
|
||||
"agenda": {
|
||||
"leadEmoji": "🌟",
|
||||
@@ -56,8 +56,8 @@
|
||||
"timeValue": "2026/06/13 (Sat.) ~ 2026/06/14 (Sun.)",
|
||||
"flowLabel": "当天流程:",
|
||||
"flow": [
|
||||
"签到时间:08:30 – 10:00",
|
||||
"议题分享:10:00 开始",
|
||||
"签到时间:08:30 – 09:30",
|
||||
"议题分享:09:30 开始",
|
||||
"午休时间:12:00 – 14:00",
|
||||
"下午议题:14:00 – 18:00",
|
||||
"活动结束:18:30 前"
|
||||
@@ -77,7 +77,10 @@
|
||||
},
|
||||
"hero": {
|
||||
"title": "CMS 系统指引",
|
||||
"subs": ["通过报名系统完成活动报名、身份认证、议程提交以及入场签到等操作。"]
|
||||
"subs": [
|
||||
"通过报名系统,你可以完成活动报名、身份认证、议程提交,以及后续入场签到等操作。",
|
||||
"首次使用无需提前注册,使用邮箱登录后,系统会自动创建账号。"
|
||||
]
|
||||
},
|
||||
"tipBell": "🔔",
|
||||
"tipLabel": "提示:",
|
||||
@@ -89,7 +92,7 @@
|
||||
"bodyHtml": "进入报名系统后,输入任意可正常接收邮件的邮箱,即可获取登录链接。<br />首次登录时,系统会自动为你创建账号。你只需要根据提示设置用户名和昵称,即可进入系统。",
|
||||
"tip": {
|
||||
"kind": "inline",
|
||||
"text": "请尽量使用常用邮箱登录。后续活动报名、身份认证、议程提交和会务通知,都将与该邮箱关联。"
|
||||
"text": "请尽量使用常用邮箱登录哦。后续活动报名、身份认证、议程提交和会务通知,都将与该邮箱关联。"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -112,7 +115,7 @@
|
||||
"afterListHtml": "完成认证后,即可继续参与活动相关操作。",
|
||||
"tip": {
|
||||
"kind": "inline",
|
||||
"text": "对于未成年参会者,需要额外签署一份监护人知情同意书,请在报名结束前后留意群内通知和参会邮箱获取签署指南。"
|
||||
"text": "需要填写个人有效证件的真实信息哦。"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -146,17 +149,17 @@
|
||||
},
|
||||
"hero": {
|
||||
"title": "纪念品信息",
|
||||
"sub": "你可以在此 DIY 个性化参会证,并提前解锁更多活动限定纪念品信息。"
|
||||
"sub": "打造你的专属参会名片!你可以在此 DIY 个性化参会证,并提前解锁更多活动限定纪念品信息。"
|
||||
},
|
||||
"card": {
|
||||
"title": "打造你的专属参会证 ✨",
|
||||
"body": "提供纪念品的个性化设定服务。点击下方按钮跳转至定制通道,录入你的身份信息和偏好以生成定制化参会证。",
|
||||
"cta": "定制收集表暂未开放",
|
||||
"title": "NixCN Conference 2605 打造你的专属参会证 ✨",
|
||||
"body": "提供纪念品的个性化设定服务。点击下方按钮跳转至外部定制通道,录入你的专属身份参数以生成定制化参会证。",
|
||||
"cta": "开启专属定制",
|
||||
"ctaUrl": "https://forms.office.com/Pages/DesignPageV2.aspx?subpage=design&FormId=DQSIkWdsW0yxEjajBLZtrQAAAAAAAAAAAAZ__u0jrMtUODNTWUpJSEs0STZZWThNTTJVUlNHN01OQy4u&Token=f0e798fadec04532bf2ef8b474a4d7b2"
|
||||
},
|
||||
"comingSoon": {
|
||||
"title": "定制通道暂未开放",
|
||||
"body": "纪念品详细设计与个性化定制功能正在准备中,敬请期待",
|
||||
"body": "纪念品个性化定制功能正在准备中,敬请期待。通道开放后,点击下方按钮即可跳转至外部表单完成定制。",
|
||||
"close": "知道了",
|
||||
"ariaClose": "关闭弹窗"
|
||||
}
|
||||
@@ -164,8 +167,8 @@
|
||||
},
|
||||
"en": {
|
||||
"nav": {
|
||||
"home": "Home",
|
||||
"eventGuide": "Event Guide",
|
||||
"home": "HOME",
|
||||
"calendar": "Event Guide",
|
||||
"cmsGuide": "CMS Guide",
|
||||
"souvenir": "Souvenirs",
|
||||
"ariaLogoHome": "NixCN Home",
|
||||
@@ -188,12 +191,12 @@
|
||||
"headlineBefore": "Let your\u00a0",
|
||||
"headlineBadge": "Nix",
|
||||
"headlineAfter": "practice <br class=\"headline-break\" />be seen",
|
||||
"lead": "Welcome back. This is another in-person Nix / NixOS meetup for the Chinese community — richer, and more fun. We want to bring together veteran Nix users, deep divers, and absolute beginners alike. Let real-world applications, deep questions, and new explorations take center stage.",
|
||||
"lead": "Welcome back to the table. This is another in-person Nix / NixOS meetup for the Chinese community — richer, and more fun. We want to bring together people who are already using Nix, studying Nix, or just starting to learn about Nix. Let real practice, questions and exploration be seen onstage.",
|
||||
"ctaPrimary": "Sign up\u00a0→",
|
||||
"ctaSecondary": "CMS Guide\u00a0👈"
|
||||
}
|
||||
},
|
||||
"eventGuide": {
|
||||
"calendar": {
|
||||
"meta": {
|
||||
"htmlLang": "en",
|
||||
"title": "Event Guide · NixCN Conference 2605",
|
||||
@@ -201,7 +204,7 @@
|
||||
},
|
||||
"heading": {
|
||||
"title": "Event Guide",
|
||||
"sub": "If you're curious about what to expect at the Conference, this guide will give you a clearer picture."
|
||||
"sub": "If you are a little curious about Nix, this page may give you a more concrete answer."
|
||||
},
|
||||
"agenda": {
|
||||
"leadEmoji": "🌟",
|
||||
@@ -209,7 +212,7 @@
|
||||
"bullets": [
|
||||
"Each talk runs for about <strong>30</strong> minutes",
|
||||
"Every talk is followed by about <strong>10</strong> minutes of discussion / Q&A",
|
||||
"A tea break is scheduled around <strong>15:00</strong>"
|
||||
"A tea break is scheduled around <strong>15:00</strong> in the afternoon"
|
||||
]
|
||||
},
|
||||
"schedule": {
|
||||
@@ -217,10 +220,10 @@
|
||||
"title": "Conference schedule",
|
||||
"timeLabel": "Conference dates:",
|
||||
"timeValue": "2026/06/13 (Sat.) ~ 2026/06/14 (Sun.)",
|
||||
"flowLabel": "Daily Schedule:",
|
||||
"flowLabel": "Daily flow:",
|
||||
"flow": [
|
||||
"Check-in: 08:30 – 10:00",
|
||||
"Talks start: 10:00",
|
||||
"Check-in: 08:30 – 09:30",
|
||||
"Talks start: 09:30",
|
||||
"Lunch break: 12:00 – 14:00",
|
||||
"Afternoon talks: 14:00 – 18:00",
|
||||
"Wrap up before: 18:30"
|
||||
@@ -228,7 +231,7 @@
|
||||
},
|
||||
"food": {
|
||||
"emoji": "🍜",
|
||||
"title": "Dining Guide",
|
||||
"title": "Food guide",
|
||||
"mapAlt": "NixCN Conference 2605 nearby food map, with recommended restaurants within a 2–13 minute walk from the venue"
|
||||
}
|
||||
},
|
||||
@@ -252,7 +255,7 @@
|
||||
"id": "step-01",
|
||||
"num": "01",
|
||||
"name": "Log in with your email",
|
||||
"bodyHtml": "Once you open the registration system, enter a valid email address and you will get a login link.<br />On first login the system creates an account for you automatically. Just follow the prompts to set a username and display name, and you are in.",
|
||||
"bodyHtml": "Once you open the registration system, enter any email address that can receive mail and you will get a login link.<br />On first login the system creates an account for you automatically. Just follow the prompts to set a username and display name, and you are in.",
|
||||
"tip": {
|
||||
"kind": "inline",
|
||||
"text": "Please use an email address you check often. Future event sign-ups, identity verification, talk submissions and event notifications will all be tied to this address."
|
||||
@@ -262,7 +265,7 @@
|
||||
"id": "step-02",
|
||||
"num": "02",
|
||||
"name": "Browse upcoming events",
|
||||
"bodyHtml": "Once logged in, you can browse currently open events on the Events page.<br />Select an event to open its detail page, where you can see the date, venue, description, registration status and available actions.<br />Here you can:",
|
||||
"bodyHtml": "Once logged in, you can browse currently open events on the Events page.<br />Select an event to open its detail page, where you can see the date, venue, description, registration status and the action entries you need.<br />Here you can:",
|
||||
"list": ["Join the event", "View event details", "Submit a talk", "View events you have joined"]
|
||||
},
|
||||
{
|
||||
@@ -278,7 +281,7 @@
|
||||
"afterListHtml": "Once verified, you can continue with the rest of the event actions.",
|
||||
"tip": {
|
||||
"kind": "inline",
|
||||
"text": "For underage attendees, a guardian consent form must also be signed. Please watch for group notifications and check your registration email for the signing guide around the time registration closes."
|
||||
"text": "Please fill in real information from a valid personal ID document."
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -292,7 +295,7 @@
|
||||
"num": "05",
|
||||
"name": "Talk review and scheduling",
|
||||
"bodyHtmls": [
|
||||
"Submitted talks are reviewed collectively by the organizers.<br />We curate the program based on topic relevance, content completeness, sharing value and the overall conference plan. Accepted talks then move into the scheduling phase.",
|
||||
"Submitted talks are reviewed by the organizers as a whole.<br />We curate the program based on topic relevance, content completeness, sharing value and the overall conference plan. Accepted talks then move into the scheduling phase.",
|
||||
"The final program is expected to be announced two weeks before the conference.<br />Please refer to the latest information on the website or event page closer to the date."
|
||||
]
|
||||
},
|
||||
@@ -300,7 +303,7 @@
|
||||
"id": "step-06",
|
||||
"num": "06",
|
||||
"name": "Entry code and check-in code",
|
||||
"bodyHtml": "Once the event starts, the system will generate an entry code and a check-in code for every attendee who has joined.<br />These codes are your essential credentials on-site. Once you arrive at the venue, please present them at the respective checkpoints step under staff guidance."
|
||||
"bodyHtml": "Once the event starts, the system will generate an entry code and a check-in code for every attendee who has joined.<br />These codes are your essential credentials on-site. Once you arrive at the venue, please present them at the matching step under staff guidance."
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -312,17 +315,17 @@
|
||||
},
|
||||
"hero": {
|
||||
"title": "Souvenirs",
|
||||
"sub": "Customize your badge here and unlock more exclusive event souvenir details in advance."
|
||||
"sub": "Create your exclusive conference name card! DIY your personalized badge here and unlock more event-limited souvenir information in advance."
|
||||
},
|
||||
"card": {
|
||||
"title": "Create Your Exclusive Badge ✨",
|
||||
"body": "Personalize your souvenirs. Click the button below to open the customization form and enter your details to generate a customized conference badge.",
|
||||
"cta": "Not Open Yet",
|
||||
"title": "NixCN Conference 2605 — Create Your Exclusive Badge ✨",
|
||||
"body": "We offer personalized souvenir settings. Click the button below to open the external customization channel and enter your identity details to generate a customized conference badge.",
|
||||
"cta": "Start Customization",
|
||||
"ctaUrl": "https://forms.office.com/Pages/DesignPageV2.aspx?subpage=design&FormId=DQSIkWdsW0yxEjajBLZtrQAAAAAAAAAAAAZ__u0jrMtUODNTWUpJSEs0STZZWThNTTJVUlNHN01OQy4u&Token=f0e798fadec04532bf2ef8b474a4d7b2"
|
||||
},
|
||||
"comingSoon": {
|
||||
"title": "Customization Not Open Yet",
|
||||
"body": "Souvenir design and personalized customization is still being prepared. Please check back later.",
|
||||
"body": "Personalized souvenir customization is still being prepared. Please check back later — once live, the button below will take you to the external form.",
|
||||
"close": "Got it",
|
||||
"ariaClose": "Close dialog"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
---
|
||||
import '../styles/global.css';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description?: string;
|
||||
@@ -20,6 +18,42 @@ const { title, description, lang } = Astro.props;
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
<div class='page'>
|
||||
<slot />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style is:global>
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family:
|
||||
'PingFang SC',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
sans-serif;
|
||||
background: #ffffff;
|
||||
color: #030f20;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.page {
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
||||
10
src/pages/[lang]/calendar.astro
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
import CalendarPage from '../../components/CalendarPage.astro';
|
||||
import { getLocalePaths, type Locale } from '../../i18n/config';
|
||||
|
||||
export const getStaticPaths = getLocalePaths;
|
||||
|
||||
const { lang } = Astro.params as { lang: Locale };
|
||||
---
|
||||
|
||||
<CalendarPage locale={lang} />
|
||||
10
src/pages/[lang]/cms-guide.astro
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
import CmsGuidePage from '../../components/CmsGuidePage.astro';
|
||||
import { getLocalePaths, type Locale } from '../../i18n/config';
|
||||
|
||||
export const getStaticPaths = getLocalePaths;
|
||||
|
||||
const { lang } = Astro.params as { lang: Locale };
|
||||
---
|
||||
|
||||
<CmsGuidePage locale={lang} />
|
||||
10
src/pages/[lang]/index.astro
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
import HomePage from '../../components/HomePage.astro';
|
||||
import { getLocalePaths, type Locale } from '../../i18n/config';
|
||||
|
||||
export const getStaticPaths = getLocalePaths;
|
||||
|
||||
const { lang } = Astro.params as { lang: Locale };
|
||||
---
|
||||
|
||||
<HomePage locale={lang} />
|
||||
10
src/pages/[lang]/souvenir.astro
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
import SouvenirPage from '../../components/SouvenirPage.astro';
|
||||
import { getLocalePaths, type Locale } from '../../i18n/config';
|
||||
|
||||
export const getStaticPaths = getLocalePaths;
|
||||
|
||||
const { lang } = Astro.params as { lang: Locale };
|
||||
---
|
||||
|
||||
<SouvenirPage locale={lang} />
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
import CmsGuidePage from '../../components/CmsGuidePage.astro';
|
||||
---
|
||||
|
||||
<CmsGuidePage locale='en' />
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
import EventGuidePage from '../../components/EventGuidePage.astro';
|
||||
---
|
||||
|
||||
<EventGuidePage locale='en' />
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
import HomePage from '../../components/HomePage.astro';
|
||||
---
|
||||
|
||||
<HomePage locale='en' />
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
import SouvenirPage from '../../components/SouvenirPage.astro';
|
||||
---
|
||||
|
||||
<SouvenirPage locale='en' />
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
import CmsGuidePage from '../../components/CmsGuidePage.astro';
|
||||
---
|
||||
|
||||
<CmsGuidePage locale='zh-CN' />
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
import EventGuidePage from '../../components/EventGuidePage.astro';
|
||||
---
|
||||
|
||||
<EventGuidePage locale='zh-CN' />
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
import HomePage from '../../components/HomePage.astro';
|
||||
---
|
||||
|
||||
<HomePage locale='zh-CN' />
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
import SouvenirPage from '../../components/SouvenirPage.astro';
|
||||
---
|
||||
|
||||
<SouvenirPage locale='zh-CN' />
|
||||
@@ -1,269 +0,0 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
@theme {
|
||||
/* Brand colors */
|
||||
--color-brand-blue: #5277c3;
|
||||
--color-brand-dark: #030f20;
|
||||
--color-brand-light: #9bcef1;
|
||||
--color-surface: rgba(249, 251, 255, 0.9);
|
||||
--color-border: rgba(155, 206, 241, 0.8);
|
||||
|
||||
/* Typography */
|
||||
--font-sans: 'PingFang SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
|
||||
/* Easings */
|
||||
--ease-hero: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
--ease-modal: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
|
||||
/* Named animations */
|
||||
--animate-hero-fade-up: heroFadeUp 0.55s var(--ease-hero) both;
|
||||
--animate-hero-fade-down: heroFadeDown 0.45s var(--ease-hero) both;
|
||||
--animate-hero-fade-in: heroFadeIn 0.5s var(--ease-hero) both;
|
||||
--animate-hero-fade-in-70: heroFadeInTo70 0.5s var(--ease-hero) both;
|
||||
--animate-modal-in: modalDialogIn 0.35s var(--ease-modal) both;
|
||||
--animate-modal-overlay: modalOverlayIn 0.28s var(--ease-modal) both;
|
||||
}
|
||||
|
||||
@keyframes heroFadeUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(16px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-12px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heroFadeInTo70 {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes modalDialogIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(12px) scale(0.98);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes modalOverlayIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100%;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100%;
|
||||
}
|
||||
body {
|
||||
font-family: var(--font-sans);
|
||||
background: #ffffff;
|
||||
color: var(--color-brand-dark);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 16px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* Navbar: active-link tab underline (SVG, cannot express as utility) */
|
||||
.nav-link.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -18px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 71px;
|
||||
height: 22px;
|
||||
background: url('/images/shared/tab-line.svg') center / contain no-repeat;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Navbar: suppress <details> marker */
|
||||
.hamburger::marker,
|
||||
.hamburger::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Navbar: mobile nav current page */
|
||||
.mobile-nav a[aria-current='page'] {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Navbar: last mobile nav link no bottom border */
|
||||
.mobile-nav a:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* event-guide: bullet dot */
|
||||
.bullet-list li::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
top: 0.7em;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: #33466d;
|
||||
}
|
||||
|
||||
/* event-guide: injected <strong> inside bullet */
|
||||
.bullet-list li strong {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #33466d;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
/* event-guide: smaller strong on mobile */
|
||||
@media (max-width: 639px) {
|
||||
.bullet-list li strong {
|
||||
font-size: 19px;
|
||||
}
|
||||
}
|
||||
|
||||
/* event-guide: timeline marker */
|
||||
.timeline-list li::marker {
|
||||
color: #030f20;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/* Global: injected .accent spans */
|
||||
.accent {
|
||||
color: #5277c3;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* CmsGuide: warning capsule injected via set:html */
|
||||
.hl-warn {
|
||||
display: inline-block;
|
||||
padding: 0 8px;
|
||||
margin: 0 2px;
|
||||
color: #d94f4f;
|
||||
font-weight: 600;
|
||||
border: 1px solid rgba(217, 79, 79, 0.45);
|
||||
border-radius: 8px;
|
||||
background: rgba(217, 79, 79, 0.06);
|
||||
}
|
||||
|
||||
/* CmsGuide: step list bullet color */
|
||||
.step-list li::marker {
|
||||
color: #5277c3;
|
||||
}
|
||||
|
||||
/* CmsGuide: adjacent tip paragraphs */
|
||||
.tip p + p {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* HomePage: gloss pseudo with JS-driven CSS vars */
|
||||
.nix-highlight::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background: radial-gradient(
|
||||
circle at var(--gloss-x) var(--gloss-y),
|
||||
rgba(255, 255, 255, 0.55) 0%,
|
||||
rgba(255, 255, 255, 0.18) 28%,
|
||||
rgba(255, 255, 255, 0) 60%
|
||||
);
|
||||
mix-blend-mode: screen;
|
||||
pointer-events: none;
|
||||
transform: translateZ(1px);
|
||||
}
|
||||
|
||||
/* HomePage: highlight border pseudo */
|
||||
.nix-highlight::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
border: 1px solid rgba(255, 255, 255, 0.45);
|
||||
pointer-events: none;
|
||||
transform: translateZ(1px);
|
||||
}
|
||||
|
||||
/* HomePage: English badge inline-end spacing */
|
||||
.headline--split-mobile .nix-highlight {
|
||||
margin-inline-end: 0.22em;
|
||||
}
|
||||
|
||||
/* HomePage mobile: badge second-line block */
|
||||
@media (max-width: 639px) {
|
||||
.headline--split-mobile .nix-highlight {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
.headline--split-mobile .headline-tail {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.nix-highlight::before,
|
||||
.nix-highlight::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Souvenir: modal overlay state */
|
||||
.coming-soon-overlay[hidden] {
|
||||
display: none;
|
||||
}
|
||||
.coming-soon-overlay.is-open {
|
||||
animation: var(--animate-modal-overlay);
|
||||
}
|
||||
|
||||
/* Souvenir: modal dialog animation (triggered when overlay opens) */
|
||||
.coming-soon-overlay.is-open .coming-soon-dialog {
|
||||
animation: var(--animate-modal-in);
|
||||
animation-delay: 0.04s;
|
||||
}
|
||||
}
|
||||