diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 3424097..8fc622f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -10,6 +10,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -22,9 +23,10 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Environment (please complete the following information):** - - OS: [e.g. macOS, Windows] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] + +- OS: [e.g. macOS, Windows] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f520177..82a4fbd 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -12,6 +12,7 @@ about: Submit a pull request **Related issues:** **Checklist:** + - [ ] Tests added/updated - [ ] Documentation updated - [ ] Linting and formatting pass diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6a7695c..53f34f5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,6 @@ version: 2 updates: - - package-ecosystem: "pip" - directory: "/" - schedule: - interval: "weekly" + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..a2cf881 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,34 @@ +name: Tests + +on: + pull_request: + push: + branches: + - main + +jobs: + playwright: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Install Playwright browsers + run: npx playwright install --with-deps + + - name: Run tests + run: npm test diff --git a/CHANGELOG.md b/CHANGELOG.md index cdb1fc2..766eeb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,12 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### 2026-06-09 + +- **Roadmap PWA features:** Added offline SRS flashcards, streaks and achievements, placement testing, day quizzes, pinyin tone visuals, offline stroke-order playback, progress portability keys, static cache updates, CI, and Playwright coverage for the new retention/self-assessment/learning-depth flows. + ### 2026-05-20 + - **Review & tooling:** Added review UI, starred phrases (`js/starred-phrases.js`), related stylesheet hooks, Playwright smoke tests (`tests/e2e`, `playwright.config.ts`), `package.json` / lockfile tooling, service worker registration path updates, `.gitignore` tweaks, `README`/workspace doc updates, and miscellaneous Flutter manifests/config touch-ups. - **Remove embedded video:** Stripped embedded YouTube usage from the PWA (video loaders, JSON, CSS/JS/HTML sections) and from the Flutter app (webview/youtube_player dependencies, `Lesson.videoId`, plugin registrations). README/Flutter README now describe bundled audio/transcripts instead of embedded streaming; removed obsolete internal strategy/instruction markdown files bundled with that cleanup. - **Hero:** Restored earlier hero layout and background image styling. @@ -15,4 +20,5 @@ All notable changes to this project will be documented in this file. - Updated requirements.txt (see details in commit) ## [1.0.0] - 2025-07-25 + - Initial public release. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 0b08678..94d9f44 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,16 +1,20 @@ # Contributor Covenant Code of Conduct ## Our Pledge + We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards + Examples of behavior that contributes to a positive environment: + - Using welcoming and inclusive language - Being respectful of differing viewpoints - Gracefully accepting constructive criticism - Showing empathy towards others ## Enforcement + Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dbsectrainer@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 579435b..60483e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,6 +3,7 @@ Thank you for your interest in contributing! Please read the following guidelines to help us maintain a healthy and productive project. ## How to Contribute + - Fork the repository and create your branch from `main`. - Write clear, concise commit messages. - Ensure your code follows our style guidelines. @@ -10,14 +11,17 @@ Thank you for your interest in contributing! Please read the following guideline - Submit a pull request with a clear description of your changes. ## Coding Standards + - Use clear variable and function names. - Write docstrings/comments where helpful. - Follow PEP8 for Python code. ## Pull Request Process + - Reference related issues in your PR description. - Ensure all tests pass before requesting review. - Be responsive to feedback and requested changes. ## Code of Conduct + By participating, you agree to abide by our [Code of Conduct](CODE_OF_CONDUCT.md). diff --git a/README.md b/README.md index b4cce5d..9986a6b 100644 --- a/README.md +++ b/README.md @@ -27,16 +27,17 @@ A focused Mandarin Chinese learning platform designed to take learners from foun ### Two Versions Available 1. **Flutter Mobile/Desktop App** (recommended) - Native performance, cross-platform - - Located in: `flutter_app/` - - See: [Flutter App Documentation](flutter_app/README.md) + - Located in: `flutter_app/` + - See: [Flutter App Documentation](flutter_app/README.md) 2. **Progressive Web App (PWA)** - Original web-based version - - Located in: Root directory - - Access via local server: `python server.py` + - Located in: Root directory + - Access via local server: `python server.py` ## Technical Skills Demonstrated ### Web Development + - Interactive, responsive web interface using HTML5, CSS3, and modern JavaScript - Dynamic content updates and micro-interactions for enhanced user engagement - Progressive Web App with offline-ready static assets (first-party lesson audio and transcripts) @@ -45,11 +46,13 @@ A focused Mandarin Chinese learning platform designed to take learners from foun - Interactive reading comprehension exercises with vocabulary tools ### Python Development + - Automated content generation scripts for lesson materials - Efficient audio file processing and generation - Asynchronous programming for optimized performance ### Educational Technology + - Structured 40-day curriculum design with progressive learning paths - Interactive learning tools and progress tracking systems - Multimedia content integration (text, audio, interactive exercises) @@ -57,92 +60,101 @@ A focused Mandarin Chinese learning platform designed to take learners from foun - Reading comprehension exercises with vocabulary support ### Multilingual Support + - Trilingual content management (Simplified Chinese, Pinyin, English) - Dynamic language switching functionality - Cultural context integration ### User Experience Design + - Progress tracking with completion badges - Offline-capable web application - Persistent user preferences and progress storage - Intuitive navigation and learning flow ### Audio Processing + - Dual-language audio content management - Native speaker pronunciation integration - Custom audio playback controls ## Project Structure + - `index.html`: Main dashboard with progress tracking - `day.html`: Daily lesson interface with audio and text content - `supplementary.html`: Additional learning resources and practice materials - `reading.html`: Interactive reading practice with comprehension exercises - `writing.html`: Character writing practice with canvas-based drawing - `css/`: Stylesheets for the web interface - - `styles.css`: Main stylesheet - - `reading.css`: Styles for reading practice interface - - `writing.css`: Styles for writing practice interface + - `styles.css`: Main stylesheet + - `reading.css`: Styles for reading practice interface + - `writing.css`: Styles for writing practice interface - `js/`: JavaScript functionality and interactive features - - `script.js`: Core application functionality - - `character-drawing.js`: Canvas-based drawing system for character practice -- `audio_files/`: - - Daily lesson audio files in both English (`day{n}_en.mp3`) and Mandarin (`day{n}_zh.mp3`) - - Supplementary audio content in the `supplementary/` subdirectory - - Reading practice audio in the `reading/` subdirectory - - Writing practice audio in the `writing/` subdirectory + - `script.js`: Core application functionality + - `character-drawing.js`: Canvas-based drawing system for character practice +- `audio_files/`: + - Daily lesson audio files in both English (`day{n}_en.mp3`) and Mandarin (`day{n}_zh.mp3`) + - Supplementary audio content in the `supplementary/` subdirectory + - Reading practice audio in the `reading/` subdirectory + - Writing practice audio in the `writing/` subdirectory - `text_files/`: Text transcripts for each lesson: - - Simplified Chinese (`day{n}_zh.txt`) - - Pinyin (`day{n}_pinyin.txt`) - - English (`day{n}_en.txt`) + - Simplified Chinese (`day{n}_zh.txt`) + - Pinyin (`day{n}_pinyin.txt`) + - English (`day{n}_en.txt`) - `timing/`: Karaoke-style cue files for synced playback with transcripts: - - Daily lessons: `timing/day{n}_zh.json`, `timing/day{n}_en.json` - - Supplementary categories: `timing/supplementary/{category}_zh.json` (and `_en`) - - Reading passages: `timing/reading/{level}_{topic_slug}_zh.json` (and `_en`) - - Writing activity intros (title + description): `timing/writing/{type}_{level_slug}_zh.json` (and `_en`) - - **Chinese vs Latin UI:** Mandarin timings are generated against Chinese sentences. Reading with `lang=pinyin` loads **`_zh` audio + timings** alongside the romanized transcript (segmented like English), so cues track the same passages as `_zh`; per-token granularity is weaker than hanzi spans. Supplementary behaves the same (`pinyin` text + `_zh.json` cues). Writing with `lang=pinyin` plays the **`_zh`** intro clip (aligned to **`description_zh`**); the romanized heading uses **phrase-level** highlighting on those two cues (no `.lesson-token` children) because syllable timing differs from Mandarin speech. - - **Writing `lang=zh`:** Karaoke and **`_zh`** TTS match the Chinese title plus **`description_zh`** on each block in **`writing_activities.py`**. Mandarin stitching lives in **`scripts/audio_timings.py`**; **`generate_writing_file`** writes **`description_zh`** into zh text files only. **`_en`** intros use English copy. - - Manifests regenerate with their MP3s when you run the Python generators. + - Daily lessons: `timing/day{n}_zh.json`, `timing/day{n}_en.json` + - Supplementary categories: `timing/supplementary/{category}_zh.json` (and `_en`) + - Reading passages: `timing/reading/{level}_{topic_slug}_zh.json` (and `_en`) + - Writing activity intros (title + description): `timing/writing/{type}_{level_slug}_zh.json` (and `_en`) + - **Chinese vs Latin UI:** Mandarin timings are generated against Chinese sentences. Reading with `lang=pinyin` loads **`_zh` audio + timings** alongside the romanized transcript (segmented like English), so cues track the same passages as `_zh`; per-token granularity is weaker than hanzi spans. Supplementary behaves the same (`pinyin` text + `_zh.json` cues). Writing with `lang=pinyin` plays the **`_zh`** intro clip (aligned to **`description_zh`**); the romanized heading uses **phrase-level** highlighting on those two cues (no `.lesson-token` children) because syllable timing differs from Mandarin speech. + - **Writing `lang=zh`:** Karaoke and **`_zh`** TTS match the Chinese title plus **`description_zh`** on each block in **`writing_activities.py`**. Mandarin stitching lives in **`scripts/audio_timings.py`**; **`generate_writing_file`** writes **`description_zh`** into zh text files only. **`_en`** intros use English copy. + - Manifests regenerate with their MP3s when you run the Python generators. - `reading_files/`: Text content for reading practice exercises - `writing_files/`: Character practice content and writing exercises - `manifest.json`: PWA configuration for installable app features - `sw.js`: Service Worker for offline functionality and caching - `icons/`: PWA icons for various device sizes and resolutions - - `icon-72x72.png` to `icon-512x512.png`: Progressive sizes for different devices - - `icon.svg`: Scalable vector icon + - `icon-72x72.png` to `icon-512x512.png`: Progressive sizes for different devices + - `icon.svg`: Scalable vector icon - Python content generation scripts: - - `mandarin_phrases_days_01_07.py`: Days 1-7 content - - `mandarin_phrases_days_08_14.py`: Days 8-14 content - - `mandarin_phrases_days_15_22.py`: Days 15-22 content - - `mandarin_phrases_days_23_30.py`: Days 23-30 content - - `mandarin_phrases_days_31_40.py`: Days 31-40 content - - `mandarin_phrases_supplementary.py`: Additional practice content - - `reading_activities.py`: Reading practice content generator - - `writing_activities.py`: Writing practice content generator - - `video_search.py`: Stub only (embedded YouTube was removed); kept so old references exit gracefully + - `mandarin_phrases_days_01_07.py`: Days 1-7 content + - `mandarin_phrases_days_08_14.py`: Days 8-14 content + - `mandarin_phrases_days_15_22.py`: Days 15-22 content + - `mandarin_phrases_days_23_30.py`: Days 23-30 content + - `mandarin_phrases_days_31_40.py`: Days 31-40 content + - `mandarin_phrases_supplementary.py`: Additional practice content + - `reading_activities.py`: Reading practice content generator + - `writing_activities.py`: Writing practice content generator + - `video_search.py`: Stub only (embedded YouTube was removed); kept so old references exit gracefully ## Course Structure (40 Days) ### Foundations (Days 1–7) + - Pinyin system, tones, and pronunciation - Greetings, numbers, time, and basic Q&A - Introduction to characters ### Essential Daily Phrases (Days 8–14) + - Shopping, transportation, dining, and directions - Basic sentence patterns and grammar - Survival Mandarin for travelers ### Cultural Context & Daily Life (Days 15–22) + - Family, social interactions, and etiquette - Chinese festivals and traditions - Everyday communication at home and in public ### Professional Communication (Days 23–30) + - Workplace vocabulary and business etiquette - Remote work and online meetings - Emails, presentations, and technical phrases ### Advanced Fluency & Real-World Use (Days 31–40) + - Idioms, slang, and formal expressions - Debates, storytelling, and persuasive speech - Practice dialogues and role-play @@ -150,6 +162,7 @@ A focused Mandarin Chinese learning platform designed to take learners from foun ## Features ### Progressive Web App (PWA) + - Install as a standalone app on desktop and mobile devices - Offline access to lessons, audio, and practice materials - Push notifications for daily lesson reminders @@ -160,17 +173,20 @@ A focused Mandarin Chinese learning platform designed to take learners from foun - Works across all modern browsers and devices ### Interactive Learning Interface + - Dual audio tracks (English explanation + native Mandarin pronunciation) - Interactive transcripts (Simplified, Pinyin, English) - Daily progress tracker with lesson completion badges - Mobile-friendly, offline-capable web interface ### Mandarin-Specific Tools + - Tone practice and audio drills - Pinyin-to-Hanzi recognition games - Cultural tips embedded in lessons ### Reading Practice + - Leveled reading materials (Beginner, Intermediate, Advanced) - Interactive vocabulary lists with pronunciation - Comprehension questions with self-assessment @@ -178,50 +194,56 @@ A focused Mandarin Chinese learning platform designed to take learners from foun - Multi-language support (Simplified Chinese, Pinyin, English) ### Character Writing Practice + - Canvas-based drawing system for authentic character practice - Works with both mouse and touch screen devices - Grid system for proper character proportions - Stroke order guidance and hints - Multiple difficulty levels: - - Basic strokes and radicals - - HSK-leveled characters (HSK 1-6) - - Thematic character groups (Family, Food, Travel, etc.) + - Basic strokes and radicals + - HSK-leveled characters (HSK 1-6) + - Thematic character groups (Family, Food, Travel, etc.) - Clear, Undo, and Hint functionality for learning support ## Why Mandarin? ### 1. Global Importance + - Spoken by over 1 billion people - Key to accessing China's economic, cultural, and technological landscape ### 2. Career & Business Edge + - High demand in diplomacy, tech, import/export, and finance - Opens doors in international business, academia, and NGOs ### 3. Cultural & Intellectual Access + - Dive into Chinese philosophy, literature, and modern media - Enhanced understanding of cross-cultural dynamics ## Development Setup ### Requirements + - Python 3.12+ -- **ffmpeg** on your PATH (required by ``pydub`` when stitching per-phrase TTS into `audio_files/day{n}_*.mp3` and writing `timing/day{n}_*.json`) +- **ffmpeg** on your PATH (required by `pydub` when stitching per-phrase TTS into `audio_files/day{n}_*.mp3` and writing `timing/day{n}_*.json`) - Required Python packages: - ```bash - pip install gtts edge-tts pandas pydub - ``` - Or install from the pinned list: + ```bash + pip install gtts edge-tts pandas pydub + ``` - ```bash - pip install -r requirements.txt - ``` + Or install from the pinned list: - Regenerating Mandarin lesson audio builds **one stitched MP3 per day/voice plus** cue files under `timing/` so the web day lesson can sync highlights with playback. + ```bash + pip install -r requirements.txt + ``` + Regenerating Mandarin lesson audio builds **one stitched MP3 per day/voice plus** cue files under `timing/` so the web day lesson can sync highlights with playback. ### Generate Lessons + ```bash # Generate content for each section python mandarin_phrases_days_01_07.py @@ -235,14 +257,18 @@ python mandarin_phrases_supplementary.py ``` ### Run the Site + For basic usage and PWA features: + ```bash # Using Python's built-in server python -m http.server 8000 ``` + Then open `http://localhost:8000` in your browser. Note: Running through a local server is required to enable PWA features: + 1. Use Chrome or another modern browser that supports PWAs 2. Look for the install prompt in the address bar to install as a standalone app 3. Test offline functionality by disabling network in DevTools @@ -250,7 +276,9 @@ Note: Running through a local server is required to enable PWA features: 5. Clear site data in browser settings to test the service worker update process ## Project Standards & Best Practices + This project follows industry best practices for open source repositories: + - [x] MIT License ([LICENSE](LICENSE)) - [x] Contribution guidelines ([CONTRIBUTING.md](CONTRIBUTING.md)) - [x] Code of Conduct ([CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)) @@ -261,6 +289,7 @@ This project follows industry best practices for open source repositories: - [x] Funding options ([.github/FUNDING.yml](.github/FUNDING.yml)) ## Usage Guide + 1. Open `index.html` and select your lesson 2. Listen to both English explanation and native Mandarin pronunciation 3. Read along with Pinyin and Hanzi @@ -270,7 +299,9 @@ This project follows industry best practices for open source repositories: 7. Complete your daily badge and track your fluency gains ## Storage + Uses localStorage to save: + - Completed lessons - Last visited day - Audio playback preferences (e.g. slow speed, loop) diff --git a/SPONSORSHIP.md b/SPONSORSHIP.md index 1fd1990..5af9732 100644 --- a/SPONSORSHIP.md +++ b/SPONSORSHIP.md @@ -7,32 +7,32 @@ GitHub Sponsors is a funding platform that enables the open source community to ## How GitHub Sponsors Works 1. **For Sponsors (Supporters)** - - Can make monthly recurring payments to support developers - - Get special access to sponsor-only content, updates, or features - - Receive recognition through sponsor badges on GitHub - - Can choose different sponsorship tiers with varying benefits - - 100% of sponsorships go to developers (GitHub charges no fees) + - Can make monthly recurring payments to support developers + - Get special access to sponsor-only content, updates, or features + - Receive recognition through sponsor badges on GitHub + - Can choose different sponsorship tiers with varying benefits + - 100% of sponsorships go to developers (GitHub charges no fees) 2. **For Recipients (Developers/Organizations)** - - Receive monthly funding from sponsors - - Can offer different sponsorship tiers with unique benefits - - Get matched funding from GitHub (up to $5,000 in first year) - - Can engage with sponsors through exclusive updates and content + - Receive monthly funding from sponsors + - Can offer different sponsorship tiers with unique benefits + - Get matched funding from GitHub (up to $5,000 in first year) + - Can engage with sponsors through exclusive updates and content ## Setting Up GitHub Sponsors 1. **Eligibility Requirements** - - Have a GitHub account - - Have 2FA enabled on your GitHub account - - Have a bank account in a supported region - - Follow GitHub Community Guidelines - - Have a complete GitHub profile + - Have a GitHub account + - Have 2FA enabled on your GitHub account + - Have a bank account in a supported region + - Follow GitHub Community Guidelines + - Have a complete GitHub profile 2. **Application Process** - - Go to [GitHub Sponsors](https://github.com/sponsors) - - Click "Join the waitlist" or "Set up GitHub Sponsors" - - Complete the application form - - Wait for approval (typically 1-2 weeks) + - Go to [GitHub Sponsors](https://github.com/sponsors) + - Click "Join the waitlist" or "Set up GitHub Sponsors" + - Complete the application form + - Wait for approval (typically 1-2 weeks) ## Mandarin Pathways Sponsorship Profile @@ -63,29 +63,34 @@ Join us in making Mandarin learning accessible to everyone, everywhere! ## Sponsorship Tiers ### 1. 学生 Student Supporter ($5/month) + - ✨ Sponsor badge on GitHub - 🎯 Name in supporters.md - 📝 Access to sponsor-only updates ### 2. 朋友 Friend ($10/month) + - All Student benefits, plus: - 🎵 Early access to new audio content - 📚 Vote on new lesson topics - 💬 Priority issue responses ### 3. 老师 Teacher ($25/month) + - All Friend benefits, plus: - 🎥 Behind-the-scenes development updates - 🔍 Monthly progress reports - 💡 Suggest new features ### 4. 大师 Master ($50/month) + - All Teacher benefits, plus: - 👥 1-on-1 monthly consultation - 🌟 Featured sponsor status - 🎨 Input on design decisions ### 5. 企业 Enterprise ($100/month) + - All Master benefits, plus: - 💼 Custom deployment support - 🏢 Organization logo on README @@ -94,23 +99,23 @@ Join us in making Mandarin learning accessible to everyone, everywhere! ## Funding Goals 1. **$200/month** - - Regular content updates - - Basic maintenance + - Regular content updates + - Basic maintenance 2. **$500/month** - - New audio recordings - - Enhanced interactive features - - Weekly updates + - New audio recordings + - Enhanced interactive features + - Weekly updates 3. **$1000/month** - - Professional voice actors - - Mobile app development - - Advanced learning tools + - Professional voice actors + - Mobile app development + - Advanced learning tools 4. **$2000/month** - - Full-time development - - AI-powered features - - Expanded curriculum + - Full-time development + - AI-powered features + - Expanded curriculum ## How Funds Are Used @@ -123,19 +128,19 @@ Join us in making Mandarin learning accessible to everyone, everywhere! ## Engagement Strategy 1. **Regular Updates** - - Weekly development logs - - Monthly progress reports - - Quarterly roadmap updates + - Weekly development logs + - Monthly progress reports + - Quarterly roadmap updates 2. **Community Involvement** - - Sponsor-only polls - - Feature voting - - Development discussions + - Sponsor-only polls + - Feature voting + - Development discussions 3. **Recognition** - - Sponsor wall in README - - Social media shoutouts - - Contribution acknowledgments + - Sponsor wall in README + - Social media shoutouts + - Contribution acknowledgments ## Getting Started @@ -148,23 +153,23 @@ Join us in making Mandarin learning accessible to everyone, everywhere! ## Best Practices 1. **Communication** - - Respond promptly to sponsor messages - - Post regular updates - - Be transparent about development + - Respond promptly to sponsor messages + - Post regular updates + - Be transparent about development 2. **Content Management** - - Keep sponsor-only content updated - - Document sponsor benefits clearly - - Maintain an organized repository + - Keep sponsor-only content updated + - Document sponsor benefits clearly + - Maintain an organized repository 3. **Community Building** - - Engage with sponsors regularly - - Host sponsor-only events - - Recognize contributor milestones + - Engage with sponsors regularly + - Host sponsor-only events + - Recognize contributor milestones 4. **Project Management** - - Track sponsor-suggested features - - Maintain a public roadmap - - Document progress regularly + - Track sponsor-suggested features + - Maintain a public roadmap + - Document progress regularly Remember to keep your sponsorship tiers, goals, and benefits updated as your project grows and evolves. Regular engagement with sponsors and transparent communication about how funds are used will help build a sustainable sponsorship program. diff --git a/css/reading.css b/css/reading.css index 37ac306..39e1635 100644 --- a/css/reading.css +++ b/css/reading.css @@ -190,9 +190,9 @@ .vocabulary-list { grid-template-columns: 1fr; } - + .level-buttons, .topic-buttons { flex-direction: column; } -} \ No newline at end of file +} diff --git a/css/styles.css b/css/styles.css index 1aba686..3cfb6e7 100644 --- a/css/styles.css +++ b/css/styles.css @@ -1,11 +1,14 @@ /* Preload fonts to prevent layout shifts */ @font-face { - font-family: 'Noto Sans SC'; + font-family: "Noto Sans SC"; font-style: normal; font-weight: 400; font-display: swap; - src: local('Noto Sans SC Regular'), local('NotoSansSC-Regular'), - url('https://fonts.gstatic.com/s/notosanssc/v12/k3kXo84MPvpLmixcA63oeALhLOCT-xWNm8Hqd37g1OkDRZe7lR4sg1IzSy-MNbE9VH8V.119.woff2') format('woff2'); + src: + local("Noto Sans SC Regular"), + local("NotoSansSC-Regular"), + url("https://fonts.gstatic.com/s/notosanssc/v12/k3kXo84MPvpLmixcA63oeALhLOCT-xWNm8Hqd37g1OkDRZe7lR4sg1IzSy-MNbE9VH8V.119.woff2") + format("woff2"); unicode-range: U+4E00-9FFF; } @@ -21,7 +24,7 @@ } body { - font-family: 'Poppins', 'Noto Sans SC', Arial, sans-serif; + font-family: "Poppins", "Noto Sans SC", Arial, sans-serif; margin: 0; padding: 0; background-color: var(--secondary-color); @@ -35,7 +38,7 @@ header { color: white; padding: 2em 0; text-align: center; - box-shadow: 0 4px 6px rgba(0,0,0,0.1); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } header h1 { @@ -52,7 +55,8 @@ header p { } .hero-section { - background: url('https://images.unsplash.com/photo-1546410531-89436e5b419a?auto=format&fit=crop&w=1920&q=80') center/cover; + background: url("https://images.unsplash.com/photo-1546410531-89436e5b419a?auto=format&fit=crop&w=1920&q=80") + center/cover; padding: 100px 20px; text-align: center; color: white; @@ -60,13 +64,18 @@ header p { } .hero-section::before { - content: ''; + content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0; - background: rgba(44, 62, 80, 0.7); /* Reduced opacity for better visibility */ + background: rgba( + 44, + 62, + 80, + 0.7 + ); /* Reduced opacity for better visibility */ } .hero-content { @@ -97,7 +106,11 @@ main { position: absolute; width: 200px; height: 200px; - background-image: radial-gradient(circle, var(--accent-color) 1px, transparent 1px); + background-image: radial-gradient( + circle, + var(--accent-color) 1px, + transparent 1px + ); background-size: 20px 20px; opacity: 0.1; z-index: -1; @@ -117,15 +130,17 @@ main { background: white; border-radius: 15px; overflow: hidden; - box-shadow: 0 10px 20px rgba(0,0,0,0.1); - transition: transform 0.3s ease, box-shadow 0.3s ease; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); + transition: + transform 0.3s ease, + box-shadow 0.3s ease; cursor: pointer; position: relative; } .language-card:hover { transform: translateY(-10px); - box-shadow: 0 15px 30px rgba(0,0,0,0.15); + box-shadow: 0 15px 30px rgba(0, 0, 0, 0.15); } .language-card::after { @@ -140,7 +155,9 @@ main { border-radius: 6px; font-size: 0.85em; opacity: 0; - transition: opacity 0.3s ease, bottom 0.3s ease; + transition: + opacity 0.3s ease, + bottom 0.3s ease; pointer-events: none; white-space: nowrap; z-index: 10; @@ -153,7 +170,11 @@ main { .card-header { padding: 20px; - background: linear-gradient(135deg, var(--accent-color), var(--hover-color)); + background: linear-gradient( + 135deg, + var(--accent-color), + var(--hover-color) + ); color: white; } @@ -198,6 +219,80 @@ main { margin-top: 20px; } +.learning-dashboard { + padding: 0 20px 30px; +} + +.dashboard-panel { + background: white; + border: 1px solid #e6e9ec; + border-radius: 8px; + padding: 24px; +} + +.dashboard-panel h2 { + margin-top: 0; +} + +.dashboard-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 16px; + margin: 18px 0; +} + +.dashboard-stat { + border: 1px solid #e6e9ec; + border-radius: 8px; + padding: 16px; + background: #fbfcfd; +} + +.dashboard-number { + display: block; + color: var(--accent-color); + font-size: 2rem; + font-weight: 700; +} + +.dashboard-label { + display: block; + font-size: 0.9rem; + color: #4d5b66; +} + +.achievement-list { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); + gap: 10px; + list-style: none; + padding: 0; + margin: 18px 0 0; +} + +.achievement-badge { + border: 1px solid #dce2e7; + border-radius: 8px; + padding: 10px; + background: #f5f7f8; + color: #697782; +} + +.achievement-badge.earned { + border-color: rgba(30, 132, 73, 0.35); + background: rgba(30, 132, 73, 0.08); + color: var(--text-color); +} + +.achievement-badge strong, +.achievement-badge span { + display: block; +} + +.achievement-badge span { + font-size: 0.85rem; +} + .stat-item { text-align: center; padding: 10px; @@ -219,7 +314,12 @@ main { /* Section dividers */ .section-divider { height: 3px; - background: linear-gradient(90deg, transparent, var(--accent-color), transparent); + background: linear-gradient( + 90deg, + transparent, + var(--accent-color), + transparent + ); margin: 20px auto 40px; width: 80%; max-width: 800px; @@ -228,7 +328,7 @@ main { } .section-divider::before { - content: ''; + content: ""; position: absolute; width: 30px; height: 30px; @@ -265,14 +365,16 @@ main { background: white; padding: 25px; border-radius: 12px; - box-shadow: 0 4px 6px rgba(0,0,0,0.1); - transition: transform 0.3s ease, box-shadow 0.3s ease; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + transition: + transform 0.3s ease, + box-shadow 0.3s ease; border-left: 4px solid var(--accent-color); } .section-card:hover { transform: translateY(-5px); - box-shadow: 0 8px 15px rgba(0,0,0,0.15); + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.15); } .section-card h3 { @@ -344,7 +446,7 @@ main { border-radius: 20px; font-weight: 600; color: var(--primary-color); - box-shadow: 0 2px 4px rgba(0,0,0,0.1); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .day-grid { @@ -364,14 +466,14 @@ main { color: var(--text-color); text-decoration: none; border-radius: 12px; - box-shadow: 0 4px 6px rgba(0,0,0,0.1); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; position: relative; } .day-grid a:hover { transform: translateY(-5px); - box-shadow: 0 8px 15px rgba(0,0,0,0.15); + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.15); background: var(--accent-color); color: white; } @@ -416,7 +518,7 @@ main { justify-content: center; align-items: center; cursor: pointer; - box-shadow: 0 4px 10px rgba(0,0,0,0.2); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); opacity: 0; visibility: hidden; transition: all 0.3s ease; @@ -517,8 +619,10 @@ footer { background: white; padding: 30px; border-radius: 15px; - box-shadow: 0 6px 12px rgba(0,0,0,0.15); - transition: transform 0.3s ease, box-shadow 0.3s ease; + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); + transition: + transform 0.3s ease, + box-shadow 0.3s ease; text-decoration: none; color: var(--text-color); position: relative; @@ -530,7 +634,7 @@ footer { .core-skill-card:hover { transform: translateY(-10px); - box-shadow: 0 12px 24px rgba(0,0,0,0.2); + box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2); } .core-skill-card .card-icon { @@ -566,11 +670,15 @@ footer { } .core-skill-card::before { - content: ''; + content: ""; position: absolute; width: 250px; height: 250px; - background-image: radial-gradient(circle, var(--accent-color) 1px, transparent 1px); + background-image: radial-gradient( + circle, + var(--accent-color) 1px, + transparent 1px + ); background-size: 20px 20px; opacity: 0.1; z-index: 0; @@ -583,17 +691,17 @@ footer { .core-skills-grid { grid-template-columns: 1fr; } - + .core-skill-card { padding: 25px; } - + .core-skill-card .card-icon { width: 60px; height: 60px; font-size: 1.5em; } - + .core-skill-card h3 { font-size: 1.3em; } @@ -624,7 +732,7 @@ footer { background: white; padding: 25px; border-radius: 12px; - box-shadow: 0 4px 6px rgba(0,0,0,0.1); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); text-decoration: none; color: var(--text-color); position: relative; @@ -642,7 +750,7 @@ footer { /* Chinese text styling */ .zh { - font-family: 'Noto Sans SC', sans-serif; + font-family: "Noto Sans SC", sans-serif; } /* Language selector styles */ @@ -671,7 +779,7 @@ footer { .language-btn.active { background: rgba(255, 255, 255, 0.3); transform: translateY(-2px); - box-shadow: 0 2px 4px rgba(0,0,0,0.2); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); font-weight: 600; } @@ -681,7 +789,7 @@ footer { flex-wrap: wrap; gap: 5px; } - + .language-btn { padding: 6px 10px; font-size: 0.85em; @@ -689,11 +797,15 @@ footer { } .supplementary-card::before { - content: ''; + content: ""; position: absolute; width: 200px; height: 200px; - background-image: radial-gradient(circle, var(--accent-color) 1px, transparent 1px); + background-image: radial-gradient( + circle, + var(--accent-color) 1px, + transparent 1px + ); background-size: 20px 20px; opacity: 0.1; z-index: 0; @@ -704,7 +816,7 @@ footer { .supplementary-card:hover { transform: translateY(-10px); - box-shadow: 0 8px 15px rgba(0,0,0,0.15); + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.15); } .card-icon { @@ -754,14 +866,19 @@ footer { left: 50%; height: 100%; width: 4px; - background: linear-gradient(to bottom, transparent, var(--accent-color), transparent); + background: linear-gradient( + to bottom, + transparent, + var(--accent-color), + transparent + ); opacity: 0.2; z-index: 0; } .journey-path::before, .journey-path::after { - content: ''; + content: ""; position: absolute; width: 20px; height: 20px; @@ -798,13 +915,13 @@ footer { background: white; padding: 25px; border-radius: 12px; - box-shadow: 0 4px 6px rgba(0,0,0,0.1); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); transition: transform 0.3s ease; } .benefit-card:hover { transform: translateY(-5px); - box-shadow: 0 8px 15px rgba(0,0,0,0.15); + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.15); } .benefit-card h3 { @@ -869,13 +986,13 @@ footer { .benefits-grid { grid-template-columns: 1fr; } - + .combo-row { flex-direction: column; align-items: flex-start; gap: 5px; } - + .benefit { text-align: left; } @@ -905,30 +1022,60 @@ footer { /* Animations */ @keyframes fadeIn { - from { opacity: 0; transform: translateY(20px); } - to { opacity: 1; transform: translateY(0); } + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } } @keyframes pulse { - 0% { transform: scale(1); } - 50% { transform: scale(1.05); } - 100% { transform: scale(1); } + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.05); + } + 100% { + transform: scale(1); + } } @keyframes float { - 0% { transform: translateY(0px); } - 50% { transform: translateY(-10px); } - 100% { transform: translateY(0px); } + 0% { + transform: translateY(0px); + } + 50% { + transform: translateY(-10px); + } + 100% { + transform: translateY(0px); + } } @keyframes slideInRight { - from { opacity: 0; transform: translateX(50px); } - to { opacity: 1; transform: translateX(0); } + from { + opacity: 0; + transform: translateX(50px); + } + to { + opacity: 1; + transform: translateX(0); + } } @keyframes slideInLeft { - from { opacity: 0; transform: translateX(-50px); } - to { opacity: 1; transform: translateX(0); } + from { + opacity: 0; + transform: translateX(-50px); + } + to { + opacity: 1; + transform: translateX(0); + } } .animate-fade-in { @@ -966,7 +1113,7 @@ footer { background: white; border-radius: 15px; padding: 30px; - box-shadow: 0 4px 6px rgba(0,0,0,0.1); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); margin: 20px 0; position: relative; animation: fadeIn 0.8s ease forwards; @@ -1061,11 +1208,15 @@ footer { } .phrase-section::before { - content: ''; + content: ""; position: absolute; width: 200px; height: 200px; - background-image: radial-gradient(circle, var(--accent-color) 1px, transparent 1px); + background-image: radial-gradient( + circle, + var(--accent-color) 1px, + transparent 1px + ); background-size: 20px 20px; opacity: 0.1; z-index: 0; @@ -1101,7 +1252,7 @@ footer { align-items: center; gap: 8px; transition: all 0.3s ease; - box-shadow: 0 2px 4px rgba(0,0,0,0.05); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } .phrase-reading { @@ -1111,12 +1262,16 @@ footer { } .phrase-item.audio-sync-active { - box-shadow: 0 0 0 2px rgba(26, 92, 143, 0.35), 0 4px 8px rgba(0,0,0,0.1); + box-shadow: + 0 0 0 2px rgba(26, 92, 143, 0.35), + 0 4px 8px rgba(0, 0, 0, 0.1); background-color: #f9fcff; } .lesson-token { - transition: background-color 0.12s ease, color 0.12s ease; + transition: + background-color 0.12s ease, + color 0.12s ease; } .lesson-token.audio-sync-reading { @@ -1151,7 +1306,7 @@ footer { .phrase-item:hover { transform: translateX(5px); - box-shadow: 0 4px 8px rgba(0,0,0,0.1); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } .copy-btn { @@ -1231,6 +1386,77 @@ footer { color: inherit; font-weight: 600; text-decoration: underline; + margin: 0 0.45rem; +} + +.srs-card { + background: #fff; + border: 1px solid #e0e5e9; + border-radius: 8px; + padding: 1.25rem; + margin: 1rem 0; +} + +.srs-front { + font-size: 1.8rem; + margin: 0.5rem 0; +} + +.srs-back { + border-top: 1px solid #e0e5e9; + color: #45525d; + margin: 1rem 0; + padding-top: 1rem; +} + +.practice-summary { + background: var(--secondary-color); + border-radius: 8px; + padding: 0.75rem 1rem; +} + +.streak-banner h3 { + display: flex; + flex-wrap: wrap; + gap: 0.35rem; + align-items: baseline; +} + +.tone-glyph-list { + display: inline-flex; + gap: 0.2rem; + margin-left: 0.4rem; + vertical-align: middle; +} + +.tone-glyph { + width: 26px; + height: 18px; +} + +.tone-explainer { + border-left: 4px solid var(--accent-color); +} + +.stroke-order-panel { + align-items: center; + background: #fbfcfd; + border: 1px solid #e0e5e9; + border-radius: 8px; + display: flex; + flex-direction: column; + gap: 0.75rem; + margin: 0 auto 1rem; + padding: 1rem; + width: min(100%, 260px); +} + +.stroke-order-writer { + min-height: 180px; +} + +.hanzi-writer-lite { + display: block; } .navigation { @@ -1297,11 +1523,15 @@ footer { } .section-info::before { - content: ''; + content: ""; position: absolute; width: 200px; height: 200px; - background-image: radial-gradient(circle, var(--accent-color) 1px, transparent 1px); + background-image: radial-gradient( + circle, + var(--accent-color) 1px, + transparent 1px + ); background-size: 20px 20px; opacity: 0.1; z-index: 0; @@ -1326,7 +1556,10 @@ footer { .lesson-actions { display: flex; + flex-wrap: wrap; + gap: 0.75rem; justify-content: center; + align-items: center; margin: 30px 0; animation: fadeIn 0.8s ease forwards; } @@ -1347,7 +1580,7 @@ footer { .complete-btn:hover { transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(0,0,0,0.1); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } .complete-btn.completed { @@ -1363,24 +1596,36 @@ footer { color: white; padding: 10px 20px; border-radius: 8px; - box-shadow: 0 2px 4px rgba(0,0,0,0.2); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); display: none; animation: fadeInOut 2s ease; z-index: 1000; } @keyframes fadeInOut { - 0% { opacity: 0; transform: translateY(20px); } - 20% { opacity: 1; transform: translateY(0); } - 80% { opacity: 1; transform: translateY(0); } - 100% { opacity: 0; transform: translateY(-20px); } + 0% { + opacity: 0; + transform: translateY(20px); + } + 20% { + opacity: 1; + transform: translateY(0); + } + 80% { + opacity: 1; + transform: translateY(0); + } + 100% { + opacity: 0; + transform: translateY(-20px); + } } @media (max-width: 768px) { .language-selector { gap: 5px; } - + .language-btn { padding: 6px 10px; font-size: 0.9em; @@ -1403,7 +1648,8 @@ footer { align-items: stretch; } - .nav-btn, .home-btn { + .nav-btn, + .home-btn { width: 100%; justify-content: center; padding: 12px; @@ -1417,7 +1663,7 @@ footer { justify-content: flex-start; gap: 8px; } - + .language-btn { padding: 5px 8px; font-size: 0.8em; @@ -1431,8 +1677,99 @@ footer { width: 40px; height: 40px; } - + body { padding-bottom: 0; /* Remove padding since footer is no longer fixed */ } } + +/* Modals */ +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.modal-content { + background: white; + padding: 2rem; + border-radius: 8px; + max-width: 400px; + width: 90%; +} + +.settings-form { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.button-group { + display: flex; + gap: 1rem; + justify-content: flex-end; + margin-top: 1rem; +} + +.primary-btn { + background: var(--primary-color); + color: white; + border: none; + padding: 0.5rem 1rem; + border-radius: 4px; + cursor: pointer; + font-family: inherit; + font-size: inherit; +} + +/* Quiz day selector */ +.level-selector { + margin: 1.5rem 0; +} + +.level-selector h3 { + font-size: 1rem; + margin-bottom: 0.75rem; + color: var(--text-color); +} + +.level-buttons { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.level-btn { + padding: 0.4rem 0.9rem; + border-radius: 20px; + border: 2px solid var(--accent-color); + background: transparent; + color: var(--accent-color); + text-decoration: none; + font-size: 0.875rem; + font-family: inherit; + transition: + background 0.2s, + color 0.2s; + display: inline-block; + cursor: pointer; +} + +.level-btn.active, +.level-btn:hover { + background: var(--accent-color); + color: white; +} diff --git a/css/styles.min.css b/css/styles.min.css index dedadd5..692705d 100644 --- a/css/styles.min.css +++ b/css/styles.min.css @@ -1 +1,1519 @@ -@font-face{font-family:'Noto Sans SC';font-style:normal;font-weight:400;font-display:swap;src:local('Noto Sans SC Regular'),local('NotoSansSC-Regular'),url('https://fonts.gstatic.com/s/notosanssc/v12/k3kXo84MPvpLmixcA63oeALhLOCT-xWNm8Hqd37g1OkDRZe7lR4sg1IzSy-MNbE9VH8V.119.woff2') format('woff2');unicode-range:U+4E00-9FFF}:root{--primary-color:#1a2733;--secondary-color:#f8f9fa;--accent-color:#1a5c8f;--text-color:#1a2733;--hover-color:#134166;--success-color:#1e8449;--warning-color:#996600;--danger-color:#c0392b}body{font-family:Poppins,'Noto Sans SC',Arial,sans-serif;margin:0;padding:0;background-color:var(--secondary-color);color:var(--text-color);line-height:1.6;padding-bottom:60px}header{background:linear-gradient(135deg,var(--primary-color),#34495e);color:#fff;padding:2em 0;text-align:center;box-shadow:0 4px 6px rgba(0,0,0,.1)}header h1{margin:0;font-size:2.5em;font-weight:700;letter-spacing:1px}header p{margin:10px 0 0;font-size:1.2em;opacity:.9}.hero-section{background:url('https://images.unsplash.com/photo-1546410531-89436e5b419a?auto=format&fit=crop&w=1920&q=80') center/cover;padding:100px 20px;text-align:center;color:#fff;position:relative}.hero-section::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:rgba(44,62,80,.7)}.hero-content{position:relative;max-width:800px;margin:0 auto}.hero-content h2{font-size:2.8em;margin-bottom:20px;font-weight:700}.hero-content p{font-size:1.2em;margin-bottom:30px}main{padding:20px;max-width:1200px;margin:0 auto;position:relative}.visual-anchor{position:absolute;width:200px;height:200px;background-image:radial-gradient(circle,var(--accent-color) 1px,transparent 1px);background-size:20px 20px;opacity:.1;z-index:-1;border-radius:50%}.language-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:30px;padding:40px 20px;max-width:1200px;margin:0 auto}.language-card{background:#fff;border-radius:15px;overflow:hidden;box-shadow:0 10px 20px rgba(0,0,0,.1);transition:transform .3s ease,box-shadow .3s ease;cursor:pointer;position:relative}.language-card:hover{transform:translateY(-10px);box-shadow:0 15px 30px rgba(0,0,0,.15)}.language-card::after{content:attr(title);position:absolute;top:-40px;left:50%;transform:translateX(-50%);background:rgba(44,62,80,.9);color:#fff;padding:8px 12px;border-radius:6px;font-size:.85em;opacity:0;transition:opacity .3s ease,bottom .3s ease;pointer-events:none;white-space:nowrap;z-index:10}.language-card:hover::after{opacity:1;top:-50px}.card-header{padding:20px;background:linear-gradient(135deg,var(--accent-color),var(--hover-color));color:#fff}.card-header h3{margin:0;font-size:1.5em;display:flex;align-items:center;gap:10px}.flag{font-size:1.2em}.card-content{padding:20px}.progress-container{margin:15px 0}.progress-bar{height:8px;background:#ecf0f1;border-radius:4px;overflow:hidden}.progress-fill{height:100%;background:var(--success-color);width:0%;transition:width 1s ease}.stats{display:grid;grid-template-columns:repeat(2,1fr);gap:15px;margin-top:20px}.stat-item{text-align:center;padding:10px;background:var(--secondary-color);border-radius:8px}.stat-number{font-size:1.5em;font-weight:700;color:var(--accent-color)}.stat-label{font-size:.9em;color:#444}.section-divider{height:3px;background:linear-gradient(90deg,transparent,var(--accent-color),transparent);margin:20px auto 40px;width:80%;max-width:800px;opacity:.5;position:relative}.section-divider::before{content:'';position:absolute;width:30px;height:30px;background:#fff;border:3px solid var(--accent-color);border-radius:50%;top:50%;left:50%;transform:translate(-50%,-50%);opacity:.8}.course-overview{padding:40px 0;position:relative;overflow:hidden}.course-overview h2{text-align:center;margin-bottom:30px;color:var(--primary-color);font-size:2em}.course-sections{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:20px;padding:0 20px}.section-card{background:#fff;padding:25px;border-radius:12px;box-shadow:0 4px 6px rgba(0,0,0,.1);transition:transform .3s ease,box-shadow .3s ease;border-left:4px solid var(--accent-color)}.section-card:hover{transform:translateY(-5px);box-shadow:0 8px 15px rgba(0,0,0,.15)}.section-card h3{color:var(--accent-color);margin:0 0 15px 0;font-size:1.3em;display:flex;align-items:center;gap:10px}.section-card h3 i{font-size:1.2em;opacity:.9}.section-card p{margin:0;color:#444;font-size:.95em;line-height:1.5}#day-selection{margin:40px 0;position:relative;overflow:hidden}#day-selection h2{text-align:center;margin-bottom:30px;color:var(--primary-color);font-size:2em}.day-controls{display:flex;justify-content:center;margin-bottom:20px;gap:10px}.day-controls button{background:var(--accent-color);color:#fff;border:none;padding:8px 15px;border-radius:20px;cursor:pointer;font-weight:500;transition:all .3s ease;display:flex;align-items:center;gap:5px}.day-controls button:hover{background:var(--hover-color);transform:translateY(-2px)}.day-controls .day-range{display:flex;align-items:center;background:#fff;padding:8px 15px;border-radius:20px;font-weight:600;color:var(--primary-color);box-shadow:0 2px 4px rgba(0,0,0,.1)}.day-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));gap:15px;padding:20px}.day-grid a{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px 15px;background:#fff;color:var(--text-color);text-decoration:none;border-radius:12px;box-shadow:0 4px 6px rgba(0,0,0,.1);transition:all .3s ease;position:relative}.day-grid a:hover{transform:translateY(-5px);box-shadow:0 8px 15px rgba(0,0,0,.15);background:var(--accent-color);color:#fff}.day-grid a:hover::after{content:attr(title);position:absolute;top:-35px;left:50%;transform:translateX(-50%);background:rgba(44,62,80,.9);color:#fff;padding:6px 10px;border-radius:4px;font-size:.8em;white-space:nowrap;z-index:10}.day-number{font-size:1.5em;font-weight:700;margin-bottom:5px}.day-status{font-size:.8em;color:#444}.return-to-top{position:fixed;bottom:80px;right:30px;background:var(--primary-color);color:#fff;width:50px;height:50px;border-radius:50%;display:flex;justify-content:center;align-items:center;cursor:pointer;box-shadow:0 4px 10px rgba(0,0,0,.2);opacity:0;visibility:hidden;transition:all .3s ease;z-index:99}.return-to-top.visible{opacity:1;visibility:visible}.return-to-top:hover{background:var(--accent-color);transform:translateY(-5px)}footer{text-align:center;padding:1.5em 0;background:var(--primary-color);color:#fff;width:100%;z-index:100;position:relative}@media (max-width:768px){.hero-section{padding:60px 20px}.hero-content h2{font-size:2em}.language-cards{grid-template-columns:1fr;padding:20px}.course-sections{grid-template-columns:1fr}.day-grid{grid-template-columns:repeat(auto-fit,minmax(80px,1fr));gap:10px}.day-grid a{padding:15px 10px}.day-number{font-size:1.2em}.section-card{padding:20px}.section-card h3{font-size:1.2em}}.core-skills-section{padding:40px 0;position:relative;overflow:hidden}.core-skills-section h2{text-align:center;margin-bottom:15px;color:var(--primary-color);font-size:2em}.section-description{text-align:center;max-width:800px;margin:0 auto 30px;color:#444;font-size:1.1em;line-height:1.5}.core-skills-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(400px,1fr));gap:30px;padding:0 20px}.core-skill-card{background:#fff;padding:30px;border-radius:15px;box-shadow:0 6px 12px rgba(0,0,0,.15);transition:transform .3s ease,box-shadow .3s ease;text-decoration:none;color:var(--text-color);position:relative;overflow:hidden;border-left:5px solid var(--accent-color);display:flex;flex-direction:column}.core-skill-card:hover{transform:translateY(-10px);box-shadow:0 12px 24px rgba(0,0,0,.2)}.core-skill-card .card-icon{width:70px;height:70px;background:var(--accent-color);border-radius:50%;display:flex;align-items:center;justify-content:center;margin-bottom:20px;color:#fff;font-size:1.8em;position:relative;z-index:1}.core-skill-card h3{color:var(--primary-color);margin:0 0 15px 0;font-size:1.5em;position:relative;z-index:1}.core-skill-card p{margin:0;color:#555;font-size:1em;line-height:1.6;position:relative;z-index:1}.core-skill-card::before{content:'';position:absolute;width:250px;height:250px;background-image:radial-gradient(circle,var(--accent-color) 1px,transparent 1px);background-size:20px 20px;opacity:.1;z-index:0;border-radius:50%;right:-125px;top:-125px}@media (max-width:768px){.core-skills-grid{grid-template-columns:1fr}.core-skill-card{padding:25px}.core-skill-card .card-icon{width:60px;height:60px;font-size:1.5em}.core-skill-card h3{font-size:1.3em}}.supplementary-section{padding:40px 0;position:relative;overflow:hidden}.supplementary-section h2{text-align:center;margin-bottom:30px;color:var(--primary-color);font-size:2em}.supplementary-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:25px;padding:0 20px}.supplementary-card{background:#fff;padding:25px;border-radius:12px;box-shadow:0 4px 6px rgba(0,0,0,.1);text-decoration:none;color:var(--text-color);position:relative;overflow:hidden}[lang=en] .zh{display:none}[lang=zh-CN] .en{display:none}.zh{font-family:'Noto Sans SC',sans-serif}.language-selector{display:flex;justify-content:center;gap:10px;margin-bottom:20px}.language-btn{padding:8px 15px;border:none;border-radius:20px;background:rgba(255,255,255,.1);color:#fff;cursor:pointer;transition:all .3s ease;display:flex;align-items:center;gap:5px;font-size:.9em}.language-btn.active,.language-btn:hover{background:rgba(255,255,255,.3);transform:translateY(-2px);box-shadow:0 2px 4px rgba(0,0,0,.2);font-weight:600}@media (max-width:768px){.language-selector{flex-wrap:wrap;gap:5px}.language-btn{padding:6px 10px;font-size:.85em}}.supplementary-card::before{content:'';position:absolute;width:200px;height:200px;background-image:radial-gradient(circle,var(--accent-color) 1px,transparent 1px);background-size:20px 20px;opacity:.1;z-index:0;border-radius:50%;right:-100px;top:-100px}.supplementary-card:hover{transform:translateY(-10px);box-shadow:0 8px 15px rgba(0,0,0,.15)}.card-icon{width:60px;height:60px;background:var(--accent-color);border-radius:50%;display:flex;align-items:center;justify-content:center;margin-bottom:15px;color:#fff;font-size:1.5em;position:relative;z-index:1}.supplementary-card h3{color:var(--accent-color);margin:0 0 10px 0;font-size:1.3em;position:relative;z-index:1}.supplementary-card p{margin:0;color:#444;font-size:.95em;line-height:1.5;position:relative;z-index:1}.benefits-section{padding:40px 0;background:var(--secondary-color);position:relative;overflow:hidden}.journey-path{position:absolute;top:0;left:50%;height:100%;width:4px;background:linear-gradient(to bottom,transparent,var(--accent-color),transparent);opacity:.2;z-index:0}.journey-path::after,.journey-path::before{content:'';position:absolute;width:20px;height:20px;border-radius:50%;background:var(--accent-color);left:50%;transform:translateX(-50%);opacity:.4}.journey-path::before{top:10%}.journey-path::after{bottom:10%}.benefits-section h2{text-align:center;margin-bottom:30px;color:var(--primary-color);font-size:2em}.benefits-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:25px;padding:0 20px}.benefit-card{background:#fff;padding:25px;border-radius:12px;box-shadow:0 4px 6px rgba(0,0,0,.1);transition:transform .3s ease}.benefit-card:hover{transform:translateY(-5px);box-shadow:0 8px 15px rgba(0,0,0,.15)}.benefit-card h3{color:var(--accent-color);margin:0 0 20px 0;font-size:1.3em;display:flex;align-items:center;gap:10px}.benefit-card h3 i{font-size:1.2em;opacity:.9}.benefit-card ul{list-style:none;padding:0;margin:0}.benefit-card ul li{margin-bottom:12px;padding-left:20px;position:relative}.benefit-card ul li:before{content:"•";color:var(--accent-color);position:absolute;left:0}.combo-table{margin-top:15px}.combo-row{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid #eee}.combo-row:last-child{border-bottom:none}.combo{font-weight:600;color:var(--primary-color)}.benefit{color:var(--accent-color);text-align:right}@media (max-width:768px){.benefits-grid{grid-template-columns:1fr}.combo-row{flex-direction:column;align-items:flex-start;gap:5px}.benefit{text-align:left}}@media (max-width:480px){header h1{font-size:2em}.hero-content h2{font-size:1.8em}.hero-content p{font-size:1em}body{padding-bottom:80px}.language-card::after{display:none}}@keyframes fadeIn{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes pulse{0%{transform:scale(1)}50%{transform:scale(1.05)}100%{transform:scale(1)}}@keyframes float{0%{transform:translateY(0)}50%{transform:translateY(-10px)}100%{transform:translateY(0)}}@keyframes slideInRight{from{opacity:0;transform:translateX(50px)}to{opacity:1;transform:translateX(0)}}@keyframes slideInLeft{from{opacity:0;transform:translateX(-50px)}to{opacity:1;transform:translateX(0)}}.animate-fade-in{animation:fadeIn .8s ease forwards}.animate-pulse{animation:pulse 2s ease-in-out infinite}.animate-float{animation:float 3s ease-in-out infinite}.animate-slide-right{animation:slideInRight .8s ease forwards}.animate-slide-left{animation:slideInLeft .8s ease forwards}.hover-scale{transition:transform .3s ease}.hover-scale:hover{transform:scale(1.05)}.lesson-container{background:#fff;border-radius:15px;padding:30px;box-shadow:0 4px 6px rgba(0,0,0,.1);margin:20px 0;position:relative;animation:fadeIn .8s ease forwards}.lesson-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px}.lesson-title{display:flex;align-items:center;gap:10px}.lesson-title .flag{font-size:1.5em}.language-selector{display:flex;gap:10px}.language-btn{padding:8px 15px;border:none;border-radius:8px;background:var(--secondary-color);color:var(--text-color);cursor:pointer;transition:all .3s ease;display:flex;align-items:center;gap:5px}.language-btn.active,.language-btn:hover{background:var(--accent-color);color:#fff;transform:translateY(-2px)}.audio-player{width:100%;margin:20px 0;padding:15px;background:var(--secondary-color);border-radius:10px;animation:fadeIn .8s ease forwards}.audio-player audio{width:100%}#audio-fallback{margin-top:10px}#audio-fallback .note{background-color:rgba(52,152,219,.1);border-left:4px solid var(--accent-color);padding:10px 15px;border-radius:4px;font-size:.9em;color:var(--primary-color);display:flex;align-items:center;gap:8px}.text-content{margin:20px 0;line-height:1.8;font-size:1.1em;animation:fadeIn .8s ease forwards}.phrase-section{margin-bottom:30px;background:var(--secondary-color);padding:20px;border-radius:10px;animation:fadeIn .8s ease forwards;position:relative;overflow:hidden}.phrase-section::before{content:'';position:absolute;width:200px;height:200px;background-image:radial-gradient(circle,var(--accent-color) 1px,transparent 1px);background-size:20px 20px;opacity:.1;z-index:0;border-radius:50%;right:-100px;top:-100px}.phrase-section h3{color:var(--accent-color);margin:0 0 15px 0;font-size:1.3em;border-bottom:2px solid var(--accent-color);padding-bottom:5px;position:relative;z-index:1}.phrase-list{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:15px;position:relative;z-index:1}.phrase-item{background:#fff;padding:12px;border-radius:8px;display:flex;justify-content:space-between;align-items:center;gap:8px;transition:all .3s ease;box-shadow:0 2px 4px rgba(0,0,0,.05)}.phrase-reading{flex:1;min-width:0;line-height:1.55}.phrase-item.audio-sync-active{box-shadow:0 0 0 2px rgba(26,92,143,.35),0 4px 8px rgba(0,0,0,.1);background-color:#f9fcff}.lesson-token{transition:background-color .12s ease,color .12s ease}.lesson-token.audio-sync-reading{background-color:rgba(243,156,18,.35);border-radius:2px}.lesson-space{pointer-events:none}.reading-sync-line{margin:.75rem 0;line-height:1.65}.reading-sync-line.audio-sync-active,.writing-sync-cue.audio-sync-active{background-color:rgba(249,252,255,.95);box-shadow:0 0 0 2px rgba(26,92,143,.3);border-radius:4px}.writing-sync-cue{display:block;margin-bottom:.35rem}.reading-passage-reading{display:inline}.phrase-item:hover{transform:translateX(5px);box-shadow:0 4px 8px rgba(0,0,0,.1)}.copy-btn{background:0 0;border:none;color:var(--accent-color);cursor:pointer;padding:5px;opacity:.7;transition:all .3s ease}.copy-btn:hover{opacity:1;transform:scale(1.1)}.phrase-star-btn{background:0 0;border:none;color:var(--warning-color);cursor:pointer;padding:5px;opacity:.85;transition:all .3s ease}.phrase-star-btn:hover{opacity:1;transform:scale(1.1)}.phrase-star-btn[aria-pressed=true]{color:#f39c12}.review-card{border:1px solid var(--secondary-color);border-radius:8px;padding:1rem;margin-bottom:1rem;background:#fff}.review-phrase{font-size:1.15rem;margin:0 0 .5rem}.review-meta{margin:0 0 .75rem;font-size:.9rem;color:#555}.review-actions{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center}.secondary-btn{background:#f0f0f0;border:1px solid #ddd;padding:.5rem 1rem;border-radius:4px;cursor:pointer}.site-nav-links{margin:.5rem 0 0;text-align:center}.site-nav-links a{color:inherit;font-weight:600;text-decoration:underline}.navigation{display:flex;justify-content:space-between;margin:20px 0;animation:fadeIn .8s ease forwards}.navigation:last-child{margin-top:30px;padding-top:20px;border-top:1px solid var(--secondary-color)}.nav-btn{padding:10px 20px;border:none;border-radius:8px;background:var(--accent-color);color:#fff;text-decoration:none;display:flex;align-items:center;gap:8px;transition:all .3s ease}.nav-btn:hover{background:var(--hover-color);transform:translateY(-2px)}.nav-btn.disabled{opacity:.5;cursor:not-allowed}.home-btn{padding:10px 20px;background:var(--primary-color);color:#fff;text-decoration:none;border-radius:8px;display:inline-flex;align-items:center;gap:8px;transition:all .3s ease}.home-btn:hover{background:#34495e;transform:translateY(-2px)}.section-info{background:var(--secondary-color);padding:15px;border-radius:8px;margin:20px 0;animation:fadeIn .8s ease forwards;position:relative;overflow:hidden}.section-info::before{content:'';position:absolute;width:200px;height:200px;background-image:radial-gradient(circle,var(--accent-color) 1px,transparent 1px);background-size:20px 20px;opacity:.1;z-index:0;border-radius:50%;left:-100px;bottom:-100px}.section-info h3{color:var(--accent-color);margin:0 0 10px 0;position:relative;z-index:1}.section-info p{margin:0;color:#444;position:relative;z-index:1}.lesson-actions{display:flex;justify-content:center;margin:30px 0;animation:fadeIn .8s ease forwards}.complete-btn{padding:12px 24px;border:none;border-radius:8px;background:var(--success-color);color:#fff;font-size:1.1em;cursor:pointer;display:flex;align-items:center;gap:8px;transition:all .3s ease}.complete-btn:hover{transform:translateY(-2px);box-shadow:0 4px 8px rgba(0,0,0,.1)}.complete-btn.completed{background:var(--accent-color);cursor:default}.copy-notification{position:fixed;bottom:20px;right:20px;background:var(--success-color);color:#fff;padding:10px 20px;border-radius:8px;box-shadow:0 2px 4px rgba(0,0,0,.2);display:none;animation:fadeInOut 2s ease;z-index:1000}@keyframes fadeInOut{0%{opacity:0;transform:translateY(20px)}20%{opacity:1;transform:translateY(0)}80%{opacity:1;transform:translateY(0)}100%{opacity:0;transform:translateY(-20px)}}@media (max-width:768px){.language-selector{gap:5px}.language-btn{padding:6px 10px;font-size:.9em}.lesson-header{flex-direction:column;align-items:flex-start;gap:15px}.language-selector{width:100%;justify-content:flex-start}.navigation{flex-direction:column;gap:10px;align-items:stretch}.home-btn,.nav-btn{width:100%;justify-content:center;padding:12px;font-size:.9em}}@media (max-width:480px){.language-selector{flex-wrap:wrap;justify-content:flex-start;gap:8px}.language-btn{padding:5px 8px;font-size:.8em}}@media (max-width:768px){.return-to-top{bottom:70px;right:20px;width:40px;height:40px}body{padding-bottom:0}} \ No newline at end of file +@font-face { + font-family: "Noto Sans SC"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: + local("Noto Sans SC Regular"), + local("NotoSansSC-Regular"), + url("https://fonts.gstatic.com/s/notosanssc/v12/k3kXo84MPvpLmixcA63oeALhLOCT-xWNm8Hqd37g1OkDRZe7lR4sg1IzSy-MNbE9VH8V.119.woff2") + format("woff2"); + unicode-range: U+4E00-9FFF; +} +:root { + --primary-color: #1a2733; + --secondary-color: #f8f9fa; + --accent-color: #1a5c8f; + --text-color: #1a2733; + --hover-color: #134166; + --success-color: #1e8449; + --warning-color: #996600; + --danger-color: #c0392b; +} +body { + font-family: "Poppins", "Noto Sans SC", Arial, sans-serif; + margin: 0; + padding: 0; + background-color: var(--secondary-color); + color: var(--text-color); + line-height: 1.6; + padding-bottom: 60px; +} +header { + background: linear-gradient(135deg, var(--primary-color), #34495e); + color: white; + padding: 2em 0; + text-align: center; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} +header h1 { + margin: 0; + font-size: 2.5em; + font-weight: 700; + letter-spacing: 1px; +} +header p { + margin: 10px 0 0; + font-size: 1.2em; + opacity: 0.9; +} +.hero-section { + background: url("https://images.unsplash.com/photo-1546410531-89436e5b419a?auto=format&fit=crop&w=1920&q=80") + center/cover; + padding: 100px 20px; + text-align: center; + color: white; + position: relative; +} +.hero-section::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(44, 62, 80, 0.7); +} +.hero-content { + position: relative; + max-width: 800px; + margin: 0 auto; +} +.hero-content h2 { + font-size: 2.8em; + margin-bottom: 20px; + font-weight: 700; +} +.hero-content p { + font-size: 1.2em; + margin-bottom: 30px; +} +main { + padding: 20px; + max-width: 1200px; + margin: 0 auto; + position: relative; +} +.visual-anchor { + position: absolute; + width: 200px; + height: 200px; + background-image: radial-gradient( + circle, + var(--accent-color) 1px, + transparent 1px + ); + background-size: 20px 20px; + opacity: 0.1; + z-index: -1; + border-radius: 50%; +} +.language-cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 30px; + padding: 40px 20px; + max-width: 1200px; + margin: 0 auto; +} +.language-card { + background: white; + border-radius: 15px; + overflow: hidden; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); + transition: + transform 0.3s ease, + box-shadow 0.3s ease; + cursor: pointer; + position: relative; +} +.language-card:hover { + transform: translateY(-10px); + box-shadow: 0 15px 30px rgba(0, 0, 0, 0.15); +} +.language-card::after { + content: attr(title); + position: absolute; + top: -40px; + left: 50%; + transform: translateX(-50%); + background: rgba(44, 62, 80, 0.9); + color: white; + padding: 8px 12px; + border-radius: 6px; + font-size: 0.85em; + opacity: 0; + transition: + opacity 0.3s ease, + bottom 0.3s ease; + pointer-events: none; + white-space: nowrap; + z-index: 10; +} +.language-card:hover::after { + opacity: 1; + top: -50px; +} +.card-header { + padding: 20px; + background: linear-gradient( + 135deg, + var(--accent-color), + var(--hover-color) + ); + color: white; +} +.card-header h3 { + margin: 0; + font-size: 1.5em; + display: flex; + align-items: center; + gap: 10px; +} +.flag { + font-size: 1.2em; +} +.card-content { + padding: 20px; +} +.progress-container { + margin: 15px 0; +} +.progress-bar { + height: 8px; + background: #ecf0f1; + border-radius: 4px; + overflow: hidden; +} +.progress-fill { + height: 100%; + background: var(--success-color); + width: 0%; + transition: width 1s ease; +} +.stats { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 15px; + margin-top: 20px; +} +.learning-dashboard { + padding: 0 20px 30px; +} +.dashboard-panel { + background: white; + border: 1px solid #e6e9ec; + border-radius: 8px; + padding: 24px; +} +.dashboard-panel h2 { + margin-top: 0; +} +.dashboard-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 16px; + margin: 18px 0; +} +.dashboard-stat { + border: 1px solid #e6e9ec; + border-radius: 8px; + padding: 16px; + background: #fbfcfd; +} +.dashboard-number { + display: block; + color: var(--accent-color); + font-size: 2rem; + font-weight: 700; +} +.dashboard-label { + display: block; + font-size: 0.9rem; + color: #4d5b66; +} +.achievement-list { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); + gap: 10px; + list-style: none; + padding: 0; + margin: 18px 0 0; +} +.achievement-badge { + border: 1px solid #dce2e7; + border-radius: 8px; + padding: 10px; + background: #f5f7f8; + color: #697782; +} +.achievement-badge.earned { + border-color: rgba(30, 132, 73, 0.35); + background: rgba(30, 132, 73, 0.08); + color: var(--text-color); +} +.achievement-badge strong, +.achievement-badge span { + display: block; +} +.achievement-badge span { + font-size: 0.85rem; +} +.stat-item { + text-align: center; + padding: 10px; + background: var(--secondary-color); + border-radius: 8px; +} +.stat-number { + font-size: 1.5em; + font-weight: 700; + color: var(--accent-color); +} +.stat-label { + font-size: 0.9em; + color: #444; +} +.section-divider { + height: 3px; + background: linear-gradient( + 90deg, + transparent, + var(--accent-color), + transparent + ); + margin: 20px auto 40px; + width: 80%; + max-width: 800px; + opacity: 0.5; + position: relative; +} +.section-divider::before { + content: ""; + position: absolute; + width: 30px; + height: 30px; + background: white; + border: 3px solid var(--accent-color); + border-radius: 50%; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + opacity: 0.8; +} +.course-overview { + padding: 40px 0; + position: relative; + overflow: hidden; +} +.course-overview h2 { + text-align: center; + margin-bottom: 30px; + color: var(--primary-color); + font-size: 2em; +} +.course-sections { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + padding: 0 20px; +} +.section-card { + background: white; + padding: 25px; + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + transition: + transform 0.3s ease, + box-shadow 0.3s ease; + border-left: 4px solid var(--accent-color); +} +.section-card:hover { + transform: translateY(-5px); + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.15); +} +.section-card h3 { + color: var(--accent-color); + margin: 0 0 15px 0; + font-size: 1.3em; + display: flex; + align-items: center; + gap: 10px; +} +.section-card h3 i { + font-size: 1.2em; + opacity: 0.9; +} +.section-card p { + margin: 0; + color: #444; + font-size: 0.95em; + line-height: 1.5; +} +#day-selection { + margin: 40px 0; + position: relative; + overflow: hidden; +} +#day-selection h2 { + text-align: center; + margin-bottom: 30px; + color: var(--primary-color); + font-size: 2em; +} +.day-controls { + display: flex; + justify-content: center; + margin-bottom: 20px; + gap: 10px; +} +.day-controls button { + background: var(--accent-color); + color: white; + border: none; + padding: 8px 15px; + border-radius: 20px; + cursor: pointer; + font-weight: 500; + transition: all 0.3s ease; + display: flex; + align-items: center; + gap: 5px; +} +.day-controls button:hover { + background: var(--hover-color); + transform: translateY(-2px); +} +.day-controls .day-range { + display: flex; + align-items: center; + background: white; + padding: 8px 15px; + border-radius: 20px; + font-weight: 600; + color: var(--primary-color); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} +.day-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); + gap: 15px; + padding: 20px; +} +.day-grid a { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 20px 15px; + background: white; + color: var(--text-color); + text-decoration: none; + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; + position: relative; +} +.day-grid a:hover { + transform: translateY(-5px); + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.15); + background: var(--accent-color); + color: white; +} +.day-grid a:hover::after { + content: attr(title); + position: absolute; + top: -35px; + left: 50%; + transform: translateX(-50%); + background: rgba(44, 62, 80, 0.9); + color: white; + padding: 6px 10px; + border-radius: 4px; + font-size: 0.8em; + white-space: nowrap; + z-index: 10; +} +.day-number { + font-size: 1.5em; + font-weight: 700; + margin-bottom: 5px; +} +.day-status { + font-size: 0.8em; + color: #444; +} +.return-to-top { + position: fixed; + bottom: 80px; + right: 30px; + background: var(--primary-color); + color: white; + width: 50px; + height: 50px; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + z-index: 99; +} +.return-to-top.visible { + opacity: 1; + visibility: visible; +} +.return-to-top:hover { + background: var(--accent-color); + transform: translateY(-5px); +} +footer { + text-align: center; + padding: 1.5em 0; + background: var(--primary-color); + color: white; + width: 100%; + z-index: 100; + position: relative; +} +@media (max-width: 768px) { + .hero-section { + padding: 60px 20px; + } + .hero-content h2 { + font-size: 2em; + } + .language-cards { + grid-template-columns: 1fr; + padding: 20px; + } + .course-sections { + grid-template-columns: 1fr; + } + .day-grid { + grid-template-columns: repeat(auto-fit, minmax(80px, 1fr)); + gap: 10px; + } + .day-grid a { + padding: 15px 10px; + } + .day-number { + font-size: 1.2em; + } + .section-card { + padding: 20px; + } + .section-card h3 { + font-size: 1.2em; + } +} +.core-skills-section { + padding: 40px 0; + position: relative; + overflow: hidden; +} +.core-skills-section h2 { + text-align: center; + margin-bottom: 15px; + color: var(--primary-color); + font-size: 2em; +} +.section-description { + text-align: center; + max-width: 800px; + margin: 0 auto 30px; + color: #444; + font-size: 1.1em; + line-height: 1.5; +} +.core-skills-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); + gap: 30px; + padding: 0 20px; +} +.core-skill-card { + background: white; + padding: 30px; + border-radius: 15px; + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); + transition: + transform 0.3s ease, + box-shadow 0.3s ease; + text-decoration: none; + color: var(--text-color); + position: relative; + overflow: hidden; + border-left: 5px solid var(--accent-color); + display: flex; + flex-direction: column; +} +.core-skill-card:hover { + transform: translateY(-10px); + box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2); +} +.core-skill-card .card-icon { + width: 70px; + height: 70px; + background: var(--accent-color); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 20px; + color: white; + font-size: 1.8em; + position: relative; + z-index: 1; +} +.core-skill-card h3 { + color: var(--primary-color); + margin: 0 0 15px 0; + font-size: 1.5em; + position: relative; + z-index: 1; +} +.core-skill-card p { + margin: 0; + color: #555; + font-size: 1em; + line-height: 1.6; + position: relative; + z-index: 1; +} +.core-skill-card::before { + content: ""; + position: absolute; + width: 250px; + height: 250px; + background-image: radial-gradient( + circle, + var(--accent-color) 1px, + transparent 1px + ); + background-size: 20px 20px; + opacity: 0.1; + z-index: 0; + border-radius: 50%; + right: -125px; + top: -125px; +} +@media (max-width: 768px) { + .core-skills-grid { + grid-template-columns: 1fr; + } + .core-skill-card { + padding: 25px; + } + .core-skill-card .card-icon { + width: 60px; + height: 60px; + font-size: 1.5em; + } + .core-skill-card h3 { + font-size: 1.3em; + } +} +.supplementary-section { + padding: 40px 0; + position: relative; + overflow: hidden; +} +.supplementary-section h2 { + text-align: center; + margin-bottom: 30px; + color: var(--primary-color); + font-size: 2em; +} +.supplementary-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 25px; + padding: 0 20px; +} +.supplementary-card { + background: white; + padding: 25px; + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + text-decoration: none; + color: var(--text-color); + position: relative; + overflow: hidden; +} +[lang="en"] .zh { + display: none; +} +[lang="zh-CN"] .en { + display: none; +} +.zh { + font-family: "Noto Sans SC", sans-serif; +} +.language-selector { + display: flex; + justify-content: center; + gap: 10px; + margin-bottom: 20px; +} +.language-btn { + padding: 8px 15px; + border: none; + border-radius: 20px; + background: rgba(255, 255, 255, 0.1); + color: white; + cursor: pointer; + transition: all 0.3s ease; + display: flex; + align-items: center; + gap: 5px; + font-size: 0.9em; +} +.language-btn:hover, +.language-btn.active { + background: rgba(255, 255, 255, 0.3); + transform: translateY(-2px); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + font-weight: 600; +} +@media (max-width: 768px) { + .language-selector { + flex-wrap: wrap; + gap: 5px; + } + .language-btn { + padding: 6px 10px; + font-size: 0.85em; + } +} +.supplementary-card::before { + content: ""; + position: absolute; + width: 200px; + height: 200px; + background-image: radial-gradient( + circle, + var(--accent-color) 1px, + transparent 1px + ); + background-size: 20px 20px; + opacity: 0.1; + z-index: 0; + border-radius: 50%; + right: -100px; + top: -100px; +} +.supplementary-card:hover { + transform: translateY(-10px); + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.15); +} +.card-icon { + width: 60px; + height: 60px; + background: var(--accent-color); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 15px; + color: white; + font-size: 1.5em; + position: relative; + z-index: 1; +} +.supplementary-card h3 { + color: var(--accent-color); + margin: 0 0 10px 0; + font-size: 1.3em; + position: relative; + z-index: 1; +} +.supplementary-card p { + margin: 0; + color: #444; + font-size: 0.95em; + line-height: 1.5; + position: relative; + z-index: 1; +} +.benefits-section { + padding: 40px 0; + background: var(--secondary-color); + position: relative; + overflow: hidden; +} +.journey-path { + position: absolute; + top: 0; + left: 50%; + height: 100%; + width: 4px; + background: linear-gradient( + to bottom, + transparent, + var(--accent-color), + transparent + ); + opacity: 0.2; + z-index: 0; +} +.journey-path::before, +.journey-path::after { + content: ""; + position: absolute; + width: 20px; + height: 20px; + border-radius: 50%; + background: var(--accent-color); + left: 50%; + transform: translateX(-50%); + opacity: 0.4; +} +.journey-path::before { + top: 10%; +} +.journey-path::after { + bottom: 10%; +} +.benefits-section h2 { + text-align: center; + margin-bottom: 30px; + color: var(--primary-color); + font-size: 2em; +} +.benefits-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 25px; + padding: 0 20px; +} +.benefit-card { + background: white; + padding: 25px; + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + transition: transform 0.3s ease; +} +.benefit-card:hover { + transform: translateY(-5px); + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.15); +} +.benefit-card h3 { + color: var(--accent-color); + margin: 0 0 20px 0; + font-size: 1.3em; + display: flex; + align-items: center; + gap: 10px; +} +.benefit-card h3 i { + font-size: 1.2em; + opacity: 0.9; +} +.benefit-card ul { + list-style: none; + padding: 0; + margin: 0; +} +.benefit-card ul li { + margin-bottom: 12px; + padding-left: 20px; + position: relative; +} +.benefit-card ul li:before { + content: "•"; + color: var(--accent-color); + position: absolute; + left: 0; +} +.combo-table { + margin-top: 15px; +} +.combo-row { + display: flex; + justify-content: space-between; + padding: 8px 0; + border-bottom: 1px solid #eee; +} +.combo-row:last-child { + border-bottom: none; +} +.combo { + font-weight: 600; + color: var(--primary-color); +} +.benefit { + color: var(--accent-color); + text-align: right; +} +@media (max-width: 768px) { + .benefits-grid { + grid-template-columns: 1fr; + } + .combo-row { + flex-direction: column; + align-items: flex-start; + gap: 5px; + } + .benefit { + text-align: left; + } +} +@media (max-width: 480px) { + header h1 { + font-size: 2em; + } + .hero-content h2 { + font-size: 1.8em; + } + .hero-content p { + font-size: 1em; + } + body { + padding-bottom: 80px; + } + .language-card::after { + display: none; + } +} +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} +@keyframes pulse { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.05); + } + 100% { + transform: scale(1); + } +} +@keyframes float { + 0% { + transform: translateY(0px); + } + 50% { + transform: translateY(-10px); + } + 100% { + transform: translateY(0px); + } +} +@keyframes slideInRight { + from { + opacity: 0; + transform: translateX(50px); + } + to { + opacity: 1; + transform: translateX(0); + } +} +@keyframes slideInLeft { + from { + opacity: 0; + transform: translateX(-50px); + } + to { + opacity: 1; + transform: translateX(0); + } +} +.animate-fade-in { + animation: fadeIn 0.8s ease forwards; +} +.animate-pulse { + animation: pulse 2s ease-in-out infinite; +} +.animate-float { + animation: float 3s ease-in-out infinite; +} +.animate-slide-right { + animation: slideInRight 0.8s ease forwards; +} +.animate-slide-left { + animation: slideInLeft 0.8s ease forwards; +} +.hover-scale { + transition: transform 0.3s ease; +} +.hover-scale:hover { + transform: scale(1.05); +} +.lesson-container { + background: white; + border-radius: 15px; + padding: 30px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + margin: 20px 0; + position: relative; + animation: fadeIn 0.8s ease forwards; +} +.lesson-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 20px; +} +.lesson-title { + display: flex; + align-items: center; + gap: 10px; +} +.lesson-title .flag { + font-size: 1.5em; +} +.language-selector { + display: flex; + gap: 10px; +} +.language-btn { + padding: 8px 15px; + border: none; + border-radius: 8px; + background: var(--secondary-color); + color: var(--text-color); + cursor: pointer; + transition: all 0.3s ease; + display: flex; + align-items: center; + gap: 5px; +} +.language-btn:hover, +.language-btn.active { + background: var(--accent-color); + color: white; + transform: translateY(-2px); +} +.audio-player { + width: 100%; + margin: 20px 0; + padding: 15px; + background: var(--secondary-color); + border-radius: 10px; + animation: fadeIn 0.8s ease forwards; +} +.audio-player audio { + width: 100%; +} +#audio-fallback { + margin-top: 10px; +} +#audio-fallback .note { + background-color: rgba(52, 152, 219, 0.1); + border-left: 4px solid var(--accent-color); + padding: 10px 15px; + border-radius: 4px; + font-size: 0.9em; + color: var(--primary-color); + display: flex; + align-items: center; + gap: 8px; +} +.text-content { + margin: 20px 0; + line-height: 1.8; + font-size: 1.1em; + animation: fadeIn 0.8s ease forwards; +} +.phrase-section { + margin-bottom: 30px; + background: var(--secondary-color); + padding: 20px; + border-radius: 10px; + animation: fadeIn 0.8s ease forwards; + position: relative; + overflow: hidden; +} +.phrase-section::before { + content: ""; + position: absolute; + width: 200px; + height: 200px; + background-image: radial-gradient( + circle, + var(--accent-color) 1px, + transparent 1px + ); + background-size: 20px 20px; + opacity: 0.1; + z-index: 0; + border-radius: 50%; + right: -100px; + top: -100px; +} +.phrase-section h3 { + color: var(--accent-color); + margin: 0 0 15px 0; + font-size: 1.3em; + border-bottom: 2px solid var(--accent-color); + padding-bottom: 5px; + position: relative; + z-index: 1; +} +.phrase-list { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 15px; + position: relative; + z-index: 1; +} +.phrase-item { + background: white; + padding: 12px; + border-radius: 8px; + display: flex; + justify-content: space-between; + align-items: center; + gap: 8px; + transition: all 0.3s ease; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); +} +.phrase-reading { + flex: 1; + min-width: 0; + line-height: 1.55; +} +.phrase-item.audio-sync-active { + box-shadow: + 0 0 0 2px rgba(26, 92, 143, 0.35), + 0 4px 8px rgba(0, 0, 0, 0.1); + background-color: #f9fcff; +} +.lesson-token { + transition: + background-color 0.12s ease, + color 0.12s ease; +} +.lesson-token.audio-sync-reading { + background-color: rgba(243, 156, 18, 0.35); + border-radius: 2px; +} +.lesson-space { + pointer-events: none; +} +.reading-sync-line { + margin: 0.75rem 0; + line-height: 1.65; +} +.reading-sync-line.audio-sync-active, +.writing-sync-cue.audio-sync-active { + background-color: rgba(249, 252, 255, 0.95); + box-shadow: 0 0 0 2px rgba(26, 92, 143, 0.3); + border-radius: 4px; +} +.writing-sync-cue { + display: block; + margin-bottom: 0.35rem; +} +.reading-passage-reading { + display: inline; +} +.phrase-item:hover { + transform: translateX(5px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} +.copy-btn { + background: none; + border: none; + color: var(--accent-color); + cursor: pointer; + padding: 5px; + opacity: 0.7; + transition: all 0.3s ease; +} +.copy-btn:hover { + opacity: 1; + transform: scale(1.1); +} +.phrase-star-btn { + background: none; + border: none; + color: var(--warning-color); + cursor: pointer; + padding: 5px; + opacity: 0.85; + transition: all 0.3s ease; +} +.phrase-star-btn:hover { + opacity: 1; + transform: scale(1.1); +} +.phrase-star-btn[aria-pressed="true"] { + color: #f39c12; +} +.review-card { + border: 1px solid var(--secondary-color); + border-radius: 8px; + padding: 1rem; + margin-bottom: 1rem; + background: #fff; +} +.review-phrase { + font-size: 1.15rem; + margin: 0 0 0.5rem; +} +.review-meta { + margin: 0 0 0.75rem; + font-size: 0.9rem; + color: #555; +} +.review-actions { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + align-items: center; +} +.secondary-btn { + background: #f0f0f0; + border: 1px solid #ddd; + padding: 0.5rem 1rem; + border-radius: 4px; + cursor: pointer; +} +.site-nav-links { + margin: 0.5rem 0 0; + text-align: center; +} +.site-nav-links a { + color: inherit; + font-weight: 600; + text-decoration: underline; + margin: 0 0.45rem; +} +.srs-card { + background: #fff; + border: 1px solid #e0e5e9; + border-radius: 8px; + padding: 1.25rem; + margin: 1rem 0; +} +.srs-front { + font-size: 1.8rem; + margin: 0.5rem 0; +} +.srs-back { + border-top: 1px solid #e0e5e9; + color: #45525d; + margin: 1rem 0; + padding-top: 1rem; +} +.practice-summary { + background: var(--secondary-color); + border-radius: 8px; + padding: 0.75rem 1rem; +} +.streak-banner h3 { + display: flex; + flex-wrap: wrap; + gap: 0.35rem; + align-items: baseline; +} +.tone-glyph-list { + display: inline-flex; + gap: 0.2rem; + margin-left: 0.4rem; + vertical-align: middle; +} +.tone-glyph { + width: 26px; + height: 18px; +} +.tone-explainer { + border-left: 4px solid var(--accent-color); +} +.stroke-order-panel { + align-items: center; + background: #fbfcfd; + border: 1px solid #e0e5e9; + border-radius: 8px; + display: flex; + flex-direction: column; + gap: 0.75rem; + margin: 0 auto 1rem; + padding: 1rem; + width: min(100%, 260px); +} +.stroke-order-writer { + min-height: 180px; +} +.hanzi-writer-lite { + display: block; +} +.navigation { + display: flex; + justify-content: space-between; + margin: 20px 0; + animation: fadeIn 0.8s ease forwards; +} +.navigation:last-child { + margin-top: 30px; + padding-top: 20px; + border-top: 1px solid var(--secondary-color); +} +.nav-btn { + padding: 10px 20px; + border: none; + border-radius: 8px; + background: var(--accent-color); + color: white; + text-decoration: none; + display: flex; + align-items: center; + gap: 8px; + transition: all 0.3s ease; +} +.nav-btn:hover { + background: var(--hover-color); + transform: translateY(-2px); +} +.nav-btn.disabled { + opacity: 0.5; + cursor: not-allowed; +} +.home-btn { + padding: 10px 20px; + background: var(--primary-color); + color: white; + text-decoration: none; + border-radius: 8px; + display: inline-flex; + align-items: center; + gap: 8px; + transition: all 0.3s ease; +} +.home-btn:hover { + background: #34495e; + transform: translateY(-2px); +} +.section-info { + background: var(--secondary-color); + padding: 15px; + border-radius: 8px; + margin: 20px 0; + animation: fadeIn 0.8s ease forwards; + position: relative; + overflow: hidden; +} +.section-info::before { + content: ""; + position: absolute; + width: 200px; + height: 200px; + background-image: radial-gradient( + circle, + var(--accent-color) 1px, + transparent 1px + ); + background-size: 20px 20px; + opacity: 0.1; + z-index: 0; + border-radius: 50%; + left: -100px; + bottom: -100px; +} +.section-info h3 { + color: var(--accent-color); + margin: 0 0 10px 0; + position: relative; + z-index: 1; +} +.section-info p { + margin: 0; + color: #444; + position: relative; + z-index: 1; +} +.lesson-actions { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + justify-content: center; + align-items: center; + margin: 30px 0; + animation: fadeIn 0.8s ease forwards; +} +.complete-btn { + padding: 12px 24px; + border: none; + border-radius: 8px; + background: var(--success-color); + color: white; + font-size: 1.1em; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + transition: all 0.3s ease; +} +.complete-btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} +.complete-btn.completed { + background: var(--accent-color); + cursor: default; +} +.copy-notification { + position: fixed; + bottom: 20px; + right: 20px; + background: var(--success-color); + color: white; + padding: 10px 20px; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + display: none; + animation: fadeInOut 2s ease; + z-index: 1000; +} +@keyframes fadeInOut { + 0% { + opacity: 0; + transform: translateY(20px); + } + 20% { + opacity: 1; + transform: translateY(0); + } + 80% { + opacity: 1; + transform: translateY(0); + } + 100% { + opacity: 0; + transform: translateY(-20px); + } +} +@media (max-width: 768px) { + .language-selector { + gap: 5px; + } + .language-btn { + padding: 6px 10px; + font-size: 0.9em; + } + .lesson-header { + flex-direction: column; + align-items: flex-start; + gap: 15px; + } + .language-selector { + width: 100%; + justify-content: flex-start; + } + .navigation { + flex-direction: column; + gap: 10px; + align-items: stretch; + } + .nav-btn, + .home-btn { + width: 100%; + justify-content: center; + padding: 12px; + font-size: 0.9em; + } +} +@media (max-width: 480px) { + .language-selector { + flex-wrap: wrap; + justify-content: flex-start; + gap: 8px; + } + .language-btn { + padding: 5px 8px; + font-size: 0.8em; + } +} +@media (max-width: 768px) { + .return-to-top { + bottom: 70px; + right: 20px; + width: 40px; + height: 40px; + } + body { + padding-bottom: 0; + } +} +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} +.modal-content { + background: white; + padding: 2rem; + border-radius: 8px; + max-width: 400px; + width: 90%; +} +.settings-form { + display: flex; + flex-direction: column; + gap: 1rem; +} +.form-group { + display: flex; + flex-direction: column; + gap: 0.5rem; +} +.button-group { + display: flex; + gap: 1rem; + justify-content: flex-end; + margin-top: 1rem; +} +.primary-btn { + background: var(--primary-color); + color: white; + border: none; + padding: 0.5rem 1rem; + border-radius: 4px; + cursor: pointer; + font-family: inherit; + font-size: inherit; +} +.level-selector { + margin: 1.5rem 0; +} +.level-selector h3 { + font-size: 1rem; + margin-bottom: 0.75rem; + color: var(--text-color); +} +.level-buttons { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} +.level-btn { + padding: 0.4rem 0.9rem; + border-radius: 20px; + border: 2px solid var(--accent-color); + background: transparent; + color: var(--accent-color); + text-decoration: none; + font-size: 0.875rem; + font-family: inherit; + transition: + background 0.2s, + color 0.2s; + display: inline-block; + cursor: pointer; +} +.level-btn.active, +.level-btn:hover { + background: var(--accent-color); + color: white; +} diff --git a/css/writing.css b/css/writing.css index 6211ec3..59bed27 100644 --- a/css/writing.css +++ b/css/writing.css @@ -203,11 +203,11 @@ textarea.answer-input { .tools-container { flex-direction: column; } - + .practice-area { flex-direction: column; } - + .character-display { margin-bottom: 10px; } @@ -218,12 +218,12 @@ textarea.answer-input { flex-wrap: wrap; justify-content: center; } - + .practice-cell { width: 50px; height: 50px; } - + .writing-input { width: 40px; height: 40px; diff --git a/day.html b/day.html index 73cd050..adbd5fc 100644 --- a/day.html +++ b/day.html @@ -1,117 +1,241 @@ - + - - - - - - - - - Mandarin Pathways - Daily Lesson - - - - - - - - - -
-

