Add testimonials
This commit is contained in:
parent
06e943d478
commit
40223a12b5
7 changed files with 318 additions and 101 deletions
|
|
@ -22,6 +22,10 @@
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
|
||||
|
|
@ -54,13 +58,14 @@
|
|||
}
|
||||
|
||||
header h1 {
|
||||
margin: 0 0 10px;
|
||||
font-size: clamp(2.2rem, 3vw, 3.4rem);
|
||||
margin: 0;
|
||||
font-size: clamp(2.2rem, 5vw, 5rem);
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0 0 10px;
|
||||
font-size: clamp(2.2rem, 3vw, 3rem);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
|
@ -107,6 +112,12 @@
|
|||
color: var(--muted);
|
||||
}
|
||||
|
||||
.section-lead {
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
max-width: 700px;
|
||||
}
|
||||
|
||||
#what-we-do {
|
||||
background: #eef2ff;
|
||||
}
|
||||
|
|
@ -179,23 +190,31 @@
|
|||
.slides {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-height: 180px;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
@property --auto {
|
||||
syntax: '<number>';
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
|
||||
.slides-track {
|
||||
display: flex;
|
||||
width: calc(100% * var(--count));
|
||||
transform: translateX(calc(-33.3333% * var(--current, 0)));
|
||||
transition: transform 320ms ease;
|
||||
animation: auto-pan calc(var(--count) * 6s) infinite linear;
|
||||
}
|
||||
|
||||
.slide-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.slide {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
transition: opacity 250ms ease, transform 250ms ease;
|
||||
flex: 0 0 calc(100% / var(--count));
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
.slide.active {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.slide p {
|
||||
|
|
@ -207,11 +226,31 @@
|
|||
.controls {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
.control-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #c7cedc;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
transition: transform 150ms ease, background 150ms ease, border-color 150ms ease;
|
||||
}
|
||||
|
||||
.control-dot:hover {
|
||||
transform: scale(1.1);
|
||||
background: #eef2ff;
|
||||
}
|
||||
|
||||
.slides:has(.slide-toggle:checked) .slides-track {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.control-arrow {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 50%;
|
||||
|
|
@ -224,16 +263,83 @@
|
|||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
.control-arrow:hover {
|
||||
background: #eef2ff;
|
||||
}
|
||||
|
||||
.arrow-sets {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.arrow-set {
|
||||
display: none;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
padding: 0 8px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.arrow-set:first-of-type {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@keyframes auto-pan {
|
||||
0% {
|
||||
--auto: 0;
|
||||
}
|
||||
100% {
|
||||
--auto: calc(var(--count) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Active state wiring via :has so dots/arrows track selection. */
|
||||
.slides {
|
||||
--current: var(--auto);
|
||||
}
|
||||
|
||||
/* Default to first dot when nothing is picked. */
|
||||
.controls .control-dot:first-of-type {
|
||||
background: #eef2ff;
|
||||
border-color: #c7cedc;
|
||||
}
|
||||
|
||||
.arrow-set .control-arrow {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
{{- range $index, $_ := .Testimonials }}
|
||||
.slides:has(#t-{{$index}}:checked) {
|
||||
--current: {{$index}};
|
||||
}
|
||||
|
||||
.slides:has(#t-{{$index}}:checked) .controls .control-dot[for="t-{{$index}}"] {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.slides:has(#t-{{$index}}:checked) .arrow-set.arrows-{{$index}} {
|
||||
display: flex;
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
footer {
|
||||
background: #0b1224;
|
||||
color: #f1f5f9;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
footer .footer-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -274,7 +380,7 @@
|
|||
<section id="about">
|
||||
<div class="container">
|
||||
<h2>About Us</h2>
|
||||
<p class="muted" style="text-align:center; margin:0 auto; max-width:700px;">Three builders, one vision: ship reliable, human-centered software and services.</p>
|
||||
<p class="muted section-lead">Three builders, one vision: ship reliable, human-centered software and services.</p>
|
||||
<div class="about-grid">
|
||||
<div class="profile-card">
|
||||
<img src="headshots/chandlerswift.jpg"
|
||||
|
|
@ -303,18 +409,33 @@
|
|||
<section id="testimonials">
|
||||
<div class="container">
|
||||
<h2>What Our Clients Say</h2>
|
||||
<div class="testimonial-shell" data-slider>
|
||||
<div class="slides">
|
||||
{{range $index, $testimonial := .Testimonials}}
|
||||
<div class="slide {{if eq $index 0}}active{{end}}">
|
||||
<p>"{{$testimonial.Quote}}"</p>
|
||||
<strong>— {{$testimonial.Name}}, {{$testimonial.Position}}</strong>
|
||||
{{- $count := len .Testimonials -}}
|
||||
<div class="testimonial-shell">
|
||||
<div class="slides" style="--count: {{$count}};">
|
||||
<div class="slides-track">
|
||||
{{range $index, $testimonial := .Testimonials}}
|
||||
<input type="radio" name="testimonial" id="t-{{$index}}" class="slide-toggle" {{if eq $index 0}}checked{{end}}>
|
||||
<div class="slide">
|
||||
<p>"{{$testimonial.Quote}}"</p>
|
||||
<strong>— {{$testimonial.Name}}, {{$testimonial.Position}}</strong>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="arrow-sets">
|
||||
{{range $index, $_ := .Testimonials}}
|
||||
{{- $prev := mod (add $index (sub $count 1)) $count -}}
|
||||
{{- $next := mod (add $index 1) $count -}}
|
||||
<div class="arrow-set arrows-{{$index}}">
|
||||
<label class="control-arrow" for="t-{{$prev}}" aria-label="Previous testimonial">←</label>
|
||||
<label class="control-arrow" for="t-{{$next}}" aria-label="Next testimonial">→</label>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="controls">
|
||||
{{range $index, $_ := .Testimonials}}
|
||||
<label class="control-dot" for="t-{{$index}}" aria-label="Show testimonial {{$index}}"></label>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="controls">
|
||||
<button class="control-btn" data-prev aria-label="Previous testimonial">←</button>
|
||||
<button class="control-btn" data-next aria-label="Next testimonial">→</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -325,43 +446,12 @@
|
|||
<p>
|
||||
<b>Swift, Villnow & Swift Industries</b><br>
|
||||
{{.Occupation}} Division<br>
|
||||
<a href="tel:2182105180" style="color:#cbd5e1;">(218) 210-5180</a><br>
|
||||
<a href="mailto:sales@svsindustries.org" style="color:#cbd5e1;">sales@svsindustries.org</a>
|
||||
<a href="tel:2182105180">(218) 210-5180</a><br>
|
||||
<a href="mailto:sales@svsindustries.org">sales@svsindustries.org</a>
|
||||
</p>
|
||||
<p>© {{.CurrentYear}} Swift, Villnow & Swift Industries. All rights reserved.</p>
|
||||
<p>© {{.CurrentYear}} Swift, Villnow & Swift Industries<br>All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const slider = document.querySelector('[data-slider]');
|
||||
if (!slider) return;
|
||||
const slides = Array.from(slider.querySelectorAll('.slide'));
|
||||
const prev = slider.querySelector('[data-prev]');
|
||||
const next = slider.querySelector('[data-next]');
|
||||
let index = slides.findIndex(s => s.classList.contains('active'));
|
||||
|
||||
function show(i) {
|
||||
slides.forEach((s, idx) => {
|
||||
s.classList.toggle('active', idx === i);
|
||||
});
|
||||
}
|
||||
|
||||
function go(delta) {
|
||||
index = (index + delta + slides.length) % slides.length;
|
||||
show(index);
|
||||
}
|
||||
|
||||
prev?.addEventListener('click', () => go(-1));
|
||||
next?.addEventListener('click', () => go(1));
|
||||
|
||||
let timer = setInterval(() => go(1), 6500);
|
||||
slider.addEventListener('mouseenter', () => clearInterval(timer));
|
||||
slider.addEventListener('mouseleave', () => {
|
||||
timer = setInterval(() => go(1), 6500);
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue