Compare commits

30 Commits

Author SHA1 Message Date
Kunthawat Greethong
c20883cb4f fix: override .page-section white text in legal pages via global.css
Astro scoped CSS cannot override global rules. Added overrides
in global.css for .legal-section, .legal-rail, .legal-content.
2026-07-01 14:50:42 +07:00
Kunthawat Greethong
58162354c6 fix: override .page-section white text in legal pages 2026-07-01 14:43:37 +07:00
Kunthawat Greethong
fd6b70dbf2 fix: switch blog pagination to client-side
Astro 6 getStaticPaths+paginate has issues with non-dynamic pages.
Client-side pagination works fine for small blog volumes.
2026-07-01 14:39:57 +07:00
Kunthawat Greethong
a7a2724c09 fix: use black text color in privacy and terms pages 2026-07-01 14:36:40 +07:00
Kunthawat Greethong
ab3ca64ec9 feat: add pagination to blog listing (12 posts per page)
- Use Astro's paginate() with pageSize: 12
- Add prev/next navigation with page counter
- Generate /blog/, /blog/2/, /blog/3/ etc.
2026-07-01 14:35:55 +07:00
Kunthawat Greethong
28d2be03f0 feat: publish 3 articles from today's news digest
1. ChatGPT เลือกแหล่งข้อมูลยังไง? (Source Selection - GEO)
2. ChatGPT Instant vs Thinking Mode 75% ต่างกัน (Reasoning Modes)
3. DeepSeek DSpark + Devin Fusion: ลดต้นทุน AI 85% (Cost Revolution)

Categories: SEO (2), AI & Marketing (1)
Images: 9 (3 featured + 6 inline)
Source: news digest 2026-07-01
2026-07-01 14:25:11 +07:00
Kunthawat Greethong
abafd4015f fix: add missing site.webmanifest 2026-07-01 13:01:45 +07:00
Kunthawat Greethong
8f03e6af69 fix: correct GA4 Measurement ID to G-74BHREDLC3 2026-07-01 12:53:11 +07:00
Kunthawat Greethong
94a1325f1f fix: add is:inline to tracking scripts for cookie scanner detection
Astro bundles inline scripts as type=module which cookie scanners
cannot detect. is:inline forces them to render as type=text/javascript.
2026-07-01 12:48:54 +07:00
Kunthawat Greethong
0c31f800cf feat: add GA4, Meta Pixel, Umami, and ConsentOS tracking scripts
- GA4: G-397993276
- Meta Pixel: 418349260078648
- Umami: self-hosted at umami.moreminimore.com
- ConsentOS: cookie consent loader
2026-07-01 12:06:04 +07:00
Kunthawat Greethong
14aac31916 fix: use verified email for SES sandbox mode 2026-07-01 11:49:57 +07:00
Kunthawat Greethong
8aae25ef4f fix: correct nodemailer SES transport config for v9 API
SES transport expects { sesClient, SendEmailCommand }, not { sesv2, ses }
2026-07-01 11:35:04 +07:00
Kunthawat Greethong
eb785a53aa cleanup: remove blog-article-lead, remove 4 mock-up articles
- Remove .blog-article-lead from article detail and listing pages
- Delete 4 stub articles: sme-website-next-action, marketing-automation-reduce-work, ads-not-worth-it, ai-for-sme-start-with-pain
- Clean up unused CSS
2026-07-01 11:34:09 +07:00
Kunthawat Greethong
33043260a9 fix: migrate to @aws-sdk/client-sesv2 for nodemailer v9 compatibility 2026-07-01 11:19:09 +07:00
Kunthawat Greethong
b75ed98804 fix: add @rollup/rollup-linux-x64-gnu as optionalDependency for Docker build 2026-07-01 11:01:31 +07:00
Kunthawat Greethong
4c7531ddc5 fix: use npm ci without --omit=dev to install rollup native binaries 2026-07-01 10:58:43 +07:00
Kunthawat Greethong
4fbeb01003 Add Dockerfile: Astro build + Express + SES 2026-07-01 10:34:28 +07:00
Kunthawat Greethong
236edb5f9f Fix blog feature image: use heroImage from frontmatter instead of hardcode 2026-07-01 10:24:47 +07:00
Kunthawat Greethong
54c9b381b9 feat: publish 14 blog articles from vault research
- 14 new Thai-language blog posts covering AI, SEO, B2B, and Tools
- 66 images (featured + inline) for all articles
- Categories: AI & Marketing, SEO, B2B, Tools
- Sources: arXiv papers, Search Engine Journal, Marketing AI Institute, HubSpot, Neil Patel
- All articles GEO-optimized with TL;DR, FAQ, and structured data
- Social posts (FB, X, IG) created in vault for future publishing

Articles:
1. Fintech มองไม่เห็นโดย AI Agents
2. AI Brand Reputation — 167K Citations
3. AI Recommendation Multi-Industry Map
4. Google ตอบ SEO สำหรับ AI Agents
5. Google Open Knowledge Format (OKF)
6. OPID — AI Agent ฝึกตัวเองได้
7. Two Things B2B Marketer ควรทำกับ AI
8. B2B Marketers Future of Work
9. AI Visibility ≠ SEO — Operational Alignment
10. Brand Reputation Precedes You With AI
11. Broken Form Cost Agency Leads
12. ChatGPT Opens Ads for All
13. OpenKnowledge AI-First Obsidian
14. Profound vs Bluefish AEO
2026-06-30 22:51:34 +07:00
Kunthawat Greethong
c004ee6504 Add SEO+GEO foundation + blog auto JSON-LD + robots fix 2026-06-30 22:16:47 +07:00
Kunthawat Greethong
479ed4722e Improve mobile responsive layout and site content 2026-06-30 21:02:47 +07:00
Kunthawat Greethong
bb1b1ba568 feat: oversized H1 hero for all inner pages + portfolio refinements
- Add hero-oversized class (Option B) to global.css
- Apply oversized H1 to: about, services, blog, contact, faq, privacy, terms
- Portfolio page: oversized hero, new text, portfolio-design-note in section-heading
- Clean up duplicate styles from portfolio.astro scoped block
2026-06-28 17:52:56 +07:00
Kunthawat Greethong
9ebbc91e5b feat: major content & layout overhaul across all sections
- Problem strip: 4 sign cards with closing statement
- Dataroot section: rebranded as case study with new content
- Services: 2×2 content-rich cards with bullet lists, updated copy
- Process: moved above portfolio, new strategy-focused copy
- Portfolio: 9 items with correct tags, uniform cards, light theme
- SEO: OG tags, canonical, favicons, robots.txt, sitemap, 404
- Backdrop: lighter white for lead panel readability
- Footer, LegalPages component, various text refinements
- Fix CSS syntax error in neural hero hover state
2026-06-28 16:52:44 +07:00
Kunthawat Greethong
f114a34a62 Magnetic Field hero, Thai eyebrows, black text-stroke, service pages fixes
- Hero: Magnetic Field design (ripple rings, field curves, attract animations)
- H1: เปลี่ยนเป็น 'เป้าหมายของเราคือการเพิ่มกำไรให้ลูกค้า', กำไร เน้นขอบดำ
- Site: ทุก eyebrow แปลเป็นไทย
- Buttons: text สีดำทุกหน้าแม้ใน dark section
- Yellow text: เพิ่ม -webkit-text-stroke ขอบดำทุก element
- Service pages: light/light/dark pattern, process-grid แถวเดียว
- Logo: อัพเดทใหม่
- Demos: เพิ่ม 5 hero design concepts (orbital, energy flow, holographic, constellation, magnetic)
2026-06-26 11:15:58 +07:00
Kunthawat Greethong
689a8924e6 fix: Business Knowledge card visibility, spread neural cards, adjust layout
- Add position:absolute to neural-node.liquid-glass override to fix
  3D positioning (liquid-glass was overriding to position:relative)
- Spread outer cards further apart for better visual separation
- Increase container size to 600x520px to accommodate spread
- Enlarge neural cards (220px width, 130px min-height)
- Update float animations to match new positions
2026-06-24 14:20:57 +07:00
Kunthawat Greethong
2a3062357f fix: hero neural UX improvements
1. Mouse move listener now on document (not just hero section)
2. Removed hover effect on outer cards, kept only for center กำไร card
3. Bigger text: card-tag 20px, card-desc 16px
4. Hero overflow visible on desktop (cards can extend left)
5. Hero overflow clip on mobile (normal containment)
2026-06-24 09:14:55 +07:00
Kunthawat Greethong
1d893e1bcb fix: remove duplicate CSS causing style conflicts
- Removed old translateZ() CSS that was overriding new translate3d() styles
- This was causing nodes to not display in correct 3D positions
2026-06-24 09:02:19 +07:00
Kunthawat Greethong
61c2bd6924 feat: neural network hero with true 3D and dynamic lines
- True 3D positioning with translate3d(x, y, z) for each node
- Larger cards (200px width) with proper spacing
- Canvas-based dynamic connection lines
- Lines connect at card borders (edge-to-edge)
- Straight solid lines (3px, yellow, 50% opacity)
- Mouse parallax with smooth easing
- 3D perspective changes card sizes dynamically
- Mobile responsive: flat column layout
- Device orientation support for touch devices
2026-06-24 08:58:32 +07:00
Kunthawat Greethong
fdb03f6117 feat: neural network hero with 3D parallax
- Replace profit cluster with neural network visualization
- 4 liquid-glass nodes: กำไร (center), Marketing, AI, Business Knowledge
- 3D perspective with CSS preserve-3d
- Mouse parallax: scene rotates ±15deg following cursor
- Unequal distances: nodes at z: -100, -150, -200
- Floating animations for each outer node
- SVG dashed connector lines with pulse animation
- Device orientation support for mobile
- Responsive: flat column layout on mobile (≤620px)
- Smooth easing with requestAnimationFrame
2026-06-24 08:45:49 +07:00
Kunthawat Greethong
0f244424c0 feat: hero profit cluster, dark text fix, footer, privacy/terms pages
- Replace hero right panel with liquid-glass profit cluster (กำไร + Marketing/AI/Business Knowledge)
- Fix dark-on-dark text visibility in all inverted sections (scene-dark, page-section)
- Add Footer component with liquid-glass design, contact info, legal links
- Add privacy and terms placeholder pages
- Update PageShell to include Footer on all pages
2026-06-24 08:24:49 +07:00
154 changed files with 12768 additions and 1338 deletions