Mandarin Pathways

-

开启您的中文学习之旅Your Journey to Chinese Language Mastery

-
- -
- -
- -
+ + + + + + + + + Mandarin Pathways - Daily Lesson + + + + + + + + + + +
+

Mandarin Pathways

+

+ 开启您的中文学习之旅Your Journey to Chinese Language Mastery +

+
+ +
+ +
+ +
- -
+ +
-
-
-
- -

Day

+
+
+
+ +

+ Day +

+
+
+ + + +
-
- - - + + -
- + - + -
- -
-
+ -
- -
+
+ +
+
-
- -
+
+ +
+ +
+ + + 课程测验Quiz this day + + +
- -
- - + + +
+
+ + + +
+ 短语已复制到剪贴板!Phrase copied to clipboard!
- -
-
- - - -
- 短语已复制到剪贴板!Phrase copied to clipboard! -
- - - - - - - - + + + + + + + + + + + diff --git a/flutter_app/README.md b/flutter_app/README.md index ca67196..b5dce68 100644 --- a/flutter_app/README.md +++ b/flutter_app/README.md @@ -16,32 +16,38 @@ Mandarin Pathways has been converted from a Progressive Web App (PWA) to a nativ ## Features ### 📚 40-Day Structured Curriculum + - Daily lessons organized into 5 progressive sections - Lessons available in three formats: Simplified Chinese (简体中文), Pinyin, and English - Progress tracking across all languages ### 🎵 Audio Features + - Dual-language audio for every lesson (Chinese + English) - Variable playback speed (0.5x to 2.0x) - Loop functionality for practice - Seek controls with 10-second skip forward/backward ### ✍️ Core Skills Practice + - **Reading Skills**: Practice comprehension at multiple difficulty levels - **Writing Skills**: Character practice, sentence building, and translation exercises ### 📊 Progress Tracking + - Track completion across all 40 days - Separate progress for each language format - Visual progress indicators on home screen - Persistent storage using SharedPreferences ### 🔔 Notifications + - Daily reminder notifications - Customizable reminder times - Completion celebration notifications ### 🌐 Supplementary Materials + - Education & Academic Life - Hobbies & Interests - Emotions & Feelings @@ -99,65 +105,74 @@ flutter_app/ ### Installation 1. **Install Flutter**: - ```bash - # Clone Flutter repository - git clone https://github.com/flutter/flutter.git -b stable - # Add to PATH - export PATH="$PATH:`pwd`/flutter/bin" + ```bash + # Clone Flutter repository + git clone https://github.com/flutter/flutter.git -b stable - # Verify installation - flutter doctor - ``` + # Add to PATH + export PATH="$PATH:`pwd`/flutter/bin" + + # Verify installation + flutter doctor + ``` 2. **Install dependencies** (from the repository root, this folder is `flutter_app/`): - ```bash - cd flutter_app - flutter pub get - ``` + + ```bash + cd flutter_app + flutter pub get + ``` 3. **iOS / macOS native builds**: After dependency changes, refresh CocoaPods: - ```bash - cd ios && pod install && cd .. - cd macos && pod install && cd .. - ``` - Run these commands from **`flutter_app/`** (same directory as `pubspec.yaml`). + + ```bash + cd ios && pod install && cd .. + cd macos && pod install && cd .. + ``` + + Run these commands from **`flutter_app/`** (same directory as `pubspec.yaml`). 4. **Run the app**: - ```bash - # Run on connected device/emulator - flutter run - - # Run on specific platform - flutter run -d chrome # Web - flutter run -d ios # iOS - flutter run -d android # Android - flutter run -d macos # macOS - flutter run -d windows # Windows - flutter run -d linux # Linux - ``` + + ```bash + # Run on connected device/emulator + flutter run + + # Run on specific platform + flutter run -d chrome # Web + flutter run -d ios # iOS + flutter run -d android # Android + flutter run -d macos # macOS + flutter run -d windows # Windows + flutter run -d linux # Linux + ``` ### Building for Production #### Android (APK) + ```bash flutter build apk --release # Output: build/app/outputs/flutter-apk/app-release.apk ``` #### iOS (IPA) + ```bash flutter build ios --release # Then use Xcode to create IPA ``` #### Web + ```bash flutter build web --release # Output: build/web/ ``` #### Desktop + ```bash # macOS flutter build macos --release @@ -184,12 +199,14 @@ Hot-restart or rebuild after adding files so Flutter picks up new bundle entries ### Audio Files Audio files should be placed in `assets/audio/` with the naming convention: + - `day{n}_zh.mp3` - Mandarin audio - `day{n}_en.mp3` - English explanations ### Text Files Text content should be in `assets/text/` with the naming convention: + - `day{n}_zh.txt` - Simplified Chinese text - `day{n}_pinyin.txt` - Pinyin romanization - `day{n}_en.txt` - English translation @@ -207,20 +224,20 @@ Version pins and exact constraints are in **`pubspec.yaml`**; run `dart pub deps Packages referenced from current **`lib/`** code include: -| Package | Role | -|---------|------| -| `provider` | `AppState` + reactive UI wiring | -| `shared_preferences` | Progress, preferences, notification settings cache | -| `audioplayers` | Bundled lesson audio playback | -| `flutter_local_notifications` | Daily reminders / completion pings | -| `timezone` | Local time handling for notification scheduling | +| Package | Role | +| ----------------------------- | -------------------------------------------------- | +| `provider` | `AppState` + reactive UI wiring | +| `shared_preferences` | Progress, preferences, notification settings cache | +| `audioplayers` | Bundled lesson audio playback | +| `flutter_local_notifications` | Daily reminders / completion pings | +| `timezone` | Local time handling for notification scheduling | UI and typography: -| Package | Role | -|---------|------| -| `google_fonts` | Fonts such as Poppins | -| `font_awesome_flutter` | Icons | +| Package | Role | +| ---------------------- | --------------------- | +| `google_fonts` | Fonts such as Poppins | +| `font_awesome_flutter` | Icons | Still listed in **`pubspec.yaml`** (verify before removing—they may become unused during refactors): @@ -281,11 +298,13 @@ dart format lib/ ## Troubleshooting ### Audio Not Playing + - Ensure audio files are in `assets/audio/` directory - Verify `pubspec.yaml` includes audio assets - Run `flutter clean && flutter pub get` ### Build Errors + ```bash flutter clean flutter pub get @@ -325,6 +344,7 @@ Copyright © 2026 Mandarin Pathways. All rights reserved. ## Support For issues, questions, or suggestions: + - Open an issue on GitHub - Check the Flutter documentation: https://docs.flutter.dev - Review the original PWA at: [original web URL] diff --git a/flutter_app/analysis_options.yaml b/flutter_app/analysis_options.yaml index 0d29021..9a7b9b0 100644 --- a/flutter_app/analysis_options.yaml +++ b/flutter_app/analysis_options.yaml @@ -10,19 +10,19 @@ include: package:flutter_lints/flutter.yaml linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at https://dart.dev/lints. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. - rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options diff --git a/flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index d36b1fa..f1e3e62 100644 --- a/flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,122 +1,122 @@ { - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" + "images": [ + { + "size": "20x20", + "idiom": "iphone", + "filename": "Icon-App-20x20@2x.png", + "scale": "2x" + }, + { + "size": "20x20", + "idiom": "iphone", + "filename": "Icon-App-20x20@3x.png", + "scale": "3x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "Icon-App-29x29@1x.png", + "scale": "1x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "Icon-App-29x29@2x.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "iphone", + "filename": "Icon-App-29x29@3x.png", + "scale": "3x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "Icon-App-40x40@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "iphone", + "filename": "Icon-App-40x40@3x.png", + "scale": "3x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "Icon-App-60x60@2x.png", + "scale": "2x" + }, + { + "size": "60x60", + "idiom": "iphone", + "filename": "Icon-App-60x60@3x.png", + "scale": "3x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "Icon-App-20x20@1x.png", + "scale": "1x" + }, + { + "size": "20x20", + "idiom": "ipad", + "filename": "Icon-App-20x20@2x.png", + "scale": "2x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "Icon-App-29x29@1x.png", + "scale": "1x" + }, + { + "size": "29x29", + "idiom": "ipad", + "filename": "Icon-App-29x29@2x.png", + "scale": "2x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "Icon-App-40x40@1x.png", + "scale": "1x" + }, + { + "size": "40x40", + "idiom": "ipad", + "filename": "Icon-App-40x40@2x.png", + "scale": "2x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "Icon-App-76x76@1x.png", + "scale": "1x" + }, + { + "size": "76x76", + "idiom": "ipad", + "filename": "Icon-App-76x76@2x.png", + "scale": "2x" + }, + { + "size": "83.5x83.5", + "idiom": "ipad", + "filename": "Icon-App-83.5x83.5@2x.png", + "scale": "2x" + }, + { + "size": "1024x1024", + "idiom": "ios-marketing", + "filename": "Icon-App-1024x1024@1x.png", + "scale": "1x" + } + ], + "info": { + "version": 1, + "author": "xcode" } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } } diff --git a/flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json index 0bedcf2..dcd5980 100644 --- a/flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ b/flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -1,23 +1,23 @@ { - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" + "images": [ + { + "idiom": "universal", + "filename": "LaunchImage.png", + "scale": "1x" + }, + { + "idiom": "universal", + "filename": "LaunchImage@2x.png", + "scale": "2x" + }, + { + "idiom": "universal", + "filename": "LaunchImage@3x.png", + "scale": "3x" + } + ], + "info": { + "version": 1, + "author": "xcode" } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } } diff --git a/flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md index 89c2725..b5b843a 100644 --- a/flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ b/flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -2,4 +2,4 @@ You can customize the launch screen with your own desired assets by replacing the image files in this directory. -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. diff --git a/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index a2ec33f..6e00ef8 100644 --- a/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,68 +1,68 @@ { - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" + "images": [ + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_16.png", + "scale": "1x" + }, + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "2x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "1x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_64.png", + "scale": "2x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_128.png", + "scale": "1x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "2x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "1x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "2x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "1x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_1024.png", + "scale": "2x" + } + ], + "info": { + "version": 1, + "author": "xcode" } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } } diff --git a/flutter_app/pubspec.yaml b/flutter_app/pubspec.yaml index 8ed62bf..c96b18b 100644 --- a/flutter_app/pubspec.yaml +++ b/flutter_app/pubspec.yaml @@ -3,46 +3,46 @@ description: Learn Mandarin Chinese with structured daily lessons and supplement version: 1.0.0+1 environment: - sdk: '>=3.10.0 <4.0.0' + sdk: ">=3.10.0 <4.0.0" dependencies: - flutter: - sdk: flutter + flutter: + sdk: flutter - # UI & Design - cupertino_icons: ^1.0.6 - google_fonts: ^8.1.0 - font_awesome_flutter: ^11.0.0 + # UI & Design + cupertino_icons: ^1.0.6 + google_fonts: ^8.1.0 + font_awesome_flutter: ^11.0.0 - # State Management - provider: ^6.1.1 + # State Management + provider: ^6.1.1 - # Local Storage - shared_preferences: ^2.2.2 - path_provider: ^2.1.1 + # Local Storage + shared_preferences: ^2.2.2 + path_provider: ^2.1.1 - # Audio Playback - audioplayers: ^6.6.0 + # Audio Playback + audioplayers: ^6.6.0 - # Networking - http: ^1.1.2 + # Networking + http: ^1.1.2 - # Notifications - flutter_local_notifications: ^21.0.0 - timezone: ^0.11.0 + # Notifications + flutter_local_notifications: ^21.0.0 + timezone: ^0.11.0 - # Utilities - url_launcher: ^6.2.2 + # Utilities + url_launcher: ^6.2.2 dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^6.0.0 + flutter_test: + sdk: flutter + flutter_lints: ^6.0.0 flutter: - uses-material-design: true + uses-material-design: true - assets: - - assets/audio/ - - assets/icons/ - - assets/text/ + assets: + - assets/audio/ + - assets/icons/ + - assets/text/ diff --git a/flutter_app/web/index.html b/flutter_app/web/index.html index fe0aaed..331958a 100644 --- a/flutter_app/web/index.html +++ b/flutter_app/web/index.html @@ -1,7 +1,7 @@ - + - - - + - - - + + + - - - - - + + + + + - - + + - mandarin_pathways - - - - - + mandarin_pathways + + + + + diff --git a/index.html b/index.html index f78384e..c309e19 100644 --- a/index.html +++ b/index.html @@ -1,496 +1,1118 @@ - + - - - - - - - - - Mandarin Pathways - Your Journey to Chinese Language Mastery - - - - - - - - - - -
-
- - - -
-

