|
|
@@ -25,14 +25,17 @@ const pillars = [
|
|
|
{
|
|
|
title: "油气专属底座 · 适配全场景需求",
|
|
|
desc: "聚焦油气领域专属场景,深度适配数采、大模型相关各类应用需求,可灵活承载勘探、开发等全环节数字化场景,贴合行业业务特性。",
|
|
|
+ image: platformCover,
|
|
|
},
|
|
|
{
|
|
|
title: "高兼容架构 · 打破系统壁垒",
|
|
|
desc: "采用高兼容技术架构,可无缝对接油气领域各类数采设备、现有系统及大模型应用,实现数据互通、系统联动,降低对接成本。",
|
|
|
+ image: new URL("../assets/images/news3.png", import.meta.url).href,
|
|
|
},
|
|
|
{
|
|
|
title: "灵活可扩展 · 承载多元需求",
|
|
|
desc: "平台底座具备高度灵活性与可扩展性,可根据油气领域不同场景、不同需求,灵活承载数采、大模型相关各类数字化应用落地。",
|
|
|
+ image: new URL("../assets/images/case7.png", import.meta.url).href,
|
|
|
},
|
|
|
],
|
|
|
},
|
|
|
@@ -46,18 +49,22 @@ const pillars = [
|
|
|
{
|
|
|
title: "全维度设备管理 · 筑牢管控基础",
|
|
|
desc: "依托平台底座,承载油气领域设备全生命周期管理场景,兼容各类数采设备,实现设备台账、参数信息统一管控,适配不同型号、不同环节油气设备,高效统筹设备资源。",
|
|
|
+ image: equipmentCover,
|
|
|
},
|
|
|
{
|
|
|
title: "智慧维保巡检 · 降低运维成本",
|
|
|
desc: "联动巡检、维保全流程,支持自定义巡检计划与维保流程,无缝对接现场数采数据,实现维保巡检标准化、规范化,减少人工冗余,提高运维效率。",
|
|
|
+ image: new URL("../assets/images/a.png", import.meta.url).href,
|
|
|
},
|
|
|
{
|
|
|
title: "预测性维护 · 防范设备风险",
|
|
|
desc: "借助平台大模型与数采技术支撑,实时分析设备运行数据,精准预测潜在故障并提前触发维护提醒,规避停机风险,降低故障损失。",
|
|
|
+ image: new URL("../assets/images/b.png", import.meta.url).href,
|
|
|
},
|
|
|
{
|
|
|
title: "实时监控预警 · 保障作业安全",
|
|
|
desc: "实时采集运行参数,实现设备状态可视化监控;异常情况快速预警、精准推送,适配油气现场复杂作业环境,筑牢安全生产防线。",
|
|
|
+ image: new URL("../assets/images/c.png", import.meta.url).href,
|
|
|
},
|
|
|
],
|
|
|
},
|
|
|
@@ -71,18 +78,22 @@ const pillars = [
|
|
|
{
|
|
|
title: "项目任务管理 · 联动平台高效统筹",
|
|
|
desc: "承接油气领域生产项目全流程任务管理,无缝对接平台数据体系,实现任务分配、进度跟踪、闭环管理,统筹项目各环节,确保生产任务有序推进。",
|
|
|
+ image: new URL("../assets/images/d.png", import.meta.url).href,
|
|
|
},
|
|
|
{
|
|
|
title: "计划与实际联动 · 精准把控生产节奏",
|
|
|
desc: "基于平台数采能力,科学制定生产计划;同步对接生产实际数据,自动匹配计划与实际差异,实时调整生产安排,保障流程高效衔接。",
|
|
|
+ image: new URL("../assets/images/e.png", import.meta.url).href,
|
|
|
},
|
|
|
{
|
|
|
title: "自动报表生成 · 沉淀核心数据资产",
|
|
|
desc: "自动汇总数采数据、生产进度与任务完成情况,一键生成日报、月报等报表,减少人工录入,实现生产数据规范化保存与资产沉淀。",
|
|
|
+ image: new URL("../assets/images/f.png", import.meta.url).href,
|
|
|
},
|
|
|
{
|
|
|
title: "全流程数智赋能 · 提升生产整体效能",
|
|
|
desc: "深度联动平台数采与数据管理能力,打造生产计划、任务执行、数据统计全链条,减少人工冗余,提升协同效率,实现数字化、智能化转型。",
|
|
|
+ image: new URL("../assets/images/g.png", import.meta.url).href,
|
|
|
},
|
|
|
],
|
|
|
},
|
|
|
@@ -96,18 +107,22 @@ const pillars = [
|
|
|
{
|
|
|
title: "合规适配 QHSE 体系 · 筑牢安全底线",
|
|
|
desc: "全面贴合 QHSE 管理体系标准,覆盖风险隐患、作业许可、环境管理等核心模块,确保管理流程合规达标,契合油气行业安全环保监管要求,规避合规风险。",
|
|
|
+ image: new URL("../assets/images/value5.jpg", import.meta.url).href,
|
|
|
},
|
|
|
{
|
|
|
title: "多系统集成联动 · 实现协同管控",
|
|
|
desc: "深度集成设备管理、生产管理等系统,实现数据互通、流程联动,同步设备状态、作业计划等数据,打破系统壁垒,构建安全 + 生产 + 设备一体化管控体系。",
|
|
|
+ image: new URL("../assets/images/h.png", import.meta.url).href,
|
|
|
},
|
|
|
{
|
|
|
title: "全维度风险防控 · 保障作业安全",
|
|
|
desc: "整合风险隐患管理、应急与健康管理、作业许可与过程监督等功能,借助数采与数据处理能力,实现风险精准识别、隐患闭环治理、作业全过程可控。",
|
|
|
+ image: new URL("../assets/images/m.png", import.meta.url).href,
|
|
|
},
|
|
|
{
|
|
|
title: "体系化支撑 · 赋能管理升级",
|
|
|
desc: "涵盖目标管理、培训与体系支持等模块,联动平台数据资产,实现目标拆解、培训落地与体系落地,推动从被动应对到主动防控转型,提升管理效能。",
|
|
|
+ image: new URL("../assets/images/k.png", import.meta.url).href,
|
|
|
},
|
|
|
],
|
|
|
},
|
|
|
@@ -141,6 +156,58 @@ function scrollToTab(key) {
|
|
|
}
|
|
|
|
|
|
let observer = null;
|
|
|
+
|
|
|
+const currentImageIndex = ref({
|
|
|
+ platform: 0,
|
|
|
+ equipment: 0,
|
|
|
+ production: 0,
|
|
|
+ qhse: 0,
|
|
|
+});
|
|
|
+
|
|
|
+const imageTimers = ref({});
|
|
|
+
|
|
|
+const getActiveIndex = (key, totalItems) => {
|
|
|
+ const rawIndex = currentImageIndex.value[key];
|
|
|
+ const index = Number.isFinite(rawIndex) ? rawIndex : 0;
|
|
|
+ if (!totalItems || totalItems <= 0) return 0;
|
|
|
+ return ((index % totalItems) + totalItems) % totalItems;
|
|
|
+};
|
|
|
+
|
|
|
+const getActiveItem = (pillar) => {
|
|
|
+ const totalItems = pillar?.items?.length || 0;
|
|
|
+ const index = getActiveIndex(pillar?.key, totalItems);
|
|
|
+ return pillar?.items?.[index] || null;
|
|
|
+};
|
|
|
+
|
|
|
+const getActiveImageSrc = (pillar) =>
|
|
|
+ getActiveItem(pillar)?.image || pillar.cover;
|
|
|
+const getActiveImageAlt = (pillar) =>
|
|
|
+ getActiveItem(pillar)?.title || pillar.title;
|
|
|
+
|
|
|
+const startImageCarousel = (key, totalItems) => {
|
|
|
+ if (prefersReducedMotion()) return;
|
|
|
+ if (!totalItems || totalItems < 2) return;
|
|
|
+
|
|
|
+ stopImageCarousel(key);
|
|
|
+
|
|
|
+ imageTimers.value[key] = setInterval(() => {
|
|
|
+ currentImageIndex.value[key] =
|
|
|
+ (currentImageIndex.value[key] + 1) % totalItems;
|
|
|
+ }, 4000);
|
|
|
+};
|
|
|
+
|
|
|
+const stopImageCarousel = (key) => {
|
|
|
+ if (imageTimers.value[key]) {
|
|
|
+ clearInterval(imageTimers.value[key]);
|
|
|
+ imageTimers.value[key] = null;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const goToImageSlide = (key, index, totalItems) => {
|
|
|
+ currentImageIndex.value[key] = index;
|
|
|
+ startImageCarousel(key, totalItems);
|
|
|
+};
|
|
|
+
|
|
|
onMounted(() => {
|
|
|
const offset = headerOffsetPx();
|
|
|
observer = new IntersectionObserver(
|
|
|
@@ -164,11 +231,21 @@ onMounted(() => {
|
|
|
const el = document.getElementById(`product-${p.key}`);
|
|
|
if (el) observer.observe(el);
|
|
|
}
|
|
|
+
|
|
|
+ for (const p of pillars) {
|
|
|
+ if (currentImageIndex.value[p.key] === undefined)
|
|
|
+ currentImageIndex.value[p.key] = 0;
|
|
|
+ startImageCarousel(p.key, p.items?.length || 0);
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
observer?.disconnect?.();
|
|
|
observer = null;
|
|
|
+
|
|
|
+ for (const key of Object.keys(imageTimers.value || {})) {
|
|
|
+ stopImageCarousel(key);
|
|
|
+ }
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
@@ -230,18 +307,59 @@ onBeforeUnmount(() => {
|
|
|
<p class="productLead">{{ p.lead }}</p>
|
|
|
</header>
|
|
|
|
|
|
- <div v-if="p.key === 'platform'" class="platformLayout">
|
|
|
- <div class="platformImage">
|
|
|
- <img :src="p.cover" :alt="p.title" />
|
|
|
+ <div class="tabContentLayout">
|
|
|
+ <div class="tabContentImage">
|
|
|
+ <div class="imageCarouselWrapper">
|
|
|
+ <div class="imageCarouselViewport">
|
|
|
+ <Transition name="image-carousel-slide" mode="out-in">
|
|
|
+ <div
|
|
|
+ v-if="p.items?.length"
|
|
|
+ :key="`${p.key}-${getActiveIndex(p.key, p.items.length)}`"
|
|
|
+ class="imageCarouselSlide"
|
|
|
+ >
|
|
|
+ <img
|
|
|
+ :src="getActiveImageSrc(p)"
|
|
|
+ :alt="getActiveImageAlt(p)"
|
|
|
+ loading="lazy"
|
|
|
+ decoding="async"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </Transition>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="carouselIndicators">
|
|
|
+ <button
|
|
|
+ v-for="(item, idx) in p.items"
|
|
|
+ :key="idx"
|
|
|
+ :class="[
|
|
|
+ 'indicator',
|
|
|
+ { 'is-active': idx === currentImageIndex[p.key] },
|
|
|
+ ]"
|
|
|
+ @click="goToImageSlide(p.key, idx, p.items.length)"
|
|
|
+ @mouseenter="stopImageCarousel(p.key)"
|
|
|
+ @mouseleave="startImageCarousel(p.key, p.items.length)"
|
|
|
+ :aria-label="`第${idx + 1}张图片`"
|
|
|
+ ></button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="platformFeatures">
|
|
|
+ <div class="tabContentFeatures">
|
|
|
<div
|
|
|
- v-for="it in p.items"
|
|
|
+ v-for="(it, idx) in p.items"
|
|
|
:key="it.title"
|
|
|
- class="platformFeature"
|
|
|
+ class="tabFeatureRow"
|
|
|
+ :class="{
|
|
|
+ 'is-highlighted': idx === currentImageIndex[p.key],
|
|
|
+ }"
|
|
|
+ @mouseenter="
|
|
|
+ () => {
|
|
|
+ stopImageCarousel(p.key);
|
|
|
+ currentImageIndex[p.key] = idx;
|
|
|
+ }
|
|
|
+ "
|
|
|
+ @mouseleave="() => startImageCarousel(p.key, p.items.length)"
|
|
|
>
|
|
|
- <div class="featureDot" aria-hidden="true"></div>
|
|
|
- <div class="featureMain">
|
|
|
+ <div class="tabFeatureMain">
|
|
|
<div class="featureTitle">{{ it.title }}</div>
|
|
|
<div class="muted featureDesc">{{ it.desc }}</div>
|
|
|
</div>
|
|
|
@@ -249,15 +367,22 @@ onBeforeUnmount(() => {
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div v-else class="tabContentLayout">
|
|
|
- <div class="tabContentImage">
|
|
|
- <img :src="p.cover" :alt="p.title" />
|
|
|
- </div>
|
|
|
+ <!-- <div class="tabContentLayout" v-else>
|
|
|
<div class="tabContentFeatures">
|
|
|
<div
|
|
|
v-for="(it, idx) in p.items"
|
|
|
:key="it.title"
|
|
|
class="tabFeatureRow"
|
|
|
+ :class="{
|
|
|
+ 'is-highlighted': idx === currentImageIndex[p.key],
|
|
|
+ }"
|
|
|
+ @mouseenter="
|
|
|
+ () => {
|
|
|
+ stopImageCarousel(p.key);
|
|
|
+ currentImageIndex[p.key] = idx;
|
|
|
+ }
|
|
|
+ "
|
|
|
+ @mouseleave="() => startImageCarousel(p.key, p.items.length)"
|
|
|
>
|
|
|
<div class="tabFeatureMain">
|
|
|
<div class="featureTitle">{{ it.title }}</div>
|
|
|
@@ -265,7 +390,42 @@ onBeforeUnmount(() => {
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ <div class="tabContentImage">
|
|
|
+ <div class="imageCarouselWrapper">
|
|
|
+ <div class="imageCarouselViewport">
|
|
|
+ <Transition name="image-carousel-slide" mode="out-in">
|
|
|
+ <div
|
|
|
+ v-if="p.items?.length"
|
|
|
+ :key="`${p.key}-${getActiveIndex(p.key, p.items.length)}`"
|
|
|
+ class="imageCarouselSlide"
|
|
|
+ >
|
|
|
+ <img
|
|
|
+ :src="getActiveImageSrc(p)"
|
|
|
+ :alt="getActiveImageAlt(p)"
|
|
|
+ loading="lazy"
|
|
|
+ decoding="async"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </Transition>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="carouselIndicators">
|
|
|
+ <button
|
|
|
+ v-for="(item, idx) in p.items"
|
|
|
+ :key="idx"
|
|
|
+ :class="[
|
|
|
+ 'indicator',
|
|
|
+ { 'is-active': idx === currentImageIndex[p.key] },
|
|
|
+ ]"
|
|
|
+ @click="goToImageSlide(p.key, idx, p.items.length)"
|
|
|
+ @mouseenter="stopImageCarousel(p.key)"
|
|
|
+ @mouseleave="startImageCarousel(p.key, p.items.length)"
|
|
|
+ :aria-label="`第${idx + 1}张图片`"
|
|
|
+ ></button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div> -->
|
|
|
</section>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -327,7 +487,7 @@ onBeforeUnmount(() => {
|
|
|
background-repeat: no-repeat, no-repeat;
|
|
|
background-blend-mode: overlay;
|
|
|
color: #111;
|
|
|
- height: 40vh;
|
|
|
+ height: 50vh;
|
|
|
border-bottom: none;
|
|
|
padding: 80px 0;
|
|
|
}
|
|
|
@@ -492,6 +652,7 @@ onBeforeUnmount(() => {
|
|
|
|
|
|
.tabContentImage {
|
|
|
/* border-radius: 16px; */
|
|
|
+ --carousel-h: 500px;
|
|
|
overflow: hidden;
|
|
|
box-shadow: 0 12px 40px rgba(2, 6, 23, 0.12);
|
|
|
position: sticky;
|
|
|
@@ -500,7 +661,7 @@ onBeforeUnmount(() => {
|
|
|
|
|
|
.tabContentImage img {
|
|
|
width: 100%;
|
|
|
- height: 500px;
|
|
|
+ height: var(--carousel-h);
|
|
|
object-fit: cover;
|
|
|
display: block;
|
|
|
}
|
|
|
@@ -581,10 +742,7 @@ onBeforeUnmount(() => {
|
|
|
|
|
|
.tabContentImage {
|
|
|
position: static;
|
|
|
- }
|
|
|
-
|
|
|
- .tabContentImage img {
|
|
|
- height: 300px;
|
|
|
+ --carousel-h: 300px;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -620,4 +778,234 @@ onBeforeUnmount(() => {
|
|
|
transform: none !important;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+.imageCarouselWrapper {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.imageCarouselViewport {
|
|
|
+ flex: 1;
|
|
|
+ overflow: hidden;
|
|
|
+ position: relative;
|
|
|
+ height: var(--carousel-h);
|
|
|
+}
|
|
|
+
|
|
|
+.imageCarouselSlide {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.imageCarouselSlide img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+
|
|
|
+.imageOverlay {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ padding: 20px;
|
|
|
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent);
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.overlayText {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 1.4;
|
|
|
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
|
|
+}
|
|
|
+
|
|
|
+.image-carousel-slide-enter-active,
|
|
|
+.image-carousel-slide-leave-active {
|
|
|
+ transition: all 0.6s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.image-carousel-slide-enter-from {
|
|
|
+ opacity: 0;
|
|
|
+ transform: translateY(50px);
|
|
|
+}
|
|
|
+
|
|
|
+.image-carousel-slide-leave-to {
|
|
|
+ opacity: 0;
|
|
|
+ transform: translateY(-50px);
|
|
|
+}
|
|
|
+
|
|
|
+.carouselBtn {
|
|
|
+ width: 44px;
|
|
|
+ height: 44px;
|
|
|
+ border-radius: 50%;
|
|
|
+ border: 2px solid rgba(7, 102, 205, 0.3);
|
|
|
+ background: rgba(255, 255, 255, 0.95);
|
|
|
+ color: #0766cd;
|
|
|
+ cursor: pointer;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+ flex-shrink: 0;
|
|
|
+ z-index: 2;
|
|
|
+}
|
|
|
+
|
|
|
+.carouselBtn:hover {
|
|
|
+ background: #0766cd;
|
|
|
+ color: #fff;
|
|
|
+ border-color: #0766cd;
|
|
|
+ transform: scale(1.08);
|
|
|
+}
|
|
|
+
|
|
|
+.carouselBtn:focus-visible {
|
|
|
+ outline: none;
|
|
|
+ box-shadow: 0 0 0 3px rgba(7, 102, 205, 0.24);
|
|
|
+}
|
|
|
+
|
|
|
+.carouselIndicators {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: end;
|
|
|
+ gap: 10px;
|
|
|
+ /* margin-top: 16px; */
|
|
|
+ padding-right: 10px;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ top: -300px;
|
|
|
+}
|
|
|
+
|
|
|
+.indicator {
|
|
|
+ width: 8px;
|
|
|
+ height: 8px;
|
|
|
+ /* border-radius: 50%; */
|
|
|
+ border: none;
|
|
|
+ background: rgba(7, 102, 205, 0.25);
|
|
|
+ cursor: pointer;
|
|
|
+ padding: 0;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.indicator:hover {
|
|
|
+ background: rgba(7, 102, 205, 0.6);
|
|
|
+ transform: scale(1.3);
|
|
|
+}
|
|
|
+
|
|
|
+.indicator.is-active {
|
|
|
+ background: #0766cd;
|
|
|
+}
|
|
|
+
|
|
|
+.indicator:focus-visible {
|
|
|
+ outline: none;
|
|
|
+ box-shadow: 0 0 0 3px rgba(7, 102, 205, 0.24);
|
|
|
+}
|
|
|
+
|
|
|
+.tabFeatureRow {
|
|
|
+ padding: 28px;
|
|
|
+ background: #fff;
|
|
|
+ /* border-radius: 16px; */
|
|
|
+ box-shadow: 0 8px 24px rgba(2, 6, 23, 0.08);
|
|
|
+ min-height: 140px;
|
|
|
+ transition:
|
|
|
+ transform 180ms ease,
|
|
|
+ box-shadow 180ms ease,
|
|
|
+ border-left 0.3s ease;
|
|
|
+ border-left: 3px solid transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.tabFeatureRow:hover {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 12px 32px rgba(2, 6, 23, 0.12);
|
|
|
+}
|
|
|
+
|
|
|
+.tabFeatureRow.is-highlighted {
|
|
|
+ border-left: 3px solid #0766cd;
|
|
|
+ box-shadow: 0 8px 24px rgba(7, 102, 205, 0.15);
|
|
|
+}
|
|
|
+
|
|
|
+/* ... existing code ... */
|
|
|
+
|
|
|
+.tabContentImage {
|
|
|
+ /* border-radius: 16px; */
|
|
|
+ --carousel-h: 500px;
|
|
|
+ overflow: hidden;
|
|
|
+ box-shadow: 0 12px 40px rgba(2, 6, 23, 0.12);
|
|
|
+ position: sticky;
|
|
|
+ top: calc(var(--header-h) + 100px);
|
|
|
+}
|
|
|
+
|
|
|
+.imageCarouselWrapper {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.imageCarouselViewport {
|
|
|
+ flex: 1;
|
|
|
+ overflow: hidden;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.imageCarouselSlide {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.imageCarouselSlide img {
|
|
|
+ width: 100%;
|
|
|
+ height: 500px;
|
|
|
+ object-fit: cover;
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+
|
|
|
+.carouselIndicators {
|
|
|
+ position: absolute;
|
|
|
+ right: 20px;
|
|
|
+ top: 50%;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ z-index: 10;
|
|
|
+}
|
|
|
+
|
|
|
+.indicator {
|
|
|
+ width: 8px;
|
|
|
+ height: 8px;
|
|
|
+ border-radius: 50%;
|
|
|
+ border: none;
|
|
|
+ background: rgba(255, 255, 255, 0.5);
|
|
|
+ cursor: pointer;
|
|
|
+ padding: 0;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.indicator:hover {
|
|
|
+ background: rgba(255, 255, 255, 0.9);
|
|
|
+ transform: scale(1.3);
|
|
|
+}
|
|
|
+
|
|
|
+.indicator.is-active {
|
|
|
+ background: #014198;
|
|
|
+ width: 6px;
|
|
|
+ height: 20px;
|
|
|
+ border-radius: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.indicator:focus-visible {
|
|
|
+ outline: none;
|
|
|
+ box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.5);
|
|
|
+}
|
|
|
</style>
|