29
Dockerfile Normal file
View File

@@ -0,0 +1,29 @@
# ── MoreminiMore — Astro + Express + SES ─────────────────────────
# Build: npm run build → Runtime: node server.js (port 4321)
#
# Env vars:
# SES_ACCESS_KEY_ID (optional — SES email sending)
# SES_SECRET_ACCESS_KEY (optional)
# SES_REGION (default: ap-southeast-1)
# PORT (default: 4321)
FROM node:22-bookworm-slim
WORKDIR /app
# ── Dependencies ──────────────────────────────────────────────────
COPY package.json package-lock.json ./
RUN npm ci
# ── Source + Build ───────────────────────────────────────────────
COPY . .
RUN npm run build
# ── Prune build-only deps (astro is ~200MB) ──────────────────────
# Keep: express, cors, nodemailer, @aws-sdk/client-ses
RUN npm prune --omit=dev 2>/dev/null; \
npm ls --depth=0 2>/dev/null || true
# ── Runtime ──────────────────────────────────────────────────────
EXPOSE 4321
CMD ["node", "server.js"]

View File

@@ -1,21 +1,22 @@
# Plan Review Log: MoreminiMore Homepage Rebuild
# Plan Review Log: Footer Component + Remove Yellow Lines from Process Section
Act 1 (grill) complete — plan locked with the user. MAX_ROUNDS=5.
Act 1 grill complete — plan locked with user.
## Round 1 — Codex
พบ 10 issues (3 critical, 3 significant, 4 minor):
1. 🔴 PageShell layout ignored — ควรแก้ผ่าน PageShell
2. 🔴 Privacy/Terms pages ไม่มี — dead links
3. 🔴 Liquid glass markup pattern ไม่ระบุ
4. 🟡 service-proof-grid yellow accent missed
5. 🟡 Mobile layout spec ambiguous
6. 🟡 Step-flow visual cue removed without alternative
7. 🟡 Missing semantic `<footer>` element
8. 🟠 LINE URL unspecified
9. 🟠 Logo dimensions omitted
10. 🟠 z-index risk unresolved
MAX_ROUNDS=5
### Claude's response
แก้ทั้ง 10 ข้อใน PLAN.md revision
## Act 1 Summary
- User clarified credibility priority: SME understanding > measurable business value > modern UX/coding impression.
- User clarified homepage must be company-site quality, not a one-page landing page.
- User clarified visual direction: subtle macOS-inspired liquid glass, hybrid light/dark rhythm, abstract business-map parallax, restrained motion.
- User clarified implementation preference: Astro static + vanilla JS first, Google Apps Script for low-volume lead handling.
- User clarified CTA wording: use "ปรึกษาฟรี" only, not "ปรึกษาฟรี 30 นาที".
- User clarified Dealplustech must be included in portfolio using a homepage screenshot.
- User requested a copy-ready Google Apps Script file plus detailed setup instructions.
## Act 2 Status
Not started.
No implementation has been done from this plan yet.
## Round 2 — Codex
All 10 findings addressed. Minor note: footer ควรวางระหว่าง `</main>` กับ floating-cta (ไม่ใช่ inside `<main>`).
VERDICT: APPROVED (2 rounds)

418
PLAN.md
View File