Mandarin Pathways

-

开启您的中文学习之旅Your Journey to Chinese Language Mastery

- -
+ + + + + + + + + + Mandarin Pathways - Your Journey to Chinese Language Mastery + + + + + + + + + + + +
+
+ + + +
+

Mandarin Pathways

+

+ 开启您的中文学习之旅Your Journey to Chinese Language Mastery +

+ +
-
-
-

掌握中文Master Mandarin Chinese

-

通过40天全面的中文学习,开启全球机遇Embark on a 40-day journey through comprehensive Mandarin learning for global opportunities

-
-
+
+
+

+ 掌握中文Master Mandarin Chinese +

+

+ 通过40天全面的中文学习,开启全球机遇Embark on a 40-day journey through comprehensive + Mandarin learning for global opportunities +

+
+
-
- -
- -
-
-
-
-

- 🇨🇳 - 简体中文 -

-
-
-
-

进度:第0/40天Progress: Day 0/40

-
-
-
+
+ +
+ +
+
+
+
+

+ 🇨🇳 + 简体中文 +

-
-
-
40
-
Days
+
+
+

+ 进度:第0/40天Progress: Day 0/40 +

+
+
+
-
-
5
-
级别Levels
+
+
+
40
+
+ Days +
+
+
+
5
+
+ 级别Levels +
+
-
-
-
-

