Refactor frontend sections to use Tailwind CSS
Convert custom CSS styling to Tailwind utility classes across marketing and about pages. Improve responsive layouts in feature grids, service lists, and sliders. Consolidate section headers using the shared SectionHeader component to maintain visual consistency.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
---
|
||||
|
||||
<section
|
||||
class="py-20 px-5 bg-white text-center mx-auto lg:py-[60px] lg:px-5 md:py-10"
|
||||
class="py-15 px-5 md:py-20 bg-white text-center mx-auto"
|
||||
aria-labelledby="cta-heading"
|
||||
>
|
||||
<div class="max-w-3xl mx-auto">
|
||||
|
||||
@@ -42,8 +42,8 @@ try {
|
||||
const currentYear = new Date().getFullYear();
|
||||
---
|
||||
|
||||
<footer class="bg-[var(--color-tropical-blue)] pt-10 mt-auto relative">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<footer class="bg-(--color-tropical-blue) pt-10 mt-auto">
|
||||
<div class="max-w-4xl mx-auto px-12">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-8 mb-16">
|
||||
<div class="col-span-2">
|
||||
<Image
|
||||
@@ -56,16 +56,14 @@ const currentYear = new Date().getFullYear();
|
||||
decoding="async"
|
||||
/>
|
||||
<p
|
||||
class="text-[var(--color-st-tropaz)] text-sm pr-8 font-light leading-relaxed"
|
||||
class="text-(--color-st-tropaz) text-sm pr-0 md:pr-8 font-light leading-relaxed"
|
||||
>
|
||||
恩群數位累積多年廣告行銷操作經驗,擁有全方位行銷人才,讓我們可以為客戶精準的規劃每一分廣告預算,讓你的品牌深入人心。更重要的是恩群的存在,為了成為每家公司最佳數位夥伴,作為彼此最堅強的後盾,你會知道有我們的陪伴
|
||||
你並不孤單。
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3
|
||||
class="text-lg font-bold text-[var(--color-st-tropaz)] mb-4"
|
||||
>
|
||||
<h3 class="text-lg font-bold text-(--color-st-tropaz) mb-4">
|
||||
聯絡我們
|
||||
</h3>
|
||||
<a
|
||||
@@ -84,7 +82,7 @@ const currentYear = new Date().getFullYear();
|
||||
decoding="async"
|
||||
/>
|
||||
</a>
|
||||
<p class="text-sm text-[var(--color-st-tropaz)] mb-2">
|
||||
<p class="text-sm text-(--color-st-tropaz) mb-2">
|
||||
諮詢電話:<br /> 02 5570 0527
|
||||
</p>
|
||||
<a
|
||||
@@ -94,9 +92,7 @@ const currentYear = new Date().getFullYear();
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<h3
|
||||
class="text-lg font-bold text-[var(--color-st-tropaz)] mb-4"
|
||||
>
|
||||
<h3 class="text-lg font-bold text-(--color-st-tropaz) mb-4">
|
||||
行銷方案
|
||||
</h3>
|
||||
<ul
|
||||
@@ -125,9 +121,7 @@ const currentYear = new Date().getFullYear();
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3
|
||||
class="text-lg font-bold text-[var(--color-st-tropaz)] mb-4"
|
||||
>
|
||||
<h3 class="text-lg font-bold text-(--color-st-tropaz) mb-4">
|
||||
行銷放大鏡
|
||||
</h3>
|
||||
<ul class="text-sm font-thin space-y-2" id="marketing-articles">
|
||||
@@ -155,7 +149,7 @@ const currentYear = new Date().getFullYear();
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="w-screen bg-[var(--color-amber)] font-['Quicksand'] text-[var(--color-tarawera)] py-2 text-xs text-center"
|
||||
class="w-screen bg-(--color-amber) font-['Quicksand'] text-(--color-tarawera) py-2 text-xs text-center"
|
||||
>
|
||||
<p>
|
||||
copyright © Enchun digital 2018 - {currentYear}
|
||||
|
||||
@@ -22,7 +22,7 @@ const {
|
||||
<section class:list={classNameBg}>
|
||||
<div
|
||||
class:list={[
|
||||
"flex flex-rows sm:max-w-lg px-8 md:px-0 md:max-w-xl lg:max-w-3xl items-center justify-center gap-4 py-12 mx-auto",
|
||||
"flex flex-rows sm:max-w-lg px-8 md:px-0 md:max-w-xl lg:max-w-3xl items-center justify-center gap-4 pt-2 pb-10 mx-auto",
|
||||
className,
|
||||
]}
|
||||
>
|
||||
|
||||
@@ -28,12 +28,6 @@ const description =
|
||||
}}
|
||||
/>
|
||||
|
||||
<!-- Section Header -->
|
||||
<SectionHeader
|
||||
title="關於恩群"
|
||||
subtitle="About Enchun"
|
||||
sectionBg="bg-white"
|
||||
/>
|
||||
<!-- Service Features Section -->
|
||||
<FeatureSection />
|
||||
|
||||
|
||||
@@ -4,24 +4,28 @@
|
||||
* Pixel-perfect implementation based on Webflow design
|
||||
* Data fetched from Payload CMS Pages Collection API
|
||||
*/
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import SolutionsHero from '../sections/SolutionsHero.astro'
|
||||
import ServicesList from '../sections/ServicesList.astro'
|
||||
import { getMarketingSolutionsPage } from '../lib/api/marketing-solution'
|
||||
import SectionHeader from '../components/SectionHeader.astro'
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import SolutionsHero from "../sections/SolutionsHero.astro";
|
||||
import ServicesList from "../sections/ServicesList.astro";
|
||||
import { getMarketingSolutionsPage } from "../lib/api/marketing-solution";
|
||||
import SectionHeader from "../components/SectionHeader.astro";
|
||||
import CtaSection from "../components/CtaSection.astro";
|
||||
|
||||
// Fetch page data from CMS
|
||||
const pageData = await getMarketingSolutionsPage()
|
||||
const pageData = await getMarketingSolutionsPage();
|
||||
|
||||
// Use CMS data or fallback to defaults
|
||||
const heroTitle = pageData?.heroTitle || '行銷解決方案'
|
||||
const heroSubtitle = pageData?.heroSubtitle || '提供全方位的數位行銷服務,協助您的品牌在數位時代脫穎而出'
|
||||
const heroImage = pageData?.heroImage
|
||||
const services = pageData?.services || []
|
||||
const heroTitle = pageData?.heroTitle || "行銷解決方案";
|
||||
const heroSubtitle =
|
||||
pageData?.heroSubtitle ||
|
||||
"提供全方位的數位行銷服務,協助您的品牌在數位時代脫穎而出";
|
||||
const heroImage = pageData?.heroImage;
|
||||
const services = pageData?.services || [];
|
||||
|
||||
// Metadata for SEO
|
||||
const title = '行銷解決方案 | 恩群數位行銷'
|
||||
const description = '恩群數位行銷提供全方位的數位行銷服務,包括 Google Ads、社群代操、論壇行銷、網紅行銷、網站設計等,協助您的品牌在數位時代脫穎而出。'
|
||||
const title = "行銷解決方案 | 恩群數位行銷";
|
||||
const description =
|
||||
"恩群數位行銷提供全方位的數位行銷服務,包括 Google Ads、社群代操、論壇行銷、網紅行銷、網站設計等,協助您的品牌在數位時代脫穎而出。";
|
||||
---
|
||||
|
||||
<Layout title={title} description={description}>
|
||||
@@ -39,4 +43,5 @@ const description = '恩群數位行銷提供全方位的數位行銷服務,
|
||||
/>
|
||||
<!-- Services List -->
|
||||
<ServicesList services={services} />
|
||||
<CtaSection />
|
||||
</Layout>
|
||||
|
||||
@@ -9,7 +9,8 @@ import TeamsHero from "../sections/TeamsHero.astro";
|
||||
import EnvironmentSlider from "../sections/EnvironmentSlider.astro";
|
||||
import CompanyStory from "../sections/CompanyStory.astro";
|
||||
import BenefitsSection from "../sections/BenefitsSection.astro";
|
||||
|
||||
import SectionHeader from "../components/SectionHeader.astro";
|
||||
import CtaHrCompoents from "../sections/Cta-Hr-compoents.astro";
|
||||
// Metadata for SEO
|
||||
const title = "恩群大本營 | 恩群數位行銷";
|
||||
const description =
|
||||
@@ -28,50 +29,23 @@ const description =
|
||||
/>
|
||||
|
||||
<!-- Environment Slider Section -->
|
||||
|
||||
<EnvironmentSlider />
|
||||
|
||||
<!-- Company Story Section -->
|
||||
<CompanyStory />
|
||||
|
||||
<!-- Benefits Section -->
|
||||
|
||||
<BenefitsSection />
|
||||
|
||||
<!-- CTA Section -->
|
||||
<section
|
||||
class="py-20 px-5 bg-slate-100 text-center lg:py-[60px] lg:px-4 md:py-10"
|
||||
aria-labelledby="cta-heading"
|
||||
>
|
||||
<div class="max-w-[1200px] mx-auto">
|
||||
<div
|
||||
class="grid grid-cols-[1fr_auto] gap-8 items-center relative lg:grid-cols-1 lg:text-center"
|
||||
>
|
||||
<div class="text-left lg:text-center">
|
||||
<h3
|
||||
id="cta-heading"
|
||||
class="text-[1.75rem] font-semibold text-[#23608c] mb-4 leading-snug lg:text-[1.5rem] md:text-[1.5rem]"
|
||||
>
|
||||
以人的成長為優先<br />
|
||||
創造人的最大價值
|
||||
</h3>
|
||||
<p
|
||||
class="text-base text-slate-600 leading-relaxed max-w-[500px] lg:max-w-full md:text-[0.95rem]"
|
||||
>
|
||||
在恩群數位裡我們重視個人的特質能夠完全發揮,只要你樂於學習、善於跟人建立關係,並且重要的是你有一個善良的心,恩群數位歡迎你的加入
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="https://www.104.com.tw/company/1a2x6bkoaj?jobsource=joblist_r_cust"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center justify-center bg-link-hover text-white px-8 py-4 rounded-md font-semibold text-base transition-all duration-200 whitespace-nowrap hover:-translate-y-0.5 hover:shadow-md hover:bg-[#1a4d6e] lg:w-full lg:max-w-[300px] md:py-[14px] md:px-6 md:text-[0.95rem]"
|
||||
>
|
||||
立刻申請面試
|
||||
</a>
|
||||
<div
|
||||
class="absolute inset-0 bg-gradient-to-br from-[rgba(35,96,140,0.05)] to-[rgba(35,96,140,0.02)] rounded-lg -z-10 lg:hidden"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<CtaHrCompoents
|
||||
title="以人的成長為優先<br />創造人的最大價值"
|
||||
description="在恩群數位裡我們重視個人的特質能夠完全發揮,只要你樂於學習、善於跟人建立關係,並且重要的是你有一個善良的心,恩群數位歡迎你的加入"
|
||||
image={{
|
||||
url: "https://enchun-cms.anlstudio.cc/api/media/file/61f24aa108528b4723942d01_工作環境-銘言底圖-1400x659.jpg",
|
||||
alt: "工作環境",
|
||||
}}
|
||||
/>
|
||||
</Layout>
|
||||
|
||||
@@ -6,304 +6,93 @@
|
||||
*/
|
||||
|
||||
interface BenefitItem {
|
||||
title: string
|
||||
icon: string
|
||||
title: string;
|
||||
img: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
benefits?: BenefitItem[]
|
||||
benefits?: BenefitItem[];
|
||||
}
|
||||
|
||||
const defaultBenefits: BenefitItem[] = [
|
||||
{
|
||||
title: '高績效、高獎金\n新人開張獎金',
|
||||
icon: 'bonus',
|
||||
title: "高績效、高獎金\n新人開張獎金",
|
||||
img: "/api/media/file/61f24aa108528b79b2942d05_Make%20it%20rain-bro-%E6%96%B0%E4%BA%BA%E9%96%8B%E5%BC%B5%E7%8D%8E%E9%87%91.svg",
|
||||
},
|
||||
{
|
||||
title: '生日慶生、電影日\n員工下午茶',
|
||||
icon: 'birthday',
|
||||
title: "生日慶生、電影日\n員工下午茶",
|
||||
img: "/api/media/file/61f24aa108528be590942d06_Blowing%20out%20Birthday%20candles-bro-%E7%94%9F%E6%97%A5%E6%85%B6%E7%94%9F.svg",
|
||||
},
|
||||
{
|
||||
title: '教育訓練補助',
|
||||
icon: 'education',
|
||||
title: "教育訓練補助",
|
||||
img: "/api/media/file/61f24aa108528be22a942d03_Online%20learning-bro-%E6%95%99%E8%82%B2%E8%A8%93%E7%B7%B4%E8%A3%9C%E5%8A%A9.svg",
|
||||
},
|
||||
{
|
||||
title: '寬敞的工作空間',
|
||||
icon: 'workspace',
|
||||
title: "寬敞的工作空間",
|
||||
img: "/api/media/file/61f24aa108528be064942d08_Shared%20workspace-bro-%E5%AF%AC%E6%95%9E%E7%9A%84%E5%B7%A5%E4%BD%9C%E7%A9%BA%E9%96%93.svg",
|
||||
},
|
||||
{
|
||||
title: '員工國內外旅遊\n部門聚餐、年終活動',
|
||||
icon: 'travel',
|
||||
title: "員工國內外旅遊\n部門聚餐、年終活動",
|
||||
img: "/api/media/file/61f24aa108528b0960942d04_Flight%20Booking-bro-%E5%93%A1%E5%B7%A5%E6%97%85%E9%81%8A.svg",
|
||||
},
|
||||
{
|
||||
title: '入職培訓及團隊建設',
|
||||
icon: 'training',
|
||||
title: "入職培訓及團隊建設",
|
||||
img: "/api/media/file/61f24aa108528bf90b942d02_Brainstorming-bro-%E5%85%A5%E8%81%B7%E5%9F%B9%E8%A8%93.svg",
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
const benefits = Astro.props.benefits || defaultBenefits
|
||||
|
||||
// Icon SVG components (placeholder for now, replace with actual SVG)
|
||||
const getIconSVG = (iconType: string) => {
|
||||
const icons: Record<string, string> = {
|
||||
bonus: `<svg viewBox="0 0 200 200" class="benefit-icon-svg">
|
||||
<circle cx="100" cy="70" r="40" fill="#23608c" opacity="0.2"/>
|
||||
<rect x="60" y="100" width="80" height="60" rx="8" fill="#23608c"/>
|
||||
<text x="100" y="140" text-anchor="middle" fill="white" font-size="36">💰</text>
|
||||
</svg>`,
|
||||
birthday: `<svg viewBox="0 0 200 200" class="benefit-icon-svg">
|
||||
<circle cx="100" cy="70" r="40" fill="#23608c" opacity="0.2"/>
|
||||
<rect x="60" y="100" width="80" height="60" rx="8" fill="#23608c"/>
|
||||
<text x="100" y="140" text-anchor="middle" fill="white" font-size="36">🎂</text>
|
||||
</svg>`,
|
||||
education: `<svg viewBox="0 0 200 200" class="benefit-icon-svg">
|
||||
<circle cx="100" cy="70" r="40" fill="#23608c" opacity="0.2"/>
|
||||
<rect x="60" y="100" width="80" height="60" rx="8" fill="#23608c"/>
|
||||
<text x="100" y="140" text-anchor="middle" fill="white" font-size="36">📚</text>
|
||||
</svg>`,
|
||||
workspace: `<svg viewBox="0 0 200 200" class="benefit-icon-svg">
|
||||
<circle cx="100" cy="70" r="40" fill="#23608c" opacity="0.2"/>
|
||||
<rect x="60" y="100" width="80" height="60" rx="8" fill="#23608c"/>
|
||||
<text x="100" y="140" text-anchor="middle" fill="white" font-size="36">🏢</text>
|
||||
</svg>`,
|
||||
travel: `<svg viewBox="0 0 200 200" class="benefit-icon-svg">
|
||||
<circle cx="100" cy="70" r="40" fill="#23608c" opacity="0.2"/>
|
||||
<rect x="60" y="100" width="80" height="60" rx="8" fill="#23608c"/>
|
||||
<text x="100" y="140" text-anchor="middle" fill="white" font-size="36">✈️</text>
|
||||
</svg>`,
|
||||
training: `<svg viewBox="0 0 200 200" class="benefit-icon-svg">
|
||||
<circle cx="100" cy="70" r="40" fill="#23608c" opacity="0.2"/>
|
||||
<rect x="60" y="100" width="80" height="60" rx="8" fill="#23608c"/>
|
||||
<text x="100" y="140" text-anchor="middle" fill="white" font-size="36">🤝</text>
|
||||
</svg>`,
|
||||
}
|
||||
return icons[iconType] || icons.bonus
|
||||
}
|
||||
const benefits = Astro.props.benefits || defaultBenefits;
|
||||
import SectionHeader from "../components/SectionHeader.astro";
|
||||
---
|
||||
|
||||
<section class="section-benefit" aria-labelledby="benefits-heading">
|
||||
<div class="container w-container">
|
||||
<section
|
||||
class="py-15 px-5 bg-white md:py-10 md:px-4"
|
||||
aria-labelledby="benefits-heading"
|
||||
>
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<!-- Section Header -->
|
||||
<div class="section_header_w_line">
|
||||
<div class="divider_line"></div>
|
||||
<div class="header_subtitle">
|
||||
<h2 id="benefits-heading" class="header_subtitle_head">工作福利</h2>
|
||||
<p class="header_subtitle_paragraph">Benefit Package</p>
|
||||
</div>
|
||||
<div class="divider_line"></div>
|
||||
</div>
|
||||
<SectionHeader
|
||||
title="工作福利"
|
||||
subtitle="Benefit Packages"
|
||||
sectionBg="bg-white"
|
||||
/>
|
||||
|
||||
<!-- Benefits Grid -->
|
||||
<div class="benefit-grid-wrapper">
|
||||
<!-- Benefits Grid: 2 cards per row -->
|
||||
<div
|
||||
class="max-w-md md:max-w-3xl mx-auto grid grid-cols-1 gap-x-2 gap-y-2 md:gap-y-6 md:grid-cols-2 py-18"
|
||||
>
|
||||
{
|
||||
benefits.map((benefit, index) => (
|
||||
<div class={`benefit-card ${index % 2 === 0 ? 'benefit-card' : 'benefit-card-opposite'}`}>
|
||||
<!-- Odd: Icon on right, Even: Icon on left -->
|
||||
{
|
||||
index % 2 === 0 ? (
|
||||
<>
|
||||
<div class="benefit-content">
|
||||
<h3 class="benefit-title-text">{benefit.title}</h3>
|
||||
</div>
|
||||
<div class="benefit-image-right" set:html={getIconSVG(benefit.icon)} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div class="benefit-image-left" set:html={getIconSVG(benefit.icon)} />
|
||||
<div class="benefit-content">
|
||||
<h3 class="benefit-title-text">{benefit.title}</h3>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
))
|
||||
benefits.map((benefit, index) => {
|
||||
const isLeft = index % 2 === 0;
|
||||
return (
|
||||
<div class="grid grid-cols-2 gap-2 items-center">
|
||||
<div
|
||||
class:list={[
|
||||
isLeft ? "order-1 text-right" : "order-2 text-left",
|
||||
]}
|
||||
>
|
||||
<h3 class="text-xl md:text-base lg:text-xl font-semibold text-(--color-enchunblue) whitespace-pre-wrap leading-snug">
|
||||
{benefit.title}
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class:list={[
|
||||
"flex items-center",
|
||||
isLeft ? "order-2 justify-start" : "order-1 justify-end",
|
||||
]}
|
||||
>
|
||||
<img
|
||||
src={`https://enchun-cms.anlstudio.cc${benefit.img}`}
|
||||
alt={benefit.title}
|
||||
class="size-35 object-contain"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
/* Benefits Section Styles - Pixel-perfect from Webflow */
|
||||
.section-benefit {
|
||||
padding: 60px 20px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.w-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Section Header */
|
||||
.section_header_w_line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.header_subtitle {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header_subtitle_head {
|
||||
color: var(--color-enchunblue);
|
||||
font-family: "Noto Sans TC", sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 2rem;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.header_subtitle_paragraph {
|
||||
color: var(--color-gray-600);
|
||||
font-family: "Quicksand", sans-serif;
|
||||
font-weight: 400;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.divider_line {
|
||||
width: 40px;
|
||||
height: 2px;
|
||||
background-color: var(--color-enchunblue);
|
||||
}
|
||||
|
||||
/* Benefits Grid */
|
||||
.benefit-grid-wrapper {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Benefit Card */
|
||||
.benefit-card,
|
||||
.benefit-card-opposite {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 40px;
|
||||
align-items: center;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
/* Odd cards: icon on right */
|
||||
.benefit-card {
|
||||
grid-template-areas: "content image";
|
||||
}
|
||||
|
||||
.benefit-card .benefit-content {
|
||||
grid-area: content;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.benefit-card .benefit-image-right {
|
||||
grid-area: image;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Even cards: icon on left */
|
||||
.benefit-card-opposite {
|
||||
grid-template-areas: "image content";
|
||||
}
|
||||
|
||||
.benefit-card-opposite .benefit-content {
|
||||
grid-area: content;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.benefit-card-opposite .benefit-image-left {
|
||||
grid-area: image;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Benefit Title */
|
||||
.benefit-title-text {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-tarawera, #23608c);
|
||||
white-space: pre-line;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Benefit Icon */
|
||||
.benefit-icon-svg {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.benefit-image-right,
|
||||
.benefit-image-left {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.benefit-image-right svg,
|
||||
.benefit-image-left svg {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
/* Responsive Adjustments */
|
||||
@media (max-width: 991px) {
|
||||
.benefit-card,
|
||||
.benefit-card-opposite {
|
||||
gap: 24px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.benefit-title-text {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.section-benefit {
|
||||
padding: 40px 16px;
|
||||
}
|
||||
|
||||
.section_header_w_line {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.benefit-card,
|
||||
.benefit-card-opposite {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas: "image" "content" !important;
|
||||
gap: 24px;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.benefit-content {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.benefit-image-right,
|
||||
.benefit-image-left {
|
||||
order: -1;
|
||||
}
|
||||
|
||||
.benefit-title-text {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.benefit-icon-svg {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.benefit-image-right svg,
|
||||
.benefit-image-left svg {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,128 +4,36 @@
|
||||
* Pixel-perfect implementation based on Webflow design
|
||||
*/
|
||||
interface Props {
|
||||
title?: string
|
||||
subtitle?: string
|
||||
content?: string
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
content?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
title = '恩群數位的故事',
|
||||
subtitle = 'Something About Enchun Digital',
|
||||
content = '恩群數位是由一群年輕果斷、敢冒險的年輕人聚在一起,共同為台灣在地經營努力不懈的商家老闆們建立品牌的知名度。而商家的經營本身就不是一件容易的事情,早在恩群成立之前,我們便一直聚焦在與不同行業的老闆們建立起信賴可靠的關係,從一家又一家的合作關係當中,聚集了在行銷領域裡的頂尖好手,培育了許多優秀行銷顧問。讓每個辛苦經營的商家老闆可以獲得最佳的服務,讓每次的行銷需求可以透過有效的互動與聆聽,達到彼此心目中的預期目標。數字的確會說話,但是每一個有溫度的服務才是在恩群裡最重視的地方。',
|
||||
} = Astro.props
|
||||
title = "恩群數位的故事",
|
||||
subtitle = "Something About Enchun Digital",
|
||||
content = "恩群數位是由一群年輕果斷、敢冒險的年輕人聚在一起,共同為台灣在地經營努力不懈的商家老闆們建立品牌的知名度。而商家的經營本身就不是一件容易的事情,早在恩群成立之前,我們便一直聚焦在與不同行業的老闆們建立起信賴可靠的關係,從一家又一家的合作關係當中,聚集了在行銷領域裡的頂尖好手,培育了許多優秀行銷顧問。讓每個辛苦經營的商家老闆可以獲得最佳的服務,讓每次的行銷需求可以透過有效的互動與聆聽,達到彼此心目中的預期目標。數字的確會說話,但是每一個有溫度的服務才是在恩群裡最重視的地方。",
|
||||
} = Astro.props;
|
||||
import SectionHeader from "../components/SectionHeader.astro";
|
||||
---
|
||||
|
||||
<section class="section-story" aria-labelledby="story-heading">
|
||||
<div class="container w-container">
|
||||
<section
|
||||
class="section-story py-20 md:py-[60px] md:px-4 max-md:py-10 max-md:px-4 text-center bg-white"
|
||||
aria-labelledby="story-heading"
|
||||
>
|
||||
<div class="container w-container max-w-4xl mx-auto">
|
||||
<!-- Section Header -->
|
||||
<div class="section_header_w_line">
|
||||
<div class="divider_line"></div>
|
||||
<div class="header_subtitle">
|
||||
<h2 id="story-heading" class="header_subtitle_head">{title}</h2>
|
||||
<p class="header_subtitle_paragraph">{subtitle}</p>
|
||||
</div>
|
||||
<div class="divider_line"></div>
|
||||
</div>
|
||||
<SectionHeader
|
||||
title="恩群數位的故事"
|
||||
subtitle="Something About Enchun Digital"
|
||||
sectionBg="bg-white"
|
||||
/>
|
||||
|
||||
<!-- Story Content -->
|
||||
<p class="story-paragraph">{content}</p>
|
||||
<p
|
||||
class="story-paragraph max-w-3xl mx-auto text-base md:text-lg font-thin leading-[1.8] max-md:leading-[1.7] text-(--color-text-secondary)/80 font-[Noto_Sans_TC,sans-serif]"
|
||||
>
|
||||
{content}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
/* Company Story Styles - Pixel-perfect from Webflow */
|
||||
.section-story {
|
||||
padding: 80px 20px;
|
||||
text-align: center;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.w-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Section Header */
|
||||
.section_header_w_line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.header_subtitle {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header_subtitle_head {
|
||||
color: var(--color-enchunblue);
|
||||
font-family: "Noto Sans TC", sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 2rem;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.header_subtitle_paragraph {
|
||||
color: var(--color-gray-600);
|
||||
font-family: "Quicksand", sans-serif;
|
||||
font-weight: 400;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.divider_line {
|
||||
width: 40px;
|
||||
height: 2px;
|
||||
background-color: var(--color-enchunblue);
|
||||
}
|
||||
|
||||
/* Story Content */
|
||||
.story-paragraph {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.8;
|
||||
color: var(--color-text-secondary);
|
||||
font-family: "Noto Sans TC", sans-serif;
|
||||
}
|
||||
|
||||
/* Responsive Adjustments */
|
||||
@media (max-width: 991px) {
|
||||
.section-story {
|
||||
padding: 60px 16px;
|
||||
}
|
||||
|
||||
.header_subtitle_head {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
.story-paragraph {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.section-story {
|
||||
padding: 40px 16px;
|
||||
}
|
||||
|
||||
.section_header_w_line {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.header_subtitle_head {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.story-paragraph {
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,274 +1,139 @@
|
||||
---
|
||||
import SectionHeader from "@/components/SectionHeader.astro";
|
||||
|
||||
/**
|
||||
* ComparisonSection - 恩群數位 vs 其他行銷公司 對比表格
|
||||
* Pixel-perfect implementation based on Webflow design
|
||||
* Custom layout based on new design specs with Tailwind CSS
|
||||
*/
|
||||
interface ComparisonItem {
|
||||
feature: string
|
||||
enchun: string
|
||||
others: string
|
||||
}
|
||||
|
||||
const comparisonItems: ComparisonItem[] = [
|
||||
{
|
||||
feature: '服務範圍',
|
||||
enchun: '全方位數位行銷服務,從策略到執行一條龍',
|
||||
others: '單一服務項目,缺乏整合性',
|
||||
},
|
||||
{
|
||||
feature: '數據分析',
|
||||
enchun: '專業數據分析團隊,精準追蹤 ROI',
|
||||
others: '基礎報告,缺乏深度分析',
|
||||
},
|
||||
{
|
||||
feature: '在地化經驗',
|
||||
enchun: '深耕台灣市場,了解本地消費者習性',
|
||||
others: '通用策略,缺乏在地化調整',
|
||||
},
|
||||
{
|
||||
feature: '客戶服務',
|
||||
enchun: '一對一專人服務,快速響應',
|
||||
others: '標準化流程,回應較慢',
|
||||
},
|
||||
{
|
||||
feature: '價格透明',
|
||||
enchun: '明確報價,無隱藏費用',
|
||||
others: '複雜收費結構,容易超支',
|
||||
},
|
||||
]
|
||||
const otherCompanyItems = [
|
||||
"缺乏經驗",
|
||||
"沒有成效保證",
|
||||
"售後無服務",
|
||||
"沒有策略",
|
||||
"不了解客戶需求",
|
||||
"沒有接受客戶反饋",
|
||||
];
|
||||
|
||||
const enchunItems = [
|
||||
"實際執行經驗豐富",
|
||||
"實際成效",
|
||||
"售後服務架構完善",
|
||||
"行銷策略有方",
|
||||
"熟悉客戶需求",
|
||||
"最多客戶回饋",
|
||||
];
|
||||
---
|
||||
|
||||
<section class="section-comparison" aria-labelledby="comparison-heading">
|
||||
<div class="w-container">
|
||||
<section
|
||||
class="bg-[#f8f9fa] py-10 px-3 md:py-[60px] md:px-4 lg:py-20 lg:px-5"
|
||||
aria-labelledby="comparison-heading"
|
||||
>
|
||||
<div class="max-w-[1000px] mx-auto">
|
||||
<!-- Section Header -->
|
||||
<div class="section-header-w-line">
|
||||
<h2 id="comparison-heading" class="header-subtitle-head">
|
||||
為什麼選擇恩群數位
|
||||
</h2>
|
||||
<div class="divider-line"></div>
|
||||
<SectionHeader
|
||||
title="恩群與其他公司有什麼不同"
|
||||
subtitle="What make us different from others"
|
||||
/>
|
||||
|
||||
<!-- Comparison Cards Layout -->
|
||||
<div
|
||||
class="relative bg-white rounded-2xl shadow-xl overflow-hidden mt-10 md:mt-12 max-w-4xl mx-auto border border-gray-100"
|
||||
>
|
||||
<!-- Winning Medal (Absolute Positioned) -->
|
||||
<div
|
||||
class="absolute -top-2 right-6 md:right-12 w-16 h-16 md:w-20 md:h-20 z-10 drop-shadow-lg"
|
||||
>
|
||||
<img
|
||||
src="https://cdn.prod.website-files.com/61f24aa108528b1962942c95/61f24aa108528b7eab942cd9_winning%20medal.svg"
|
||||
loading="lazy"
|
||||
alt="Winning Medal"
|
||||
class="w-full h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2">
|
||||
<!-- Left Column: Other Companies -->
|
||||
<div
|
||||
class="p-8 md:p-12 border-b md:border-b-0 md:border-r border-gray-200 bg-gray-50 flex flex-col gap-6"
|
||||
>
|
||||
<h3
|
||||
class="text-2xl md:text-3xl font-bold text-gray-800 tracking-tight font-['Noto_Sans_TC',sans-serif]"
|
||||
>
|
||||
其他行銷公司
|
||||
</h3>
|
||||
<p
|
||||
class="text-gray-500 text-sm md:text-base leading-relaxed h-[60px]"
|
||||
>
|
||||
市場上每間行銷公司想要讓自己的公司服務可以被看見,有時候客戶只能成為待宰羔羊
|
||||
</p>
|
||||
<div class="h-px w-full bg-gray-200 my-2"></div>
|
||||
<ul role="list" class="flex flex-col gap-4 md:gap-5 mt-2">
|
||||
{
|
||||
otherCompanyItems.map((item) => (
|
||||
<li class="flex items-center gap-3">
|
||||
<span class="text-gray-700 font-medium text-base md:text-lg flex-1">
|
||||
{item}
|
||||
</span>
|
||||
<div class="flex-shrink-0 flex items-center justify-center w-6 h-6 rounded-full bg-red-50 relative top-1">
|
||||
<img
|
||||
src="https://cdn.prod.website-files.com/61f24aa108528b1962942c95/61f24aa108528b9634942cdb_wrong.svg"
|
||||
loading="lazy"
|
||||
alt="Cross Icon"
|
||||
class="w-3 h-3"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Right Column: Enchun Digital -->
|
||||
<div class="p-8 md:p-12 flex flex-col gap-6 relative bg-white">
|
||||
<h3
|
||||
class="text-2xl md:text-3xl font-bold text-[var(--color-enchunblue)] tracking-tight font-['Noto_Sans_TC',sans-serif]"
|
||||
>
|
||||
恩群數位
|
||||
</h3>
|
||||
<p
|
||||
class="text-gray-600 text-sm md:text-base leading-relaxed h-[60px]"
|
||||
>
|
||||
在恩群數位每個客戶都是我們最重視的拍檔,不論合作的項目大小,我們珍惜與客戶的合作關係
|
||||
</p>
|
||||
<div class="h-px w-full bg-blue-100 my-2"></div>
|
||||
<ul role="list" class="flex flex-col gap-4 md:gap-5 mt-2">
|
||||
{
|
||||
enchunItems.map((item) => (
|
||||
<li class="flex items-center gap-3">
|
||||
<span class="text-gray-800 font-medium text-base md:text-lg flex-1">
|
||||
{item}
|
||||
</span>
|
||||
<div class="flex-shrink-0 flex items-center justify-center w-6 h-6 rounded-full bg-green-50 relative top-1">
|
||||
<img
|
||||
src="https://cdn.prod.website-files.com/61f24aa108528b1962942c95/61f24aa108528b7a79942cda_checked.svg"
|
||||
loading="lazy"
|
||||
alt="Check Icon"
|
||||
class="w-4 h-4"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Comparison Table -->
|
||||
<div class="comparison-table-wrapper">
|
||||
<table class="comparison-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="th-feature">比較項目</th>
|
||||
<th class="th-enchun">
|
||||
<span class="enchun-badge">恩群數位</span>
|
||||
</th>
|
||||
<th class="th-others">其他行銷公司</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
comparisonItems.map((item, index) => (
|
||||
<tr class={index % 2 === 0 ? 'row-even' : 'row-odd'}>
|
||||
<td class="td-feature">{item.feature}</td>
|
||||
<td class="td-enchun">
|
||||
<span class="enchun-icon">✓</span>
|
||||
{item.enchun}
|
||||
</td>
|
||||
<td class="td-others">{item.others}</td>
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- CTA Note -->
|
||||
<div class="comparison-note">
|
||||
<p class="note-text">
|
||||
選擇恩群數位,讓您的品牌在數位時代脫穎而出!
|
||||
</p>
|
||||
<!-- CTA Button -->
|
||||
<div class="mt-12 flex justify-center">
|
||||
<a
|
||||
href="https://heyform.itslouis.cc/form/7mYtUNjA"
|
||||
target="_blank"
|
||||
class="inline-flex items-center justify-center bg-[var(--color-enchunblue)] text-white font-bold text-lg px-10 py-4 rounded-full shadow-[0_8px_20px_-6px_rgba(43,83,186,0.5)] hover:shadow-[0_12px_24px_-8px_rgba(43,83,186,0.6)] hover:opacity-95 transition-all duration-300 transform hover:-translate-y-1"
|
||||
>
|
||||
<div class="tracking-wide">跟行銷顧問聊聊</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
/* Comparison Section Styles - Pixel-perfect from Webflow */
|
||||
.section-comparison {
|
||||
background-color: #f8f9fa;
|
||||
padding: 80px 20px;
|
||||
}
|
||||
|
||||
.w-container {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Section Header */
|
||||
.section-header-w-line {
|
||||
text-align: center;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.header-subtitle-head {
|
||||
color: var(--color-enchunblue);
|
||||
font-family: "Noto Sans TC", "Quicksand", sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 2rem;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.divider-line {
|
||||
background-color: var(--color-enchunblue);
|
||||
height: 2px;
|
||||
width: 60px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Table Wrapper */
|
||||
.comparison-table-wrapper {
|
||||
background: white;
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Comparison Table */
|
||||
.comparison-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
/* Table Header */
|
||||
.comparison-table thead {
|
||||
background-color: var(--color-enchunblue);
|
||||
}
|
||||
|
||||
.comparison-table th {
|
||||
color: white;
|
||||
font-family: "Noto Sans TC", sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 1.125rem;
|
||||
padding: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.th-feature {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.th-enchun {
|
||||
width: 45%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.th-others {
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
/* Enchun Badge */
|
||||
.enchun-badge {
|
||||
display: inline-block;
|
||||
background-color: white;
|
||||
color: var(--color-enchunblue);
|
||||
padding: 4px 16px;
|
||||
border-radius: 20px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Table Body */
|
||||
.comparison-table tbody tr {
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.comparison-table tbody tr:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.row-even {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.row-odd {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.comparison-table td {
|
||||
padding: 20px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.td-feature {
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.td-enchun {
|
||||
color: var(--color-text-secondary);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.enchun-icon {
|
||||
display: inline-block;
|
||||
color: #22c55e;
|
||||
font-weight: bold;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.td-others {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
/* Comparison Note */
|
||||
.comparison-note {
|
||||
text-align: center;
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.note-text {
|
||||
font-size: 1.125rem;
|
||||
color: var(--color-enchunblue);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Responsive Adjustments */
|
||||
@media (max-width: 991px) {
|
||||
.section-comparison {
|
||||
padding: 60px 16px;
|
||||
}
|
||||
|
||||
.comparison-table th,
|
||||
.comparison-table td {
|
||||
padding: 16px 12px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.header-subtitle-head {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
/* Convert to card layout on mobile */
|
||||
.comparison-table-wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.comparison-table {
|
||||
min-width: 600px;
|
||||
}
|
||||
|
||||
.section-comparison {
|
||||
padding: 40px 12px;
|
||||
}
|
||||
|
||||
.header-subtitle-head {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.comparison-table th,
|
||||
.comparison-table td {
|
||||
padding: 12px 8px;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.enchun-badge {
|
||||
font-size: 0.875rem;
|
||||
padding: 2px 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
62
apps/frontend/src/sections/Cta-Hr-compoents.astro
Normal file
62
apps/frontend/src/sections/Cta-Hr-compoents.astro
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
/**
|
||||
* Cta-Hr-compoents - HR CTA Section
|
||||
* 招募行動呼籲區塊,引導求職者申請面試
|
||||
*/
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
image?: { url: string; alt: string };
|
||||
}
|
||||
|
||||
const { title, description, image } = Astro.props;
|
||||
---
|
||||
|
||||
<section
|
||||
class="relative py-20 px-5 bg-slate-100 bg-cover bg-center overflow-hidden"
|
||||
aria-labelledby="cta-heading"
|
||||
style={image ? `background-image: url('${image.url}')` : undefined}
|
||||
>
|
||||
{/* Dark overlay when image is present */}
|
||||
{
|
||||
image && (
|
||||
<div
|
||||
class="absolute inset-0 bg-linear-to-b from-transparent to-black/70 z-0"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)
|
||||
}
|
||||
<div class="relative z-10 max-w-3xl md:max-w-3xl mx-auto">
|
||||
<div
|
||||
class="grid grid-rows-[1fr_auto] gap-8 items-center lg:grid-cols-[1fr_auto]"
|
||||
>
|
||||
<div class="text-center md:text-left">
|
||||
<h3
|
||||
id="cta-heading"
|
||||
class:list={[
|
||||
"text-4xl md:text-4xl font-semibold mb-4 leading-snug",
|
||||
image ? "text-white" : "text-[#23608c]",
|
||||
]}
|
||||
>
|
||||
<Fragment set:html={title} />
|
||||
</h3>
|
||||
<p
|
||||
class:list={[
|
||||
"text-sm md:text-base leading-relaxed text-balance max-w-xl lg:max-w-full",
|
||||
image ? "text-white/80" : "text-slate-600",
|
||||
]}
|
||||
>
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="https://www.104.com.tw/company/1a2x6bkoaj?jobsource=joblist_r_cust"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center justify-center mx-auto bg-(--color-pale-purple) text-white px-8 py-4 rounded-md font-semibold text-xl transition-all duration-200 whitespace-nowrap hover:-translate-y-0.5 hover:shadow-md hover:bg-(--color-pale-purple)/90 w-full max-w-3xs md:w-full lg:max-w-[300px]"
|
||||
>
|
||||
立刻申請面試
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -6,54 +6,88 @@
|
||||
*/
|
||||
|
||||
interface SlideImage {
|
||||
src: string
|
||||
alt: string
|
||||
src: string;
|
||||
alt: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
slides?: SlideImage[]
|
||||
slides?: SlideImage[];
|
||||
}
|
||||
|
||||
const defaultSlides: SlideImage[] = [
|
||||
{ src: '/placeholder-environment-1.jpg', alt: '恩群環境照片 1' },
|
||||
{ src: '/placeholder-environment-2.jpg', alt: '恩群環境照片 2' },
|
||||
{ src: '/placeholder-environment-3.jpg', alt: '恩群環境照片 3' },
|
||||
{ src: '/placeholder-environment-4.jpg', alt: '恩群環境照片 4' },
|
||||
{ src: '/placeholder-environment-5.jpg', alt: '恩群環境照片 5' },
|
||||
{ src: '/placeholder-environment-6.jpg', alt: '恩群環境照片 6' },
|
||||
{ src: '/placeholder-environment-7.jpg', alt: '恩群環境照片 7' },
|
||||
{ src: '/placeholder-environment-8.jpg', alt: '恩群環境照片 8' },
|
||||
]
|
||||
{
|
||||
src: "https://enchun-cms.anlstudio.cc/api/media/file/61f76b4962117e2d84363174_%E6%81%A9%E7%BE%A4%E7%92%B0%E5%A2%83%20%E7%85%A7%E7%89%871.jpg",
|
||||
alt: "恩群環境照片 1",
|
||||
},
|
||||
{
|
||||
src: "https://enchun-cms.anlstudio.cc/api/media/file/61f76b49558b7e5b0e81de8f_%E6%81%A9%E7%BE%A4%E7%92%B0%E5%A2%83%20%E7%85%A7%E7%89%871-7.jpg",
|
||||
alt: "恩群環境照片 2",
|
||||
},
|
||||
{
|
||||
src: "https://enchun-cms.anlstudio.cc/api/media/file/61f76b4962117e2d84363174_%E6%81%A9%E7%BE%A4%E7%92%B0%E5%A2%83%20%E7%85%A7%E7%89%871-1.jpg",
|
||||
alt: "恩群環境照片 3",
|
||||
},
|
||||
{
|
||||
src: "https://enchun-cms.anlstudio.cc/api/media/file/61f76c511895ed028da1c7f0_%E6%81%A9%E7%BE%A4%E7%92%B0%E5%A2%83%20%E7%85%A7%E7%89%871-3.jpg",
|
||||
alt: "恩群環境照片 4",
|
||||
},
|
||||
{
|
||||
src: "https://enchun-cms.anlstudio.cc/api/media/file/61f76b48558b7e072a81de8e_%E6%81%A9%E7%BE%A4%E7%92%B0%E5%A2%83%20%E7%85%A7%E7%89%871-4.jpg",
|
||||
alt: "恩群環境照片 5",
|
||||
},
|
||||
{
|
||||
src: "https://enchun-cms.anlstudio.cc/api/media/file/61f76b483504a25babe537ef_%E6%81%A9%E7%BE%A4%E7%92%B0%E5%A2%83%20%E7%85%A7%E7%89%871-1.jpg",
|
||||
alt: "恩群環境照片 6",
|
||||
},
|
||||
{
|
||||
src: "https://enchun-cms.anlstudio.cc/api/media/file/61f76b48ef5754e17a8c1676_%E6%81%A9%E7%BE%A4%E7%92%B0%E5%A2%83%20%E7%85%A7%E7%89%871-6.jpg",
|
||||
alt: "恩群環境照片 7",
|
||||
},
|
||||
{
|
||||
src: "https://enchun-cms.anlstudio.cc/api/media/file/620639d7a68fef44f6569b23_%E6%81%A9%E7%BE%A4%E7%92%B0%E5%A2%83%20%E7%85%A7%E7%89%879.jpg",
|
||||
alt: "恩群環境照片 8",
|
||||
},
|
||||
];
|
||||
|
||||
const slides = Astro.props.slides || defaultSlides
|
||||
const slides = Astro.props.slides || defaultSlides;
|
||||
|
||||
import SectionHeader from '../components/SectionHeader.astro'
|
||||
import SectionHeader from "../components/SectionHeader.astro";
|
||||
---
|
||||
|
||||
<section class="section-video" aria-label="工作環境照片">
|
||||
<div class="container spacer8 w-container">
|
||||
<section
|
||||
class="section-video py-10 md:py-20 md:px-5 px-4 bg-white"
|
||||
aria-label="工作環境照片"
|
||||
>
|
||||
<div class="container w-container max-w-3xl mx-auto">
|
||||
<!-- Section Header -->
|
||||
<SectionHeader
|
||||
title="在恩群工作的環境"
|
||||
subtitle="Working Enviroment"
|
||||
sectionBg="bg-white"
|
||||
/>
|
||||
|
||||
<!-- Environment Slider -->
|
||||
<div
|
||||
class="environment-slider"
|
||||
class="environment-slider relative w-full max-w-4xl mx-auto"
|
||||
id="env-slider-{Math.random().toString(36).slice(2, 8)}"
|
||||
>
|
||||
<!-- Slides Container -->
|
||||
<div class="slides-container">
|
||||
<div
|
||||
class="slides-container flex overflow-x-auto snap-x snap-mandatory scroll-smooth overscroll-x-contain [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden cursor-grab"
|
||||
>
|
||||
{
|
||||
slides.map((slide, index) => (
|
||||
<div class="environment-slide" data-index={index}>
|
||||
<div
|
||||
class="environment-slide flex-none w-full snap-start aspect-video overflow-hidden rounded-xl"
|
||||
data-index={index}
|
||||
>
|
||||
<img
|
||||
src={slide.src}
|
||||
alt={slide.alt}
|
||||
loading={index === 0 ? 'eager' : 'lazy'}
|
||||
loading={index === 0 ? "eager" : "lazy"}
|
||||
width="800"
|
||||
height="450"
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
@@ -61,23 +95,33 @@ import SectionHeader from '../components/SectionHeader.astro'
|
||||
</div>
|
||||
|
||||
<!-- Arrow Navigation -->
|
||||
<button class="slider-arrow slider-arrow-left" aria-label="上一張">
|
||||
<button
|
||||
class="slider-arrow slider-arrow-left absolute top-1/2 -translate-y-1/2 w-12 h-12 md:w-12 md:h-12 w-10 h-10 bg-white/80 rounded-full flex items-center justify-center cursor-pointer transition-all duration-200 ease-in-out z-10 hover:bg-white hover:scale-110 left-4 md:left-4 left-2"
|
||||
aria-label="上一張"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="currentColor" d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="slider-arrow slider-arrow-right" aria-label="下一張">
|
||||
<button
|
||||
class="slider-arrow slider-arrow-right absolute top-1/2 -translate-y-1/2 w-12 h-12 md:w-12 md:h-12 w-10 h-10 bg-white/80 rounded-full flex items-center justify-center cursor-pointer transition-all duration-200 ease-in-out z-10 hover:bg-white hover:scale-110 right-4 md:right-4 right-2"
|
||||
aria-label="下一張"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="currentColor" d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Dot Navigation -->
|
||||
<div class="slider-dots">
|
||||
<div class="slider-dots flex justify-center gap-2 mt-4">
|
||||
{
|
||||
slides.map((_, index) => (
|
||||
<button
|
||||
class={`slider-dot ${index === 0 ? 'active' : ''}`}
|
||||
class={`slider-dot w-3 h-3 rounded-full cursor-pointer transition-all duration-200 ease-in-out ${index === 0 ? "active bg-(--color-enchunblue) w-8 rounded-[6px]" : "bg-(--color-enchunblue)/30"}`}
|
||||
data-index={index}
|
||||
aria-label={`顯示第 ${index + 1} 張照片`}
|
||||
/>
|
||||
@@ -88,313 +132,183 @@ import SectionHeader from '../components/SectionHeader.astro'
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
/* Environment Slider Styles - Pixel-perfect from Webflow */
|
||||
.section-video {
|
||||
padding: 80px 20px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.w-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.spacer8 {
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Environment Slider */
|
||||
.environment-slider {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.slides-container {
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
scroll-snap-type: x mandatory;
|
||||
scroll-behavior: smooth;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.slides-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.environment-slide {
|
||||
flex: 0 0 100%;
|
||||
scroll-snap-align: start;
|
||||
aspect-ratio: 16/9;
|
||||
overflow: hidden;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.environment-slide img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* Arrow Navigation */
|
||||
.slider-arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 200ms ease;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.slider-arrow:hover {
|
||||
background-color: white;
|
||||
transform: translateY(-50%) scale(1.1);
|
||||
}
|
||||
|
||||
.slider-arrow-left {
|
||||
left: 16px;
|
||||
}
|
||||
|
||||
.slider-arrow-right {
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
/* Dot Navigation */
|
||||
.slider-dots {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.slider-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(35, 96, 140, 0.3);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 200ms ease;
|
||||
}
|
||||
|
||||
.slider-dot.active {
|
||||
background-color: var(--color-enchunblue);
|
||||
width: 32px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* Responsive Adjustments */
|
||||
@media (min-width: 992px) {
|
||||
.environment-slider {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.environment-slider {
|
||||
max-width: 550px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.section-video {
|
||||
padding: 60px 16px;
|
||||
}
|
||||
|
||||
.environment-slider {
|
||||
max-width: 90vw;
|
||||
}
|
||||
|
||||
.slider-arrow {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.slider-arrow-left {
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.slider-arrow-right {
|
||||
right: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Environment Slider functionality
|
||||
function initEnvironmentSlider() {
|
||||
const sliders = document.querySelectorAll('.environment-slider')
|
||||
const sliders = document.querySelectorAll(".environment-slider");
|
||||
|
||||
sliders.forEach((slider) => {
|
||||
const container = slider.querySelector('.slides-container') as HTMLElement
|
||||
const slides = slider.querySelectorAll('.environment-slide')
|
||||
const dots = slider.querySelectorAll('.slider-dot')
|
||||
const prevBtn = slider.querySelector('.slider-arrow-left') as HTMLButtonElement
|
||||
const nextBtn = slider.querySelector('.slider-arrow-right') as HTMLButtonElement
|
||||
const container = slider.querySelector(
|
||||
".slides-container",
|
||||
) as HTMLElement;
|
||||
const slides = slider.querySelectorAll(".environment-slide");
|
||||
const dots = slider.querySelectorAll(".slider-dot");
|
||||
const prevBtn = slider.querySelector(
|
||||
".slider-arrow-left",
|
||||
) as HTMLButtonElement;
|
||||
const nextBtn = slider.querySelector(
|
||||
".slider-arrow-right",
|
||||
) as HTMLButtonElement;
|
||||
|
||||
if (!container || slides.length === 0) return
|
||||
if (!container || slides.length === 0) return;
|
||||
|
||||
let currentIndex = 0
|
||||
const totalSlides = slides.length
|
||||
let isDragging = false
|
||||
let startPos = 0
|
||||
let currentTranslate = 0
|
||||
let prevTranslate = 0
|
||||
let animationID: number
|
||||
let currentIndex = 0;
|
||||
const totalSlides = slides.length;
|
||||
let isDragging = false;
|
||||
let startPos = 0;
|
||||
let currentTranslate = 0;
|
||||
let prevTranslate = 0;
|
||||
let animationID: number;
|
||||
|
||||
// Update slider position
|
||||
const updateSlider = () => {
|
||||
container.scrollTo({
|
||||
left: currentIndex * container.offsetWidth,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
updateDots()
|
||||
}
|
||||
behavior: "smooth",
|
||||
});
|
||||
updateDots();
|
||||
};
|
||||
|
||||
// Update dots
|
||||
const updateDots = () => {
|
||||
dots.forEach((dot, index) => {
|
||||
if (index === currentIndex) {
|
||||
dot.classList.add('active')
|
||||
dot.classList.add(
|
||||
"active",
|
||||
"w-8",
|
||||
"rounded-[6px]",
|
||||
"bg-(--color-enchunblue)",
|
||||
);
|
||||
dot.classList.remove("w-3", "bg-(--color-enchunblue)/30");
|
||||
} else {
|
||||
dot.classList.remove('active')
|
||||
dot.classList.remove(
|
||||
"active",
|
||||
"w-8",
|
||||
"rounded-[6px]",
|
||||
"bg-(--color-enchunblue)",
|
||||
);
|
||||
dot.classList.add("w-3", "bg-(--color-enchunblue)/30");
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Go to specific slide
|
||||
const goToSlide = (index: number) => {
|
||||
if (index < 0) index = totalSlides - 1
|
||||
if (index >= totalSlides) index = 0
|
||||
currentIndex = index
|
||||
updateSlider()
|
||||
}
|
||||
if (index < 0) index = totalSlides - 1;
|
||||
if (index >= totalSlides) index = 0;
|
||||
currentIndex = index;
|
||||
updateSlider();
|
||||
};
|
||||
|
||||
// Previous slide
|
||||
prevBtn?.addEventListener('click', () => goToSlide(currentIndex - 1))
|
||||
prevBtn?.addEventListener("click", () => goToSlide(currentIndex - 1));
|
||||
|
||||
// Next slide
|
||||
nextBtn?.addEventListener('click', () => goToSlide(currentIndex + 1))
|
||||
nextBtn?.addEventListener("click", () => goToSlide(currentIndex + 1));
|
||||
|
||||
// Dot navigation
|
||||
dots.forEach((dot, index) => {
|
||||
dot.addEventListener('click', () => goToSlide(index))
|
||||
})
|
||||
dot.addEventListener("click", () => goToSlide(index));
|
||||
});
|
||||
|
||||
// Scroll snap detection
|
||||
container.addEventListener('scroll', () => {
|
||||
const slideIndex = Math.round(container.scrollLeft / container.offsetWidth)
|
||||
container.addEventListener("scroll", () => {
|
||||
const slideIndex = Math.round(
|
||||
container.scrollLeft / container.offsetWidth,
|
||||
);
|
||||
if (slideIndex !== currentIndex) {
|
||||
currentIndex = slideIndex
|
||||
updateDots()
|
||||
currentIndex = slideIndex;
|
||||
updateDots();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Touch/swipe support
|
||||
const touchStart = (_index: number) => {
|
||||
return function(event: TouchEvent) {
|
||||
isDragging = true
|
||||
startPos = event.touches[0].clientX
|
||||
animationID = requestAnimationFrame(animation)
|
||||
container.style.cursor = 'grabbing'
|
||||
}
|
||||
}
|
||||
return function (event: TouchEvent) {
|
||||
isDragging = true;
|
||||
startPos = event.touches[0].clientX;
|
||||
animationID = requestAnimationFrame(animation);
|
||||
container.style.cursor = "grabbing";
|
||||
};
|
||||
};
|
||||
|
||||
const touchEnd = () => {
|
||||
isDragging = false
|
||||
cancelAnimationFrame(animationID)
|
||||
container.style.cursor = 'grab'
|
||||
isDragging = false;
|
||||
cancelAnimationFrame(animationID);
|
||||
container.style.cursor = "grab";
|
||||
|
||||
const movedBy = currentTranslate - prevTranslate
|
||||
const movedBy = currentTranslate - prevTranslate;
|
||||
|
||||
if (movedBy < -50 && currentIndex < totalSlides - 1) {
|
||||
currentIndex += 1
|
||||
currentIndex += 1;
|
||||
} else if (movedBy > 50 && currentIndex > 0) {
|
||||
currentIndex -= 1
|
||||
currentIndex -= 1;
|
||||
}
|
||||
|
||||
goToSlide(currentIndex)
|
||||
}
|
||||
goToSlide(currentIndex);
|
||||
};
|
||||
|
||||
const touchMove = (event: TouchEvent) => {
|
||||
if (isDragging) {
|
||||
const currentPosition = event.touches[0].clientX
|
||||
currentTranslate = prevTranslate + currentPosition - startPos
|
||||
const currentPosition = event.touches[0].clientX;
|
||||
currentTranslate = prevTranslate + currentPosition - startPos;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const animation = () => {
|
||||
if (isDragging) requestAnimationFrame(animation)
|
||||
}
|
||||
if (isDragging) requestAnimationFrame(animation);
|
||||
};
|
||||
|
||||
// Mouse events for desktop
|
||||
let mouseStartPos = 0
|
||||
let isMouseDown = false
|
||||
let mouseStartPos = 0;
|
||||
let isMouseDown = false;
|
||||
|
||||
container.addEventListener('mousedown', (e: MouseEvent) => {
|
||||
isMouseDown = true
|
||||
mouseStartPos = e.clientX
|
||||
container.style.cursor = 'grabbing'
|
||||
})
|
||||
container.addEventListener("mousedown", (e: MouseEvent) => {
|
||||
isMouseDown = true;
|
||||
mouseStartPos = e.clientX;
|
||||
container.style.cursor = "grabbing";
|
||||
});
|
||||
|
||||
container.addEventListener('mouseup', (e: MouseEvent) => {
|
||||
if (!isMouseDown) return
|
||||
isMouseDown = false
|
||||
container.style.cursor = 'grab'
|
||||
container.addEventListener("mouseup", (e: MouseEvent) => {
|
||||
if (!isMouseDown) return;
|
||||
isMouseDown = false;
|
||||
container.style.cursor = "grab";
|
||||
|
||||
const movedBy = e.clientX - mouseStartPos
|
||||
const movedBy = e.clientX - mouseStartPos;
|
||||
if (movedBy < -50 && currentIndex < totalSlides - 1) {
|
||||
currentIndex += 1
|
||||
currentIndex += 1;
|
||||
} else if (movedBy > 50 && currentIndex > 0) {
|
||||
currentIndex -= 1
|
||||
currentIndex -= 1;
|
||||
}
|
||||
goToSlide(currentIndex)
|
||||
})
|
||||
goToSlide(currentIndex);
|
||||
});
|
||||
|
||||
container.addEventListener('mouseleave', () => {
|
||||
isMouseDown = false
|
||||
container.style.cursor = 'grab'
|
||||
})
|
||||
container.addEventListener("mouseleave", () => {
|
||||
isMouseDown = false;
|
||||
container.style.cursor = "grab";
|
||||
});
|
||||
|
||||
// Touch events
|
||||
container.addEventListener('touchstart', touchStart(currentIndex))
|
||||
container.addEventListener('touchend', touchEnd)
|
||||
container.addEventListener('touchmove', touchMove)
|
||||
container.addEventListener("touchstart", touchStart(currentIndex));
|
||||
container.addEventListener("touchend", touchEnd);
|
||||
container.addEventListener("touchmove", touchMove);
|
||||
|
||||
// Keyboard navigation
|
||||
slider.addEventListener('keydown', (e) => {
|
||||
slider.addEventListener("keydown", (e) => {
|
||||
if (e instanceof KeyboardEvent) {
|
||||
if (e.key === 'ArrowLeft') goToSlide(currentIndex - 1)
|
||||
if (e.key === 'ArrowRight') goToSlide(currentIndex + 1)
|
||||
if (e.key === "ArrowLeft") goToSlide(currentIndex - 1);
|
||||
if (e.key === "ArrowRight") goToSlide(currentIndex + 1);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Set initial state
|
||||
container.style.cursor = 'grab'
|
||||
})
|
||||
container.style.cursor = "grab";
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', initEnvironmentSlider)
|
||||
if (document.readyState !== 'loading') {
|
||||
initEnvironmentSlider()
|
||||
document.addEventListener("DOMContentLoaded", initEnvironmentSlider);
|
||||
if (document.readyState !== "loading") {
|
||||
initEnvironmentSlider();
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -36,38 +36,35 @@ const features = [
|
||||
description: '除了幫您拓展網路上的知名度,我們更是每家公司最專業的數位夥伴,你會知道有恩群的存在,事業路上你並不孤單。',
|
||||
},
|
||||
]
|
||||
import SectionHeader from '../components/SectionHeader.astro'
|
||||
---
|
||||
|
||||
<section class="section_feature bg-white px-4 py-[60px] md:px-5 md:py-[80px]" aria-labelledby="feature-heading">
|
||||
<section class="section_feature bg-white px-4 py-16 md:px-5 md:py-20" aria-labelledby="feature-heading">
|
||||
<div class="w-container mx-auto max-w-4xl">
|
||||
<!-- Section Header -->
|
||||
<div class="section_header_w_line mb-[60px] text-center">
|
||||
<h2 id="feature-heading" class="header_subtitle_head my-4 text-[1.75rem] font-bold leading-[1.2] text-[var(--color-enchunblue)] md:text-[2.25rem]">
|
||||
{title}
|
||||
</h2>
|
||||
<p class="header_subtitle_paragraph mt-2 text-base font-normal text-[#666666]">
|
||||
{subtitle}
|
||||
</p>
|
||||
<div class="divider_line mx-auto h-[2px] w-[100px] bg-[var(--color-enchunblue)]"></div>
|
||||
</div>
|
||||
<SectionHeader
|
||||
title="恩群服務特色"
|
||||
subtitle="Why you can trust us"
|
||||
sectionBg="bg-white"
|
||||
/>
|
||||
|
||||
<!-- Features Grid -->
|
||||
<div class="grid grid-cols-1 gap-1 md:grid-cols-2 md:gap-2 lg:gap-6">
|
||||
<div class="grid grid-cols-2 gap-1 md:grid-cols-2 md:gap-2 lg:gap-6">
|
||||
{
|
||||
features.map((feature) => (
|
||||
<div class="feature_card bg-white p-5 transition-all duration-[var(--transition-base)] hover:-translate-y-1 md:p-6 lg:p-8">
|
||||
<div class="grid grid-cols-[120px_1fr] items-start gap-4 md:grid-cols-[110px_1fr] lg:grid-cols-[190px_1fr] lg:gap-6">
|
||||
<div class="flex flex-col items-center gap-4 text-center md:flex-row md:items-start md:text-left lg:gap-6">
|
||||
<!-- Icon -->
|
||||
<div class="flex w-full items-center justify-center">
|
||||
<div class="flex w-full max-w-[100px] shrink-0 items-center justify-center md:max-w-[110px] lg:max-w-[190px]">
|
||||
<img src={feature.img} alt={feature.title} class="h-auto w-full object-contain" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<!-- Title -->
|
||||
<h3 class="font-['Noto_Sans_TC'] lg:text-3xl text-2xl font-semibold text-(--color-dark-blue)">{feature.title}</h3>
|
||||
<h3 class="font-['Noto_Sans_TC'] text-2xl font-semibold text-(--color-dark-blue) lg:text-3xl">{feature.title}</h3>
|
||||
|
||||
<!-- Description -->
|
||||
<p class="font-['Noto_Sans_TC'] lg:text-base text-sm font-thin leading-[1.6] text-grey-2">{feature.description}</p>
|
||||
<p class="font-['Noto_Sans_TC'] text-sm font-thin leading-[1.6] text-grey-2 lg:text-base">{feature.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -34,7 +34,7 @@ const painpoints = [
|
||||
{
|
||||
id: 'burning',
|
||||
title: '廣告行銷像燒錢',
|
||||
icon: '💸',
|
||||
img: '💸',
|
||||
description: '廣告預算投入很多,但看不到實際效果?感覺像在燒錢?',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -158,7 +158,7 @@ const renderIcon = (service: ServicesListItem): string | null => {
|
||||
---
|
||||
|
||||
<section class="bg-white py-[60px] px-5 md:py-[60px] md:px-5 lg:py-[60px] lg:px-5" aria-labelledby="services-heading">
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="max-w-3xl mx-auto flex flex-col gap-6">
|
||||
{
|
||||
servicesList.map((service, index) => {
|
||||
const isOdd = index % 2 === 0
|
||||
@@ -177,14 +177,14 @@ const renderIcon = (service: ServicesListItem): string | null => {
|
||||
<!-- Content Side -->
|
||||
<div class={`service-item-content order-2 ${isOdd ? 'md:order-1' : 'md:order-2'}`}>
|
||||
<!-- Category Tag with Icon -->
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<div class="flex flex-col-reverse items-start gap-2 mb-4">
|
||||
<span class="inline-block text-(--color-enchunblue) text-3xl font-light">
|
||||
{service.category}
|
||||
</span>
|
||||
{/* Icon displayed next to category */}
|
||||
{
|
||||
iconHtml && (
|
||||
<span class="inline-flex items-center justify-center w-8 h-8 text-(--color-enchunblue)" set:html={iconHtml} />
|
||||
<span class="inline-flex items-center justify-start w-8 h-8 ml-2 scale-150 text-(--color-enchunblue)" set:html={iconHtml} />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -36,7 +36,7 @@ const bgImageUrl = backgroundImage?.url || "";
|
||||
// Background color fallback
|
||||
!hasBackgroundImage && "bg-(--color-dark-blue)",
|
||||
// Background image styles
|
||||
hasBackgroundImage && "bg-size-[120vw] bg-center bg-no-repeat",
|
||||
hasBackgroundImage && "bg-cover bg-center bg-no-repeat",
|
||||
// Pull up to counteract layout's pt-20 padding (80px)
|
||||
"-mt-20",
|
||||
// Full viewport height
|
||||
|
||||
@@ -136,6 +136,8 @@
|
||||
/* Amber - CTA/強調 */
|
||||
--color-tarawera: #2d3748;
|
||||
/* Tarawera - 深色文字 */
|
||||
--color-pale-purple: oklch(62.664% 0.15547 290.298);
|
||||
/* Pale Purple - CTA/強調 */
|
||||
--color-nav-link: var(--color-gray-200);
|
||||
/* Navigation Link - 使用灰色系 */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user