@@ -1,351 +1,71 @@
# Plan: MoreminiMore Homepage Rebuild
_Locked via grill — by Codex + Kunthawat_
# Plan: Footer Component + Remove Yellow Lines from Process Section
_Locked via grill — by Claude + Kunthawat — Revised Round 1_
## Goal
Rebuild the MoreminiMore company website from a clean slate, starting with the homepage only. The homepage must make SME owners feel that MoreminiMore is a friendly growth partner who understands real business problems, uses real data before recommending work, and can deliver modern, trustworthy websites and systems. The visual experience should feel modern and a little wow through restrained liquid-glass UI, layered parallax, and polished UX, but never become an effect demo or a tech showcase.
## Core Positioning
Priority of trust:
1. MoreminiMore understands SME businesses and their real problems.
2. MoreminiMore can help clients choose work that is more efficient, worthwhile, and suitable for their business.
3. MoreminiMore writes modern code in the UX sense: smooth, beautiful, easy to use, and subtly impressive.
Target customer priority:
1. Businesses that already have a website, ads, or tools, but feel the result is not worth the money or effort.
2. Businesses that do not yet have a good website or system and want to start correctly.
3. Businesses growing into AI/automation/workflow needs.
Brand role:
1. Growth partner for SMEs.
2. Business diagnosis partner.
3. Team that can implement real working solutions.
Tone:
- Friendly-first.
- Thai-first copy.
- English only for service names and necessary terms.
- No consultant-speak, no overly technical language, no fake metrics.
## Homepage Strategy
Hero strategy:
- Belief-led headline.
- Pain-led subheadline.
- Promise-led CTA/context.
Current hero copy direction:
> ธุรกิจไม่ควรเสียเงินกับสิ่งที่ยังไม่รู้ว่าคุ้มไหม
>
> เราช่วย SME ดูข้อมูลจริงก่อนตัดสินใจทำเว็บ การตลาด AI หรือระบบอัตโนมัติ เพื่อเลือกสิ่งที่ควรทำ อย่างมีประสิทธิภาพ และเหมาะสมกับลูกค้า
The final copy can be polished, especially the phrase "เหมาะสมกับลูกค้า", but the idea should stay intact.
Primary conversion priority:
1. ส่งโจทย์ธุรกิจให้เราดูก่อน
2. ดูเคส/ผลงานก่อน แล้วค่อยติดต่อ
3. ปรึกษาฟรี
Do not use "30 นาที" in CTA copy. Use "ปรึกษาฟรี" only.
Homepage v1 must not include a blog section. Blog content can stay preserved for later, but the homepage should focus on trust, diagnosis, proof, service clarity, portfolio quality, and conversion.
## Homepage Sections
Recommended homepage order:
1. Hero
- Belief-led headline.
- Friendly explanation.
- CTA to open problem form.
- Light abstract business-map background with subtle liquid glass.
2. Problem framing
- Explain that customers do not need to know which service they need.
- MoreminiMore will map the problem to the suitable service.
- CTA opens the same slide-over form.
3. Dataroot diagnosis story
- Dark diagnosis stage.
- Split case layout.
- Left side: narrative diagnosis.
- Right side: metric cards and relevant visual/screenshot.
- Use only real Dataroot metrics:
- +373% impression
- +114.2% click
- -28.3% ad spend
4. Outcome-first service cards
- Service name is still visible in English.
- Card headline should sell the outcome/problem solved, not the service label first.
- Desktop hover reveals "เหมาะกับปัญหาแบบไหน".
- Mobile shows essential information directly.
5. Portfolio preview
- Dark gallery stage.
- Featured + grid layout.
- Featured 1 should be Jet Industries for credibility.
- Featured 2 should be selected from the most visually strong portfolio screenshot after inspecting assets.
- Dataroot does not need to be the main portfolio feature because it has the diagnosis story.
- Portfolio role is mainly visual website quality, because most clients are website clients.
6. Mini process
- Short 4-step process:
1. เข้าใจธุรกิจ
2. ดูข้อมูล
3. เลือกทางที่คุ้ม
4. ลงมือและวัดผล
7. Final CTA
- Friendly, low-friction.
- Reopen form panel.
- Reinforce "ส่งโจทย์ให้เราดูก่อน".
## Problem Form
The primary CTA opens a form, not a separate contact page.
Placement and behavior:
- Desktop: glass slide-over panel from the right.
- Mobile: bottom sheet.
- Floating/sticky CTA appears after the hero, not immediately on first paint.
- Navigation also has a CTA.
Problem chips:
1. เว็บมีอยู่แล้ว แต่ไม่ค่อยมีลูกค้าทัก
2. ยิงแอดอยู่ แต่ยอดขายไม่คุ้ม
3. มีคนทักมา แต่ไม่ใช่ลูกค้าที่ใช่
4. ทีมงานทำงานเดิม ๆ แต่ทำงานช้า หรือผิดพลาดบ่อย
5. อยากใช้ AI แต่ไม่รู้เริ่มตรงไหน
6. ยังไม่แน่ใจว่าควรแก้อะไรก่อน
Textarea:
- Must include a very short example because customers are assumed to be lazy and may not want to type.
- Example direction:
- "ยิงแอดอยู่ แต่ยอดขายไม่คุ้ม อยากรู้ว่าควรแก้อะไรก่อน"
- "มีเว็บแล้ว แต่ลูกค้าไม่ค่อยทัก อยากรู้ว่าควรแก้เว็บหรือยิงแอดก่อน"
Contact fields:
- Name.
- Phone.
- Email.
- Phone or email must be provided, but both are not required.
- Labels must be explicit because non-technical customers may be confused by a single generic "contact channel" field.
Submission backend:
- Use Google Apps Script first because lead volume is expected to be low.
- Send notification email to Gmail/Google Workspace.
- Ideally also store leads in Google Sheet.
- Sender should be MoreminiMore-owned; customer contact should be used as reply-to where possible.
- Keep message clean to reduce spam risk.
- Provide the Google Apps Script as a copy-ready file because the user has not done this setup before.
- Current script artifact: `google-apps-script/lead-form.gs`.
- Current setup guide: `google-apps-script/SETUP.md`.
Post-submit UX:
- Stay in the panel.
- Show success message plus light diagnosis preview.
- Do not pretend to generate a complete diagnosis.
- Example direction: "จากปัญหาที่เลือก เราน่าจะเริ่มจากการดูเว็บ/แอด/ขั้นตอนทำงานก่อน แล้วจะติดต่อกลับทางเบอร์หรืออีเมลที่ให้ไว้"
## Navigation
This is a company website, not a one-page landing page.
Navigation should include:
- หน้าแรก
- บริการ
- ผลงาน
- เกี่ยวกับ
- FAQ and/or บทความ
- ติดต่อ / ส่งโจทย์ให้เราดู
Services submenu:
- Compact mega menu.
- Use English service names:
- Website Development
- Marketing Consult
- Automation Workflow
- AI Consult
- Include a short Thai explanation under each service name.
- Desktop supports hover, click, and keyboard.
- Mobile uses an accordion/list.
Nav visual behavior:
- Hybrid glass sticky nav.
- On hero: transparent/light glass.
- After scroll: compact stronger glass/solid for readability.
## Visual System
Overall mode:
- Hybrid light/dark.
- Light glass for friendly SME trust.
- Dark/premium stages for Dataroot diagnosis and portfolio.
Section atmosphere:
- Light hero.
- Dark Dataroot diagnosis.
- Light service/process/form context.
- Dark portfolio gallery.
Brand assets:
- Use existing logo.
- Use `#fed400` as accent only, not as a full-page wash.
- Use red `#d4553a` sparingly if useful.
- Avoid recreating the old yellow-heavy design.
Liquid glass:
- Subtle macOS-inspired.
- Use in key moments only:
- Navigation.
- CTA/form slide-over.
- Hero card/form/selected elements.
- Dataroot diagnosis cards.
- Possibly light service cards, but not every surface.
- Must preserve readability before effect.
Background and parallax:
- Main background concept: abstract business map.
- Use lines, nodes, soft grids, flow paths, and layered decision-map visuals.
- Hero should not rely on stock imagery.
- Portfolio sections can use real portfolio screenshots as the main visual material.
Motion:
- Desktop:
- Hero mouse parallax should be noticeable enough to feel dimensional.
- Scroll-driven scene changes are allowed.
- Use exactly 3 main background scenes:
1. Hero / light business map.
2. Dataroot / dark diagnosis stage.
3. Portfolio / dark gallery stage.
- Mobile:
- No mouse interaction.
- Use scroll parallax/reveal only.
- Keep performance conservative.
## Portfolio Rules
Portfolio role:
- Show website visual quality and execution.
- Most clients are website clients, not consulting clients.
- Consulting-heavy proof is mainly Dataroot.
- Dealplustech must be included in the website portfolio.
- URL: `https://www.dealplustech.co.th`
- Capture a homepage screenshot and use it as a portfolio image, matching the treatment of other portfolio clients.
- Dealplustech is both website work and consulting-adjacent, but should still appear as a website portfolio item.
Client context:
- Largest client: Jet Industries.
- Second largest: Dataroot.
- Most other clients are smaller.
Sorting priority:
1. Visual quality / modern feel.
2. Professional credibility.
3. SME relatability.
Featured portfolio:
- Featured 1: Jet Industries.
- Featured 2: choose visually strongest work after inspecting assets.
- Other projects in grid.
## Technical Approach
Stack:
- Astro static, rebuilt from scratch.
- Vanilla JS first.
- CSS first for liquid glass and layout.
- Use small vanilla scripts for:
- Mouse parallax.
- Scroll scene transitions.
- Slide-over/bottom sheet form.
- Form validation and submission.
- Do not use React by default.
- Add React only if a specific interaction becomes too complex for readable vanilla JS.
Implementation scope:
- Homepage-first.
- Do not build full route details in v1.
- Navigation may reference future routes, but implementation focus is homepage.
- Preserve extracted inputs in `redesign-input/`.
- Old code remains archived in `_archive/pre-redesign-2026-06-21/`.
Suggested initial implementation files after plan approval:
1. Recreate minimal Astro project files at root:
- `package.json`
- `astro.config.mjs`
- `tsconfig.json`
- `src/pages/index.astro`
- `src/styles/global.css`
- `src/scripts/home.js`
2. Copy required assets from `redesign-input/assets/` into `public/`.
3. Build homepage components either inline first or split only when repeated:
- Navigation
- Hero
- Problem CTA panel
- Dataroot case
- Service cards
- Portfolio preview
- Process
- Footer
4. Add Google Apps Script endpoint configuration as an environment/runtime variable if possible.
## Key Decisions & Tradeoffs
- Problem-first homepage, but service cards still exist because this is a company website.
- Friendly-first copy, but visual system must remain premium enough for Jet/Dataroot credibility.
- Use English service names in nav for professionalism, but Thai copy everywhere else for clarity.
- Use Google Apps Script before heavier transactional email infrastructure because lead volume is expected to be low.
- Use Astro static + vanilla JS to keep the site light and maintainable.
- Use effects only in high-impact places to avoid repeating the previous failure mode of design overwhelming the business message.
## Risks / Open Questions
- The exact visual quality of portfolio assets must be inspected before choosing Featured 2.
- Dealplustech needs a homepage screenshot before implementation can finalize portfolio assets.
- Google Apps Script deliverability depends on the Google account/domain setup. SPF/DKIM/DMARC should be checked before production.
- Navigation points to company-site routes, but homepage is the first implementation scope. Decide whether future routes should be placeholders, omitted, or added later.
- Need final decision on exact final hero copy during implementation polish.
## Out Of Scope For Homepage V1
- Full services pages.
- Full portfolio detail pages.
- Blog section on homepage.
- Full blog redesign.
- Heavy WebGL/canvas animation.
- Full React app or SPA rewrite.
- Invented metrics or testimonials.
- Reusing old design or old code structure as the main foundation.
สร้าง Footer component ใหม่ด้วย liquid glass style และลบเส้นสีเหลืองออกจาก How we work section
## Approach
### 1. ลบเส้นสีเหลืองจาก Process Section (`src/styles/global.css`)
- ลบ `.process-grid::before` (เส้นแนวนอนสีเหลือง) → `display: none`
- ลบ `.process-grid article::after` (วงกลม ">" สีเหลืองระหว่าง step) → `display: none`
- `.process-grid .step-number`: เปลี่ยน `background: var(--yellow)``background: rgb(255 255 255 / .5)` + ลบ yellow box-shadow
- `.service-proof-grid .process-grid .step-number`: override สีเหลืองเหมือนกัน (scope เพิ่ม)
- **หมายเหตุ:** step numbers (01, 02, 03, 04) ยังคงแสดง sequential flow อยู่แล้ว ไม่ต้องเพิ่ม connector แทน
### 2. สร้าง Footer Component (`src/components/Footer.astro`)
- ใช้ `<footer>` semantic element
- ใช้ liquid glass style (ต้องมี 3 child divs):
```html
<footer class="site-footer liquid-glass liquidGlass-wrapper">
<div class="liquidGlass-effect" aria-hidden="true"></div>
<div class="liquidGlass-tint" aria-hidden="true"></div>
<div class="liquidGlass-shine" aria-hidden="true"></div>
<!-- content -->
</footer>
```
- `position: relative; z-index: auto` (ไม่ทับ lead-panel z-index: 110)
### 3. เนื้อหา Footer
**ซ้าย:**
- โลโก้: `/images/logos/logo-long-black.png` (width="205" height="36")
- คำอธิบาย: "ที่ปรึกษาเว็บไซต์ การตลาด และ AI สำหรับ SME"
**กลาง:**
- ลิงก์: หน้าแรก / บริการ / ผลงาน / บล็อก / ติดต่อ / นโยบายความเป็นส่วนตัว / เงื่อนไขการใช้งาน
**ขวา:**
- LINE: @moreminimore (link: https://line.me/ti/p/@moreminimore)
- Email: contact@moreminimore.com
**ล่าง:**
- Copyright: © {new Date().getFullYear()} MoreminiMore
### 4. Responsive
- Desktop (>768px): 3 columns (grid-template-columns: 1fr 1fr 1fr)
- Mobile (≤768px): vertical stack (flex-direction: column) — เรียง: โลโก้ → ลิงก์ → ติดต่อ → copyright
### 5. Integration Strategy
- **PageShell pages** (about, services, blog, blog/[slug], contact, faq, portfolio, services/[slug]):
เพิ่ม `<Footer />` import + render ใน `src/components/PageShell.astro` ก่อน `</main>` หรือก่อน `</body>`
- **index.astro** (standalone, ไม่ใช้ PageShell):
เพิ่ม `<Footer />` import + render ก่อน `</body>` โดยตรง
### 6. สร้าง Placeholder Pages
- สร้าง `src/pages/privacy.astro` (placeholder — "นโยบายความเป็นส่วนตัว กำลังอัพเดท")
- สร้าง `src/pages/terms.astro` (placeholder — "เงื่อนไขการใช้งาน กำลังอัพเดท")
- ใช้ PageShell layout เหมือนหน้าอื่น
## Key decisions & tradeoffs
- ใช้ liquid glass style เดียวกับ navbar เพื่อความ cohesive
- ลบเส้นเหลืองทั้งหมด — step numbers ยังบ่งบอกลำดับอยู่
- ใช้ dynamic year (`new Date().getFullYear()`) ใน copyright (Astro SSG → build-time inlined)
- Footer ใน PageShell = แก้จุดเดียว ครอบคลุม 8 หน้า
- z-index: auto (ไม่ทับ lead-panel)
## Risks / open questions
- Privacy/Terms pages เป็น placeholder — ต้องเพิ่ม content ทีหลัง
- Logo อาจต้องเปลี่ยนเป็นสีขาวเมื่ออยู่บนพื้นเข้ม (invert)
## Out of scope
- ไม่แก้ไขหน้า Privacy/Terms content จริง
- ไม่เพิ่ม social media icons (Facebook/X/LinkedIn)

View File

@@ -2,6 +2,7 @@
import { defineConfig } from 'astro/config';
export default defineConfig({
site: 'https://moreminimore.com',
output: 'static',
image: { layout: 'constrained', responsiveStyles: true },
devToolbar: { enabled: false },

4646
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -10,9 +10,17 @@
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"start": "node server.js",
"astro": "astro"
},
"dependencies": {
"astro": "^6.2.2"
"@aws-sdk/client-sesv2": "^3.1077.0",
"astro": "^6.2.2",
"cors": "^2.8.6",
"express": "^5.2.1",
"nodemailer": "^9.0.1"
},
"optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "^4.62.2"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

182
public/demos/a-orbital.html Normal file
View File

@@ -0,0 +1,182 @@
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>A: Orbital — ระบบดาวเคราะห์</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Kanit', system-ui, sans-serif;
background: #0a0f1a;
color: #fff;
display: flex; align-items: center; justify-content: center;
min-height: 100vh; overflow: hidden;
}
@import url('https://fonts.googleapis.com/css2?family=Kanit:wght@400;600;800;900&display=swap');
.demo { position: relative; width: 650px; height: 580px; }
/* Canvas for orbital rings */
canvas#orbitalCanvas {
position: absolute; inset: 0; pointer-events: none; z-index: 1;
}
.scene {
position: relative; width: 100%; height: 100%;
transform-style: preserve-3d;
transition: transform 0.2s ease-out;
}
.node {
position: absolute; border-radius: 50%;
display: flex; flex-direction: column;
align-items: center; justify-content: center;
text-align: center; backface-visibility: hidden;
}
/* Center Sun */
.sun {
left: 50%; top: 50%;
transform: translate(-50%, -50%);
width: 170px; height: 170px;
background: radial-gradient(circle at 40% 35%, #fff7cc, #fed400 40%, #d4a000 100%);
box-shadow: 0 0 60px rgba(254,212,0,0.6), 0 0 120px rgba(254,212,0,0.3), 0 0 200px rgba(254,180,0,0.15);
z-index: 10; animation: sunPulse 3s ease-in-out infinite;
}
@keyframes sunPulse {
0%, 100% { box-shadow: 0 0 60px rgba(254,212,0,0.6), 0 0 120px rgba(254,212,0,0.3), 0 0 200px rgba(254,180,0,0.15); }
50% { box-shadow: 0 0 80px rgba(254,212,0,0.8), 0 0 150px rgba(254,212,0,0.4), 0 0 230px rgba(254,180,0,0.2); }
}
.sun .label { font-size: 2.8rem; font-weight: 900; color: #3a2e00; }
.sun .sub { font-size: 0.75rem; color: #6b5500; margin-top: 4px; font-weight: 600; }
/* Orbiting planets */
.planet {
width: 110px; height: 110px;
background: rgba(255,255,255,0.06);
backdrop-filter: blur(10px);
border: 1.5px solid rgba(255,255,255,0.2);
box-shadow: 0 8px 32px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.15);
left: 50%; top: 50%;
animation: orbit var(--orbit-dur) linear infinite;
z-index: 5;
}
.planet:nth-child(2) { --orbit-dur: 12s; --radius: 230px; --angle: 0deg; --z: -40px; }
.planet:nth-child(3) { --orbit-dur: 15s; --radius: 280px; --angle: 120deg; --z: -70px; }
.planet:nth-child(4) { --orbit-dur: 18s; --radius: 330px; --angle: 240deg; --z: -100px; }
@keyframes orbit {
0% { transform: translate(-50%, -50%) rotate(0deg) translateX(var(--radius)) rotate(0deg); }
100% { transform: translate(-50%, -50%) rotate(360deg) translateX(var(--radius)) rotate(-360deg); }
}
.planet .tag { font-size: 1rem; font-weight: 800; color: #fff; text-transform: uppercase; }
.planet .desc { font-size: 0.7rem; color: rgba(255,255,255,0.6); margin-top: 4px; }
/* Connecting lines (drawn on canvas) */
.title {
position: fixed; top: 24px; left: 50%; transform: translateX(-50%);
font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.2em;
color: rgba(255,255,255,0.4); z-index: 100;
}
</style>
</head>
<body>
<span class="title">A. Orbital — ระบบดาวเคราะห์โคจร</span>
<div class="demo">
<canvas id="orbitalCanvas"></canvas>
<div class="scene" id="scene">
<div class="node sun" data-node="center">
<span class="label">กำไร</span>
<span class="sub">เป้าหมายของทุกธุรกิจ</span>
</div>
<div class="node planet" data-node="mkt">
<span class="tag">Marketing</span>
<span class="desc">เพิ่มรายได้</span>
</div>
<div class="node planet" data-node="ai">
<span class="tag">AI</span>
<span class="desc">ลดต้นทุนและเวลา</span>
</div>
<div class="node planet" data-node="biz">
<span class="tag">Business<br>Knowledge</span>
<span class="desc">ลดความเสี่ยง</span>
</div>
</div>
</div>
<script>
const canvas = document.getElementById('orbitalCanvas');
const ctx = canvas.getContext('2d');
const scene = document.getElementById('scene');
function resize() {
const rect = canvas.parentElement.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = rect.height;
}
resize();
window.addEventListener('resize', resize);
function draw() {
const rect = canvas.parentElement.getBoundingClientRect();
ctx.clearRect(0, 0, canvas.width, canvas.height);
const sun = document.querySelector('[data-node="center"]');
const planets = document.querySelectorAll('.planet');
if (!sun) return;
const sunRect = sun.getBoundingClientRect();
const sx = sunRect.left + sunRect.width/2 - rect.left;
const sy = sunRect.top + sunRect.height/2 - rect.top;
// Draw orbital rings
const sr = 85; // sun radius
const radii = [230, 280, 330];
radii.forEach((r, i) => {
ctx.beginPath();
ctx.ellipse(sx, sy, r, r * 0.45, 0, 0, Math.PI * 2);
ctx.strokeStyle = `rgba(254,212,0,${0.15 - i * 0.03})`;
ctx.lineWidth = 1;
ctx.setLineDash([8, 14]);
ctx.stroke();
ctx.setLineDash([]);
});
// Draw connection lines
planets.forEach(p => {
const pRect = p.getBoundingClientRect();
const px = pRect.left + pRect.width/2 - rect.left;
const py = pRect.top + pRect.height/2 - rect.top;
const dx = px - sx, dy = py - sy;
const dist = Math.sqrt(dx*dx + dy*dy);
const r = 85;
ctx.beginPath();
ctx.moveTo(sx + (dx/dist)*r, sy + (dy/dist)*r);
ctx.lineTo(px - (dx/dist)*55, py - (dy/dist)*55);
const grad = ctx.createLinearGradient(sx, sy, px, py);
grad.addColorStop(0, 'rgba(254,212,0,0.7)');
grad.addColorStop(1, 'rgba(254,212,0,0.15)');
ctx.strokeStyle = grad;
ctx.lineWidth = 1.5;
ctx.stroke();
});
requestAnimationFrame(draw);
}
// Mouse parallax
document.addEventListener('mousemove', e => {
const x = (e.clientX / window.innerWidth - 0.5) * 8;
const y = (e.clientY / window.innerHeight - 0.5) * -8;
scene.style.transform = `rotateX(${y}deg) rotateY(${x}deg)`;
});
draw();
</script>
</body>
</html>

View File

@@ -0,0 +1,180 @@
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>B: Energy Flow — กระแสพลังงาน</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Kanit:wght@400;600;800;900&display=swap');
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Kanit', system-ui, sans-serif;
background: #080d14;
color: #fff; overflow: hidden;
display: flex; align-items: center; justify-content: center; min-height: 100vh;
}
.demo { position: relative; width: 650px; height: 580px; }
canvas#flowCanvas { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.scene {
position: relative; width: 100%; height: 100%;
transform-style: preserve-3d;
transition: transform 0.3s ease-out;
}
.node {
position: absolute;
display: flex; flex-direction: column;
align-items: center; justify-content: center; text-align: center;
}
/* Core */
.core {
left: 50%; top: 50%;
transform: translate(-50%, -50%);
width: 160px; height: 160px; border-radius: 50%;
background: radial-gradient(circle at 35% 30%, #2a1a00, #100800);
border: 2px solid rgba(254,212,0,0.8);
box-shadow: 0 0 40px rgba(254,212,0,0.3), 0 0 80px rgba(254,180,0,0.1), inset 0 0 40px rgba(254,212,0,0.08);
z-index: 10;
animation: coreBeat 2s ease-in-out infinite;
}
@keyframes coreBeat {
0%, 100% { box-shadow: 0 0 40px rgba(254,212,0,0.3), 0 0 80px rgba(254,180,0,0.1), inset 0 0 40px rgba(254,212,0,0.08); }
50% { box-shadow: 0 0 60px rgba(254,212,0,0.5), 0 0 100px rgba(254,180,0,0.2), inset 0 0 50px rgba(254,212,0,0.12); }
}
.core .label { font-size: 2.6rem; font-weight: 900; color: #fed400; }
.core .sub { font-size: 0.7rem; color: rgba(254,212,0,0.5); margin-top: 4px; font-weight: 600; }
/* Satellites */
.satellite {
border-radius: 50%;
background: rgba(255,255,255,0.04);
backdrop-filter: blur(8px);
border: 1px solid rgba(254,212,0,0.25);
width: 120px; height: 120px;
z-index: 5;
left: 50%; top: 50%;
}
.satellite:nth-child(2) { transform: translate(-50%, -50%) translateY(-230px); animation: float1 4s ease-in-out infinite; }
.satellite:nth-child(3) { transform: translate(-50%, -50%) translateX(220px) translateY(10px); animation: float2 4.5s ease-in-out infinite; }
.satellite:nth-child(4) { transform: translate(-50%, -50%) translateX(-160px) translateY(190px); animation: float3 5s ease-in-out infinite; }
@keyframes float1 { 0%,100%{ transform: translate(-50%,-50%) translateY(-230px); } 50%{ transform: translate(-50%,-50%) translateY(-245px); } }
@keyframes float2 { 0%,100%{ transform: translate(-50%,-50%) translateX(220px) translateY(10px); } 50%{ transform: translate(-50%,-50%) translateX(235px) translateY(0px); } }
@keyframes float3 { 0%,100%{ transform: translate(-50%,-50%) translateX(-160px) translateY(190px); } 50%{ transform: translate(-50%,-50%) translateX(-175px) translateY(205px); } }
.satellite .tag { font-size: 0.9rem; font-weight: 800; color: #fff; }
.satellite .desc { font-size: 0.65rem; color: rgba(255,255,255,0.5); margin-top: 3px; }
.title {
position: fixed; top: 24px; left: 50%; transform: translateX(-50%);
font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.2em;
color: rgba(255,255,255,0.4); z-index: 100;
}
</style>
</head>
<body>
<span class="title">B. Energy Flow — กระแสพลังงาน พร้อม particles</span>
<div class="demo">
<canvas id="flowCanvas"></canvas>
<div class="scene" id="scene">
<div class="node core" data-node="center">
<span class="label">กำไร</span>
<span class="sub">เป้าหมายของทุกธุรกิจ</span>
</div>
<div class="node satellite" data-node="mkt">
<span class="tag">Marketing</span>
<span class="desc">เพิ่มรายได้</span>
</div>
<div class="node satellite" data-node="ai">
<span class="tag">AI</span>
<span class="desc">ลดต้นทุนและเวลา</span>
</div>
<div class="node satellite" data-node="biz">
<span class="tag">Business<br>Knowledge</span>
<span class="desc">ลดความเสี่ยง</span>
</div>
</div>
</div>
<script>
const canvas = document.getElementById('flowCanvas');
const ctx = canvas.getContext('2d');
const scene = document.getElementById('scene');
function resize() {
const rect = canvas.parentElement.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = rect.height;
}
resize();
window.addEventListener('resize', resize);
// Particle system per connection
const connections = [];
let t = 0;
function draw() {
t++;
const rect = canvas.parentElement.getBoundingClientRect();
ctx.clearRect(0, 0, canvas.width, canvas.height);
const sun = document.querySelector('[data-node="center"]');
const sats = document.querySelectorAll('.satellite');
if (!sun) { requestAnimationFrame(draw); return; }
const sr = sun.getBoundingClientRect();
const sx = sr.left + sr.width/2 - rect.left;
const sy = sr.top + sr.height/2 - rect.top;
sats.forEach((sat, i) => {
const pr = sat.getBoundingClientRect();
const px = pr.left + pr.width/2 - rect.left;
const py = pr.top + pr.height/2 - rect.top;
const dx = px - sx, dy = py - sy;
const dist = Math.sqrt(dx*dx + dy*dy);
// Draw stream line
ctx.beginPath();
ctx.moveTo(sx + (dx/dist)*80, sy + (dy/dist)*80);
ctx.lineTo(px - (dx/dist)*60, py - (dy/dist)*60);
const grad = ctx.createLinearGradient(sx, sy, px, py);
grad.addColorStop(0, 'rgba(254,212,0,0.6)');
grad.addColorStop(0.5, 'rgba(254,212,0,0.3)');
grad.addColorStop(1, 'rgba(254,212,0,0.1)');
ctx.strokeStyle = grad;
ctx.lineWidth = 2;
ctx.stroke();
// Animated particles along the line
const count = 12;
for (let j = 0; j < count; j++) {
const phase = (t * 0.03 + j / count + i * 0.33) % 1;
const pp = phase < 0.5 ? phase * 2 : 2 - phase * 2;
const x = sx + (dx/dist)*80 + dx * (1 - 80/dist - 60/dist) * phase;
const y = sy + (dy/dist)*80 + dy * (1 - 80/dist - 60/dist) * phase;
ctx.beginPath();
ctx.arc(x, y, 2.5, 0, Math.PI*2);
ctx.fillStyle = `rgba(254,230,100,${0.8 * pp})`;
ctx.fill();
// Glow
ctx.beginPath();
ctx.arc(x, y, 5, 0, Math.PI*2);
ctx.fillStyle = `rgba(254,212,0,${0.25 * pp})`;
ctx.fill();
}
});
requestAnimationFrame(draw);
}
document.addEventListener('mousemove', e => {
const x = (e.clientX / window.innerWidth - 0.5) * 6;
const y = (e.clientY / window.innerHeight - 0.5) * -6;
scene.style.transform = `rotateX(${y}deg) rotateY(${x}deg)`;
});
draw();
</script>
</body>
</html>

View File

@@ -0,0 +1,179 @@
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>C: Holographic 3D — Hologram Sci-Fi</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Kanit:wght@400;600;800;900&display=swap');
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Kanit', system-ui, sans-serif;
background: #020810;
color: #fff; overflow: hidden;
display: flex; align-items: center; justify-content: center; min-height: 100vh;
}
.demo { position: relative; width: 700px; height: 600px; }
canvas#holoCanvas { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.scene {
position: relative; width: 100%; height: 100%;
transform-style: preserve-3d; perspective: 1200px;
transition: transform 0.2s ease-out;
}
.node {
position: absolute;
display: flex; flex-direction: column;
align-items: center; justify-content: center; text-align: center;
border-radius: 50%;
}
/* Center hologram */
.holo-core {
left: 50%; top: 50%;
transform: translate(-50%, -50%);
width: 170px; height: 170px;
background: radial-gradient(circle at 40% 35%, rgba(0,255,255,0.15), rgba(0,200,255,0.05));
border: 2px solid rgba(0,255,255,0.5);
box-shadow: 0 0 50px rgba(0,255,255,0.2), 0 0 100px rgba(0,200,255,0.08), inset 0 0 30px rgba(0,255,255,0.05);
z-index: 10;
animation: holoPulse 2.5s ease-in-out infinite;
}
@keyframes holoPulse {
0%, 100% { border-color: rgba(0,255,255,0.5); box-shadow: 0 0 50px rgba(0,255,255,0.2), 0 0 100px rgba(0,200,255,0.08); }
50% { border-color: rgba(0,255,255,0.8); box-shadow: 0 0 70px rgba(0,255,255,0.35), 0 0 120px rgba(0,200,255,0.15); }
}
.holo-core .label { font-size: 2.8rem; font-weight: 900; color: #0ff; text-shadow: 0 0 20px rgba(0,255,255,0.5); }
.holo-core .sub { font-size: 0.7rem; color: rgba(0,255,255,0.5); margin-top: 4px; font-weight: 600; }
/* Scanline effect */
.holo-core::after {
content: ''; position: absolute; inset: 0; border-radius: 50%;
background: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,255,255,0.03) 2px, rgba(0,255,255,0.03) 4px);
pointer-events: none; z-index: 1;
}
/* Satellites - holographic */
.holo-sat {
width: 115px; height: 115px;
background: rgba(0,255,255,0.04);
border: 1.5px solid rgba(0,255,255,0.35);
box-shadow: 0 0 25px rgba(0,255,255,0.1), inset 0 0 20px rgba(0,255,255,0.04);
z-index: 5;
left: 50%; top: 50%;
}
.holo-sat::after {
content: ''; position: absolute; inset: 0; border-radius: 50%;
background: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,255,255,0.02) 2px, rgba(0,255,255,0.02) 4px);
pointer-events: none;
}
.holo-sat:nth-child(2) { transform: translate(-50%,-50%) translate3d(-220px,-140px,-80px); animation: hfloat1 5s ease-in-out infinite; }
.holo-sat:nth-child(3) { transform: translate(-50%,-50%) translate3d(180px,-20px,-30px); animation: hfloat2 6s ease-in-out infinite; }
.holo-sat:nth-child(4) { transform: translate(-50%,-50%) translate3d(-100px,210px,-100px); animation: hfloat3 5.5s ease-in-out infinite; }
@keyframes hfloat1 { 0%,100%{transform:translate(-50%,-50%) translate3d(-220px,-140px,-80px)} 50%{transform:translate(-50%,-50%) translate3d(-230px,-155px,-100px)} }
@keyframes hfloat2 { 0%,100%{transform:translate(-50%,-50%) translate3d(180px,-20px,-30px)} 50%{transform:translate(-50%,-50%) translate3d(195px,-35px,-55px)} }
@keyframes hfloat3 { 0%,100%{transform:translate(-50%,-50%) translate3d(-100px,210px,-100px)} 50%{transform:translate(-50%,-50%) translate3d(-115px,225px,-130px)} }
.holo-sat .tag { font-size: 0.9rem; font-weight: 800; color: #0ff; text-shadow: 0 0 10px rgba(0,255,255,0.4); }
.holo-sat .desc { font-size: 0.65rem; color: rgba(0,255,255,0.5); margin-top: 3px; }
.title {
position: fixed; top: 24px; left: 50%; transform: translateX(-50%);
font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.2em;
color: rgba(0,255,255,0.4); z-index: 100;
}
</style>
</head>
<body>
<span class="title">C. Holographic 3D — Hologram Sci-Fi</span>
<div class="demo">
<canvas id="holoCanvas"></canvas>
<div class="scene" id="scene">
<div class="node holo-core" data-node="center">
<span class="label">กำไร</span>
<span class="sub">เป้าหมายของทุกธุรกิจ</span>
</div>
<div class="node holo-sat" data-node="mkt">
<span class="tag">Marketing</span>
<span class="desc">เพิ่มรายได้</span>
</div>
<div class="node holo-sat" data-node="ai">
<span class="tag">AI</span>
<span class="desc">ลดต้นทุนและเวลา</span>
</div>
<div class="node holo-sat" data-node="biz">
<span class="tag">Business<br>Knowledge</span>
<span class="desc">ลดความเสี่ยง</span>
</div>
</div>
</div>
<script>
const canvas = document.getElementById('holoCanvas');
const ctx = canvas.getContext('2d');
const scene = document.getElementById('scene');
function resize() {
const rect = canvas.parentElement.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = rect.height;
}
resize(); window.addEventListener('resize', resize);
let t = 0;
function draw() {
t++;
const rect = canvas.parentElement.getBoundingClientRect();
ctx.clearRect(0, 0, canvas.width, canvas.height);
const center = document.querySelector('[data-node="center"]');
const sats = document.querySelectorAll('.holo-sat');
if (!center) { requestAnimationFrame(draw); return; }
const cr = center.getBoundingClientRect();
const cx = cr.left + cr.width/2 - rect.left;
const cy = cr.top + cr.height/2 - rect.top;
sats.forEach((sat, i) => {
const pr = sat.getBoundingClientRect();
const px = pr.left + pr.width/2 - rect.left;
const py = pr.top + pr.height/2 - rect.top;
const dx = px - cx, dy = py - cy;
const dist = Math.sqrt(dx*dx + dy*dy);
// Hologram beam line (dotted)
ctx.beginPath();
ctx.setLineDash([4, 8]);
ctx.lineDashOffset = -t * 2;
ctx.moveTo(cx + (dx/dist)*85, cy + (dy/dist)*85);
ctx.lineTo(px - (dx/dist)*58, py - (dy/dist)*58);
ctx.strokeStyle = 'rgba(0,255,255,0.3)';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.setLineDash([]);
// Glow nodes at intervals
for (let j = 0; j < 5; j++) {
const phase = ((t * 0.02 + j/5 + i*0.33) % 1);
const x = cx + (dx/dist)*85 + dx * (1 - 85/dist - 58/dist) * phase;
const y = cy + (dy/dist)*85 + dy * (1 - 85/dist - 58/dist) * phase;
const alpha = Math.sin(phase * Math.PI);
ctx.beginPath();
ctx.arc(x, y, 2, 0, Math.PI*2);
ctx.fillStyle = `rgba(0,255,255,${0.6 * alpha})`;
ctx.fill();
}
});
requestAnimationFrame(draw);
}
document.addEventListener('mousemove', e => {
scene.style.transform = `rotateX(${(e.clientY/window.innerHeight-0.5)*-8}deg) rotateY(${(e.clientX/window.innerWidth-0.5)*8}deg)`;
});
draw();
</script>
</body>
</html>

View File

@@ -0,0 +1,208 @@
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>D: Constellation — แผนที่ดาว</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Kanit:wght@400;600;800;900&display=swap');
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Kanit', system-ui, sans-serif;
background: radial-gradient(ellipse at center, #0d1520 0%, #040810 100%);
color: #fff; overflow: hidden;
display: flex; align-items: center; justify-content: center; min-height: 100vh;
}
/* Starfield background */
.stars {
position: fixed; inset: 0; z-index: 0; pointer-events: none;
}
.demo { position: relative; width: 700px; height: 600px; z-index: 1; }
canvas#constCanvas { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.scene {
position: relative; width: 100%; height: 100%;
transform-style: preserve-3d;
transition: transform 0.3s ease-out;
}
.node {
position: absolute;
display: flex; flex-direction: column;
align-items: center; justify-content: center; text-align: center;
}
/* Polaris - main star */
.star-main {
left: 50%; top: 50%;
transform: translate(-50%, -50%);
width: 150px; height: 150px; border-radius: 50%;
background: radial-gradient(circle at 38% 35%, rgba(255,255,255,0.25), rgba(254,212,0,0.15) 50%, transparent 100%);
z-index: 10;
}
.star-main .glow {
position: absolute; inset: -30px; border-radius: 50%;
background: radial-gradient(circle, rgba(254,212,0,0.08), transparent 70%);
animation: starGlow 3s ease-in-out infinite;
}
@keyframes starGlow {
0%, 100% { transform: scale(1); opacity: 0.5; }
50% { transform: scale(1.15); opacity: 0.8; }
}
.star-main .label { font-size: 2.6rem; font-weight: 900; color: #fed400; text-shadow: 0 0 30px rgba(254,212,0,0.6); position: relative; z-index: 1; }
.star-main .sub { font-size: 0.7rem; color: rgba(254,212,0,0.5); margin-top: 4px; font-weight: 600; position: relative; z-index: 1; }
/* Constellation stars */
.c-star {
width: 70px; height: 70px; border-radius: 50%;
background: radial-gradient(circle at 40% 35%, rgba(255,255,255,0.15), transparent);
z-index: 5;
left: 50%; top: 50%;
}
/* Star twinkle */
.c-star::before {
content: ''; position: absolute; inset: -15px; border-radius: 50%;
background: radial-gradient(circle, rgba(254,240,200,0.1), transparent 70%);
animation: twinkle 4s ease-in-out infinite;
}
@keyframes twinkle {
0%, 100% { opacity: 0.3; transform: scale(0.8); }
50% { opacity: 0.7; transform: scale(1.15); }
}
.c-star:nth-child(2)::before { animation-delay: 0s; }
.c-star:nth-child(3)::before { animation-delay: 1.2s; }
.c-star:nth-child(4)::before { animation-delay: 2.5s; }
/* Star points (4-point cross) */
.c-star::after {
content: ''; position: absolute; inset: -8px; border-radius: 50%;
box-shadow:
0 -25px 0 -8px rgba(254,240,200,0.3),
0 25px 0 -8px rgba(254,240,200,0.3),
-25px 0 0 -8px rgba(254,240,200,0.3),
25px 0 0 -8px rgba(254,240,200,0.3);
animation: starRotate 10s linear infinite;
}
@keyframes starRotate { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
.c-star:nth-child(2) { transform: translate(-50%,-50%) translate3d(-220px,-160px,-60px); }
.c-star:nth-child(3) { transform: translate(-50%,-50%) translate3d(190px,-30px,-30px); }
.c-star:nth-child(4) { transform: translate(-50%,-50%) translate3d(-120px,200px,-70px); }
.c-star .tag { font-size: 0.8rem; font-weight: 800; color: #fff; position: relative; z-index: 1; text-shadow: 0 0 15px rgba(255,255,255,0.4); }
.c-star .desc { font-size: 0.6rem; color: rgba(255,255,255,0.5); margin-top: 2px; position: relative; z-index: 1; }
.title {
position: fixed; top: 24px; left: 50%; transform: translateX(-50%);
font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.2em;
color: rgba(255,255,255,0.4); z-index: 100;
}
</style>
</head>
<body>
<span class="title">D. Constellation — แผนที่ดาว</span>
<!-- Starfield -->
<svg class="stars" xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient id="s"><stop offset="0%" stop-color="#fff" stop-opacity="0.8"/><stop offset="100%" stop-color="#fff" stop-opacity="0"/></radialGradient>
</defs>
<circle cx="8%" cy="12%" r="1.5" fill="#fff" opacity="0.6"/><circle cx="15%" cy="8%" r="1" fill="#fff" opacity="0.3"/>
<circle cx="22%" cy="18%" r="0.8" fill="#fff" opacity="0.5"/><circle cx="78%" cy="10%" r="1.2" fill="#fff" opacity="0.4"/>
<circle cx="85%" cy="22%" r="1" fill="#fff" opacity="0.3"/><circle cx="92%" cy="8%" r="0.6" fill="#fff" opacity="0.7"/>
<circle cx="6%" cy="85%" r="1" fill="#fff" opacity="0.4"/><circle cx="14%" cy="92%" r="0.8" fill="#fff" opacity="0.5"/>
<circle cx="88%" cy="88%" r="1.3" fill="#fff" opacity="0.35"/><circle cx="95%" cy="78%" r="0.7" fill="#fff" opacity="0.6"/>
<circle cx="42%" cy="5%" r="0.9" fill="#fff" opacity="0.45"/><circle cx="55%" cy="3%" r="0.5" fill="#fff" opacity="0.55"/>
<circle cx="65%" cy="92%" r="1.1" fill="#fff" opacity="0.3"/><circle cx="35%" cy="95%" r="0.7" fill="#fff" opacity="0.5"/>
</svg>
<div class="demo">
<canvas id="constCanvas"></canvas>
<div class="scene" id="scene">
<div class="node star-main" data-node="center">
<div class="glow"></div>
<span class="label">กำไร</span>
<span class="sub">เป้าหมายของทุกธุรกิจ</span>
</div>
<div class="node c-star" data-node="mkt">
<span class="tag">Marketing</span>
<span class="desc">เพิ่มรายได้</span>
</div>
<div class="node c-star" data-node="ai">
<span class="tag">AI</span>
<span class="desc">ลดต้นทุน</span>
</div>
<div class="node c-star" data-node="biz">
<span class="tag">Business Knowledge</span>
<span class="desc">ลดความเสี่ยง</span>
</div>
</div>
</div>
<script>
const canvas = document.getElementById('constCanvas');
const ctx = canvas.getContext('2d');
const scene = document.getElementById('scene');
function resize() {
const rect = canvas.parentElement.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = rect.height;
}
resize(); window.addEventListener('resize', resize);
function draw() {
const rect = canvas.parentElement.getBoundingClientRect();
ctx.clearRect(0, 0, canvas.width, canvas.height);
const center = document.querySelector('[data-node="center"]');
const stars = document.querySelectorAll('.c-star');
if (!center) { requestAnimationFrame(draw); return; }
const cr = center.getBoundingClientRect();
const cx = cr.left + cr.width/2 - rect.left;
const cy = cr.top + cr.height/2 - rect.top;
// Draw constellation lines (thin, elegant)
stars.forEach((star, i) => {
const pr = star.getBoundingClientRect();
const px = pr.left + pr.width/2 - rect.left;
const py = pr.top + pr.height/2 - rect.top;
const dx = px - cx, dy = py - cy;
const dist = Math.sqrt(dx*dx + dy*dy);
// Constellation line
ctx.beginPath();
ctx.moveTo(cx + (dx/dist)*75, cy + (dy/dist)*75);
ctx.lineTo(px - (dx/dist)*35, py - (dy/dist)*35);
ctx.strokeStyle = 'rgba(254,240,200,0.25)';
ctx.lineWidth = 1;
ctx.setLineDash([2, 6]);
ctx.stroke();
ctx.setLineDash([]);
// Tiny connecting stars along line
for (let j = 0; j < 3; j++) {
const p = 0.25 + j * 0.25;
const sx = cx + (dx/dist)*75 + dx * (1 - 75/dist - 35/dist) * p;
const sy = cy + (dy/dist)*75 + dy * (1 - 75/dist - 35/dist) * p;
ctx.beginPath();
ctx.arc(sx, sy, 1.2, 0, Math.PI*2);
ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.fill();
}
});
requestAnimationFrame(draw);
}
document.addEventListener('mousemove', e => {
scene.style.transform = `rotateX(${(e.clientY/window.innerHeight-0.5)*-5}deg) rotateY(${(e.clientX/window.innerWidth-0.5)*5}deg)`;
});
draw();
</script>
</body>
</html>

View File

@@ -0,0 +1,197 @@
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>E: Magnetic Field — สนามแม่เหล็ก</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Kanit:wght@400;600;800;900&display=swap');
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Kanit', system-ui, sans-serif;
background: #060a12;
color: #fff; overflow: hidden;
display: flex; align-items: center; justify-content: center; min-height: 100vh;
}
.demo { position: relative; width: 700px; height: 600px; }
canvas#magCanvas { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.scene {
position: relative; width: 100%; height: 100%;
transform-style: preserve-3d;
transition: transform 0.3s ease-out;
}
.node {
position: absolute;
display: flex; flex-direction: column;
align-items: center; justify-content: center; text-align: center;
border-radius: 50%;
}
/* Magnet core */
.magnet {
left: 50%; top: 50%;
transform: translate(-50%, -50%);
width: 170px; height: 170px;
background: radial-gradient(circle at 40% 35%, #3a2000, #0d0500);
border: 3px solid #fed400;
box-shadow: 0 0 0 12px rgba(254,212,0,0.1), 0 0 60px rgba(254,212,0,0.3), 0 0 140px rgba(254,180,0,0.1);
z-index: 10;
animation: magnetPulse 2s ease-in-out infinite;
}
@keyframes magnetPulse {
0%, 100% { box-shadow: 0 0 0 12px rgba(254,212,0,0.1), 0 0 60px rgba(254,212,0,0.3), 0 0 140px rgba(254,180,0,0.1); }
50% { box-shadow: 0 0 0 18px rgba(254,212,0,0.15), 0 0 80px rgba(254,212,0,0.4), 0 0 160px rgba(254,180,0,0.15); }
}
.magnet .label { font-size: 2.6rem; font-weight: 900; color: #fed400; }
.magnet .sub { font-size: 0.7rem; color: rgba(254,212,0,0.5); margin-top: 4px; font-weight: 600; }
/* Attracted nodes */
.attract {
width: 105px; height: 105px;
background: rgba(254,212,0,0.04);
border: 1.5px solid rgba(254,212,0,0.3);
box-shadow: 0 0 20px rgba(254,212,0,0.08);
z-index: 5;
left: 50%; top: 50%;
animation: attract 3s ease-in-out infinite alternate;
}
.attract:nth-child(2) {
transform: translate(-50%,-50%) translate3d(-200px,-150px,-60px);
animation-name: attract1;
}
.attract:nth-child(3) {
transform: translate(-50%,-50%) translate3d(170px,-20px,-25px);
animation-name: attract2;
}
.attract:nth-child(4) {
transform: translate(-50%,-50%) translate3d(-100px,190px,-70px);
animation-name: attract3;
}
@keyframes attract1 {
0% { transform: translate(-50%,-50%) translate3d(-220px,-160px,-80px); }
100% { transform: translate(-50%,-50%) translate3d(-185px,-140px,-50px); }
}
@keyframes attract2 {
0% { transform: translate(-50%,-50%) translate3d(185px,-25px,-35px); }
100% { transform: translate(-50%,-50%) translate3d(155px,-15px,-20px); }
}
@keyframes attract3 {
0% { transform: translate(-50%,-50%) translate3d(-120px,210px,-90px); }
100% { transform: translate(-50%,-50%) translate3d(-90px,180px,-60px); }
}
.attract .tag { font-size: 0.9rem; font-weight: 800; color: #fff; }
.attract .desc { font-size: 0.65rem; color: rgba(255,255,255,0.5); margin-top: 3px; }
.title {
position: fixed; top: 24px; left: 50%; transform: translateX(-50%);
font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.2em;
color: rgba(255,255,255,0.4); z-index: 100;
}
</style>
</head>
<body>
<span class="title">E. Magnetic Field — สนามแม่เหล็กดึงดูด</span>
<div class="demo">
<canvas id="magCanvas"></canvas>
<div class="scene" id="scene">
<div class="node magnet" data-node="center">
<span class="label">กำไร</span>
<span class="sub">เป้าหมายของทุกธุรกิจ</span>
</div>
<div class="node attract" data-node="mkt">
<span class="tag">Marketing</span>
<span class="desc">เพิ่มรายได้</span>
</div>
<div class="node attract" data-node="ai">
<span class="tag">AI</span>
<span class="desc">ลดต้นทุนและเวลา</span>
</div>
<div class="node attract" data-node="biz">
<span class="tag">Business<br>Knowledge</span>
<span class="desc">ลดความเสี่ยง</span>
</div>
</div>
</div>
<script>
const canvas = document.getElementById('magCanvas');
const ctx = canvas.getContext('2d');
const scene = document.getElementById('scene');
function resize() {
const rect = canvas.parentElement.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = rect.height;
}
resize(); window.addEventListener('resize', resize);
let t = 0;
function draw() {
t++;
const rect = canvas.parentElement.getBoundingClientRect();
ctx.clearRect(0, 0, canvas.width, canvas.height);
const center = document.querySelector('[data-node="center"]');
const nodes = document.querySelectorAll('.attract');
if (!center) { requestAnimationFrame(draw); return; }
const cr = center.getBoundingClientRect();
const cx = cr.left + cr.width/2 - rect.left;
const cy = cr.top + cr.height/2 - rect.top;
// Magnetic field lines (ripple waves)
for (let r = 0; r < 4; r++) {
const radius = 95 + r * 30 + (t * 0.02) % 30;
const alpha = Math.max(0, 0.25 - r * 0.06 - ((t * 0.02) % 30) / 120);
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, Math.PI*2);
ctx.strokeStyle = `rgba(254,212,0,${alpha})`;
ctx.lineWidth = 1;
ctx.stroke();
}
// Connection lines with magnetic field arcs
nodes.forEach((node, i) => {
const pr = node.getBoundingClientRect();
const px = pr.left + pr.width/2 - rect.left;
const py = pr.top + pr.height/2 - rect.top;
const dx = px - cx, dy = py - cy;
const dist = Math.sqrt(dx*dx + dy*dy);
// Main connection
ctx.beginPath();
ctx.moveTo(cx + (dx/dist)*85, cy + (dy/dist)*85);
ctx.lineTo(px - (dx/dist)*53, py - (dy/dist)*53);
ctx.strokeStyle = `rgba(254,212,0,0.4)`;
ctx.lineWidth = 2;
ctx.stroke();
// Field curve arcs on both sides
for (let side = -1; side <= 1; side += 2) {
const offset = side * 15;
const midX = cx + dx * 0.5 + (-dy/dist) * offset;
const midY = cy + dy * 0.5 + (dx/dist) * offset;
ctx.beginPath();
ctx.moveTo(cx + (dx/dist)*85, cy + (dy/dist)*85);
ctx.quadraticCurveTo(midX, midY, px - (dx/dist)*53, py - (dy/dist)*53);
ctx.strokeStyle = `rgba(254,212,0,0.1)`;
ctx.lineWidth = 1;
ctx.stroke();
}
});
requestAnimationFrame(draw);
}
document.addEventListener('mousemove', e => {
scene.style.transform = `rotateX(${(e.clientY/window.innerHeight-0.5)*-6}deg) rotateY(${(e.clientX/window.innerWidth-0.5)*6}deg)`;
});
draw();
</script>
</body>
</html>

75
public/demos/index.html Normal file
View File

@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hero Design Demos</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Kanit:wght@400;600;800;900&display=swap');
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Kanit', system-ui, sans-serif;
background: #0d1117;
color: #fff;
min-height: 100vh;
padding: 48px 24px;
}
h1 { font-size: 2rem; text-align: center; margin-bottom: 8px; }
h1 span { color: #fed400; }
.sub { text-align: center; color: rgba(255,255,255,0.5); margin-bottom: 40px; font-size: 1rem; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 20px; max-width: 1000px; margin: 0 auto; }
.card {
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 16px;
padding: 32px 24px;
text-decoration: none;
color: #fff;
transition: all 0.3s ease;
display: flex; flex-direction: column; gap: 12px;
}
.card:hover { background: rgba(254,212,0,0.06); border-color: rgba(254,212,0,0.3); transform: translateY(-2px); }
.card .emoji { font-size: 3rem; }
.card .name { font-size: 1.3rem; font-weight: 800; }
.card .name span { color: #fed400; }
.card .desc { font-size: 0.82rem; color: rgba(255,255,255,0.5); line-height: 1.5; }
.card .tag { display: inline-block; font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.15em; color: rgba(254,212,0,0.6); border: 1px solid rgba(254,212,0,0.2); border-radius: 20px; padding: 4px 10px; width: fit-content; }
</style>
</head>
<body>
<h1>Hero Section — <span>5 Design Demos</span></h1>
<p class="sub">เลือกดูแต่ละแนวคิดเพื่อเปรียบเทียบ</p>
<div class="grid">
<a href="a-orbital.html" class="card">
<div class="emoji">🌌</div>
<div class="name">A. <span>Orbital</span></div>
<div class="tag">ระบบดาวเคราะห์โคจร</div>
<div class="desc">กำไรเป็นดวงอาทิตย์เรืองแสง 3 ป้ายโคจรรอบด้วยวงแหวน orbital พร้อมหมุนอัตโนมัติ</div>
</a>
<a href="b-energyflow.html" class="card">
<div class="emoji"></div>
<div class="name">B. <span>Energy Flow</span></div>
<div class="tag">กระแสพลังงาน</div>
<div class="desc">Particle วิ่งตามเส้นเชื่อมสีทอง เหมือนพลังงาน/ข้อมูลไหลเข้าสู่กำไร ดูมีชีวิตชีวา</div>
</a>
<a href="c-holographic.html" class="card">
<div class="emoji">🌀</div>
<div class="name">C. <span>Holographic 3D</span></div>
<div class="tag">Hologram Sci-Fi</div>
<div class="desc">โทน Cyan เรืองแสง พร้อม scanline และ beam เชื่อมต่อ ดูล้ำสมัย ดูแพง</div>
</a>
<a href="d-constellation.html" class="card">
<div class="emoji"></div>
<div class="name">D. <span>Constellation</span></div>
<div class="tag">แผนที่ดาว</div>
<div class="desc">โหนดเป็นดาวระยิบระยับ เส้นเชื่อมแบบกลุ่มดาว พื้นหลังมีดาวกระจาย ดูลึกลับสง่า</div>
</a>
<a href="e-magnetic.html" class="card">
<div class="emoji">🧲</div>
<div class="name">E. <span>Magnetic Field</span></div>
<div class="tag">สนามแม่เหล็ก</div>
<div class="desc">กำไรเป็นแม่เหล็กทรงพลัง มี ripple wave + field curve สื่อถึงการดึงดูดทุกอย่างสู่กำไร</div>
</a>
</div>
</body>
</html>

BIN
public/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
public/favicon-180x180.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
public/favicon-48x48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Astro</title><path d="M8.358 20.162c-1.186-1.07-1.532-3.316-1.038-4.944.856 1.026 2.043 1.352 3.272 1.535 1.897.283 3.76.177 5.522-.678.202-.098.388-.229.608-.36.166.473.209.95.151 1.437-.14 1.185-.738 2.1-1.688 2.794-.38.277-.782.525-1.175.787-1.205.804-1.531 1.747-1.078 3.119l.044.148a3.158 3.158 0 0 1-1.407-1.188 3.31 3.31 0 0 1-.544-1.815c-.004-.32-.004-.642-.048-.958-.106-.769-.472-1.113-1.161-1.133-.707-.02-1.267.411-1.415 1.09-.012.053-.028.104-.045.165h.002zm-5.961-4.445s3.24-1.575 6.49-1.575l2.451-7.565c.092-.366.36-.614.662-.614.302 0 .57.248.662.614l2.45 7.565c3.85 0 6.491 1.575 6.491 1.575L16.088.727C15.93.285 15.663 0 15.303 0H8.697c-.36 0-.615.285-.784.727l-5.516 14.99z"/></svg>

After

Width:  |  Height:  |  Size: 779 B

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<title>Application Error</title>
<style media="screen">
html,body,iframe {
margin: 0;
padding: 0;
}
html,body {
height: 100%;
overflow: hidden;
}
iframe {
width: 100%;
height: 100%;
border: 0;
}
</style>
</head>
<body>
<iframe src="https://www.herokucdn.com/error-pages/application-error.html"></iframe>
</body>
</html>

BIN
public/images/blog-ai.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<title>Application Error</title>
<style media="screen">
html,body,iframe {
margin: 0;
padding: 0;
}
html,body {
height: 100%;
overflow: hidden;
}
iframe {
width: 100%;
height: 100%;
border: 0;
}
</style>
</head>
<body>
<iframe src="https://www.herokucdn.com/error-pages/application-error.html"></iframe>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<title>Application Error</title>
<style media="screen">
html,body,iframe {
margin: 0;
padding: 0;
}
html,body {
height: 100%;
overflow: hidden;
}
iframe {
width: 100%;
height: 100%;
border: 0;
}
</style>
</head>
<body>
<iframe src="https://www.herokucdn.com/error-pages/application-error.html"></iframe>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -0,0 +1 @@
<svg xmlns='http://www.w3.org/2000/svg' width='800' height='420'><rect width='800' height='420' fill='#fed400' opacity='0.12'/><rect x='40' y='40' width='720' height='340' rx='20' fill='none' stroke='#fed400' stroke-width='2' opacity='0.4'/><text x='400' y='160' text-anchor='middle' font-family='sans-serif' font-size='64' font-weight='900' fill='#13120d' opacity='0.3'>More</text><text x='400' y='240' text-anchor='middle' font-family='sans-serif' font-size='64' font-weight='900' fill='#13120d' opacity='0.3'>mini</text><text x='400' y='320' text-anchor='middle' font-family='sans-serif' font-size='64' font-weight='900' fill='#13120d' opacity='0.3'>More</text></svg>

After

Width:  |  Height:  |  Size: 671 B

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<title>Application Error</title>
<style media="screen">
html,body,iframe {
margin: 0;
padding: 0;
}
html,body {
height: 100%;
overflow: hidden;
}
iframe {
width: 100%;
height: 100%;
border: 0;
}
</style>
</head>
<body>
<iframe src="https://www.herokucdn.com/error-pages/application-error.html"></iframe>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 992 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 693 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 887 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1011 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Some files were not shown because too many files have changed in this diff Show More