- 🔤 - Pinyin -

-
-
-
-

进度:第0/40天Progress: Day 0/40

-
-
-
+
+
+

+ 🔤 + Pinyin +

-
-
-
40
-
Days
+
+
+

+ 进度:第0/40天Progress: Day 0/40 +

+
+
+
-
-
5
-
级别Levels
+
+
+
40
+
+ Days +
+
+
+
5
+
+ 级别Levels +
+
-
-
-
-

- 🇺🇸 - English -

-
-
-
-

进度:第0/40天Progress: Day 0/40

-
-
-
+
+
+

+ 🇺🇸 + English +

-
-
-
40
-
Days
+
+
+

+ 进度:第0/40天Progress: Day 0/40 +

+
+
+
-
-
5
-
级别Levels
+
+
+
40
+
+ Days +
+
+
+
5
+
+ 级别Levels +
+
-
- - -
- -
- -
-

课程结构Course Structure

-
-
-

第1-7天Days 1-7

-

拼音系统、声调和基本发音Pinyin system, tones, and pronunciation basics

-
-
-

第8-14天Days 8-14

-

基本日常用语和基础语法Essential daily phrases and basic grammar

-
-
-

第15-22天Days 15-22

-

文化背景和日常生活交流Cultural context and daily life communication

-
-
-

第23-30天Days 23-30

-

职业和商务中文Professional and business Mandarin

-
-
-

第31-40天Days 31-40

-

高级流利度和实际应用Advanced fluency and real-world applications

+
+
+

+ 学习仪表盘Learning Dashboard +

+
+
+ 0 + 连续学习天数Day streak +
+
+ 0 + 今日复习卡片Cards due today +
+
+ -- + 推荐起点Recommended start +
+
+ +
    -
    -
    + - -
    + +
    -
    -

    每日课程Daily Lessons

    + +
    - -
    - -
    Days 1-10
    - -
    +
    +

    + 课程结构Course Structure +

    +
    +
    +

    + + 第1-7天Days 1-7 +

    +

    + 拼音系统、声调和基本发音Pinyin system, tones, and pronunciation + basics +

    +
    +
    +

    + + 第8-14天Days 8-14 +

    +

    + 基本日常用语和基础语法Essential daily phrases and basic grammar +

    +
    +
    +

    + + 第15-22天Days 15-22 +

    +

    + 文化背景和日常生活交流Cultural context and daily life + communication +

    +
    +
    +

    + + 第23-30天Days 23-30 +

    +

    + 职业和商务中文Professional and business Mandarin +

    +
    +
    +

    + + 第31-40天Days 31-40 +

    +

    + 高级流利度和实际应用Advanced fluency and real-world + applications +

    +
    +
    +
    -
    - -
    -
    + +
    - -
    +
    +

    + 每日课程Daily Lessons +

    - -
    -

    核心语言技能Core Language Skills

    -

    通过这些专项练习增强您的日常学习,提高阅读和写作能力。Enhance your daily learning with focused practice on these essential language skills.

    - -
    + +
    + +
    + Days 1-10 +
    + +
    - -
    +
    + +
    +
    - -
    -

    补充学习材料Supplementary Learning Materials

    - -
    + +
    - -
    + +
    +

    + 核心语言技能Core Language Skills +

    +

    + 通过这些专项练习增强您的日常学习,提高阅读和写作能力。Enhance your daily learning with focused practice on + these essential language skills. +

    + +
    - -
    + +
    -
    - -
    -

    为什么学习中文?Why Learn Mandarin Chinese?

    -
    -
    -

    全球影响力Global Reach & Impact

    -
      -
    • 母语使用者:Native Speakers: 全球超过10亿Over 1 billion worldwide
    • -
    • 商业:Business: 中国巨大的经济影响力China's massive economic influence
    • -
    • 科技:Technology: 主要科技创新中心Major tech innovation hub
    • -
    • 文化:Culture: 五千年丰富文化遗产Rich 5000-year cultural heritage
    • -
    • 未来:Future: 日益增长的全球重要性Growing global significance
    • -
    -
    -
    -

    职业优势Career Advantages

    -
      -
    • 国际商务机会International business opportunities
    • -
    • 科技领域合作Tech sector collaboration
    • -
    • 进出口优势Import/export advantages
    • -
    • 外交和政府职位Diplomatic and government positions
    • -
    • 跨文化咨询Cross-cultural consulting
    • -
    -
    -
    -

    商业潜力Business Potential

    -
      -
    • 进入中国市场Access to Chinese market
    • -
    • 电子商务机会E-commerce opportunities
    • -
    • 翻译服务Translation services
    • -
    • 语言教学Language teaching
    • -
    • 文化咨询Cultural consulting
    • -
    -
    -
    -

    认知优势Cognitive Benefits

    -
      -
    • 通过汉字学习增强记忆力Enhanced memory through character learning
    • -
    • 提高模式识别能力Improved pattern recognition
    • -
    • 更好的多任务处理能力Better multitasking abilities
    • -
    • 更强的问题解决能力Stronger problem-solving skills
    • -
    -
    -
    -

    文化接触Cultural Access

    -
      -
    • 理解中国哲学Understand Chinese philosophy
    • -
    • 欣赏中国艺术Appreciate Chinese arts
    • -
    • 与当地社区联系Connect with local communities
    • -
    • 自信地探索Navigate with confidence
    • -
    -
    -
    -
    + +
    -
    -

    © 2026 Mandarin Pathways | 赋能语言学习Empowering Language Learning

    -
    + +
    +
    + +
    +

    + 为什么学习中文?Why Learn Mandarin Chinese? +

    +
    +
    +

    + + 全球影响力Global Reach & Impact +

    +
      +
    • + 母语使用者:Native Speakers: + 全球超过10亿Over 1 billion worldwide +
    • +
    • + 商业:Business: + 中国巨大的经济影响力China's massive economic influence +
    • +
    • + 科技:Technology: + 主要科技创新中心Major tech innovation hub +
    • +
    • + 文化:Culture: + 五千年丰富文化遗产Rich 5000-year cultural heritage +
    • +
    • + 未来:Future: + 日益增长的全球重要性Growing global significance +
    • +
    +
    +
    +

    + + 职业优势Career Advantages +

    +
      +
    • + 国际商务机会International business opportunities +
    • +
    • + 科技领域合作Tech sector collaboration +
    • +
    • + 进出口优势Import/export advantages +
    • +
    • + 外交和政府职位Diplomatic and government positions +
    • +
    • + 跨文化咨询Cross-cultural consulting +
    • +
    +
    +
    +

    + + 商业潜力Business Potential +

    +
      +
    • + 进入中国市场Access to Chinese market +
    • +
    • + 电子商务机会E-commerce opportunities +
    • +
    • + 翻译服务Translation services +
    • +
    • + 语言教学Language teaching +
    • +
    • + 文化咨询Cultural consulting +
    • +
    +
    +
    +

    + + 认知优势Cognitive Benefits +

    +
      +
    • + 通过汉字学习增强记忆力Enhanced memory through character + learning +
    • +
    • + 提高模式识别能力Improved pattern recognition +
    • +
    • + 更好的多任务处理能力Better multitasking abilities +
    • +
    • + 更强的问题解决能力Stronger problem-solving skills +
    • +
    +
    +
    +

    + + 文化接触Cultural Access +

    +
      +
    • + 理解中国哲学Understand Chinese philosophy +
    • +
    • + 欣赏中国艺术Appreciate Chinese arts +
    • +
    • + 与当地社区联系Connect with local communities +
    • +
    • + 自信地探索Navigate with confidence +
    • +
    +
    +
    +

    + + 书写系统Writing Systems +

    +
    +
    + 简体中文Simplified Chinese + 中国大陆标准Mainland China standard +
    +
    + 繁体中文Traditional Chinese + 台湾和香港使用Taiwan & Hong Kong usage +
    +
    + 拼音系统Pinyin System + 语音学习辅助Phonetic learning aid +
    +
    + 汉字书写Character Writing + 文化欣赏Cultural appreciation +
    +
    +
    +
    +
    +
    - - + - -