<header class="supt-hero-text-media supt-parallax-full-image " data-from-bottom="true" style="height: 100svh;">
<div class="supt-hero-text-media__media" style="height: 100%;">
<img src="/sites/gv/files/flmngr/hero-text-media.webp" alt="Hero Text Media" loading="eager" class="supt-parallax-full-image__image" />
</div>
<div class="supt-hero-text-media__content container">
<div class="supt-hero-text-media__content-inner row">
<div class="supt-hero-text-media__headline col-md-6 supt-slide-up-title">
<h1 class="supt-hero-text-media__title">We are Verisure</h1>
<p class="supt-hero-text-media__description">At Verisure, we believe that everyone has the right to feel safe and secure</p>
</div>
</div>
</div>
<div class="supt-scroll-item">
<p class="supt-scroll-item__text">Scroll down</p>
<span class="supt-scroll-item__inner">
<span class="supt-scroll-item__icon"></span>
</span>
<svg xmlns="http://www.w3.org/2000/svg" width="2" height="63" viewbox="0 0 2 63" fill="none" class="supt-scroll-item__line">
<path d="M1 1L1 63" stroke="url(#paint0_linear_10281_8341)" stroke-linecap="round" stroke-dasharray="1 4" />
<defs>
<linearGradient id="paint0_linear_10281_8341" x1="1.5" y1="1" x2="1.5" y2="63" gradientunits="userSpaceOnUse">
<stop offset="0.4" stop-color="#ED002F" />
<stop offset="1" stop-color="#ED002F" stop-opacity="0" />
</linearGradient>
</defs>
</svg>
</div>
</header>
No notes defined.
<header class="supt-hero-text-media supt-parallax-full-image {{ modifiers|modifiersAttr }}" data-from-bottom="true" {% if not modifiers|contains('small') %} style="height: 100svh;" {% endif %}>
<div class="supt-hero-text-media__media" style="height: 100%;">
{% if image %}
<img src="{{ path(image.src) }}" alt="{{ image.alt }}" loading="eager" class="supt-parallax-full-image__image"/>
{% elseif video %}
{% if video.sources %}
<video muted loop playsinline class="supt-parallax-full-image__image" width="100%" height="100%" style="object-fit: cover;">
{% for source in video.sources %}
<source src="{{ path(source.src) }}" type="{{ source.type }}">
{% endfor %}
</video>
{% else %}
<video muted loop playsinline class="supt-parallax-full-image__image" width="100%" height="100%" style="object-fit: cover;" src="{{ path(video.src) }}" poster="{{ path(video.poster) }}"/>
{% endif %}
{% endif %}
</div>
<div class="supt-hero-text-media__content container">
<div class="supt-hero-text-media__content-inner row">
<div class="supt-hero-text-media__headline col-md-6 supt-slide-up-title">
{% if date %}
{% include "atoms/post-metas/post-metas.twig" with {
"tag": {label: "News"},
"date": date
} only %}
{% endif %}
{% if title %}
<h1 class="supt-hero-text-media__title">{{ title }}</h1>
{% endif %}
{% if description %}
<p class="supt-hero-text-media__description">{{ description }}</p>
{% endif %}
</div>
</div>
</div>
{% include "atoms/scroll-item/scroll-item.twig" %}
</header>
{
"title": "We are Verisure",
"description": "At Verisure, we believe that everyone has the right to feel safe and secure",
"image": {
"src": "/sites/gv/files/flmngr/hero-text-media.webp",
"alt": "Hero Text Media"
}
}
.supt-hero-text-media {
display: flex;
position: relative;
min-height: 100svh;
height: 100svh;
@media (min-width: $breakpoint-xxl) {
min-height: 75svh;
height: 75svh;
}
&__media {
position: absolute;
top: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
object-fit: cover;
overflow: hidden;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
background: linear-gradient(
180deg,
rgb(0 0 0 / 70%) 0%,
rgb(0 0 0 / 10%) 50%,
rgb(0 0 0 / 70%) 100%
);
}
img,
video {
width: 100%;
height: 100%;
object-fit: cover;
}
}
&__content {
position: relative;
z-index: 2;
color: $color-white;
padding-top: $spacing-14;
padding-bottom: $spacing-40;
align-self: flex-end;
@media (min-width: $breakpoint-md) {
padding-top: $spacing-20;
}
}
&__headline {
@mixin clamp gap, $spacing-4, $spacing-6, $breakpoint-xs, $breakpoint-xl;
display: flex;
flex-direction: column;
.supt-post-metas,
.supt-tag__label {
color: $color-white;
}
}
&__title {
@extend %t-h1;
margin-bottom: 0;
}
&__description {
@extend %t-body-m;
a {
@extend %link-underline;
font-weight: 500;
outline: none;
}
/* No title, so make it bigger */
&:first-child {
@extend %t-h2;
}
}
&.-small {
height: auto;
min-height: auto;
/* This top spacing if to leave room for the nav & breadcrumb. */
padding-top: calc(69px + 18px + $spacing-4 * 2);
@media (min-width: $breakpoint-md) {
padding-top: calc(81px + 20px + $spacing-6 * 2);
}
.supt-hero-text-media__media {
img {
transform-origin: 50% 80%;
}
&::after {
background: linear-gradient(0deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.6) 100%);
}
}
.supt-hero-text-media__content {
padding-block: $spacing-10 $spacing-16;
@media (min-width: $breakpoint-md) {
padding-block: $spacing-16 $spacing-33;
}
}
}
&.-image-right {
.supt-hero-text-media__media {
img {
object-position: right;
}
}
}
& + .supt-section-team,
& + .supt-section-contact,
& + .supt-section-cards {
@mixin clamp padding-top, $spacing-16, $spacing-32, $breakpoint-xs, $breakpoint-xl, true;
}
& + .supt-listing-cards {
@mixin clamp padding-top, $spacing-8, $spacing-16, $breakpoint-xs, $breakpoint-xl, true;
}
& + .supt-single {
@mixin clamp padding-top, $spacing-8, $spacing-16, $breakpoint-xs, $breakpoint-xl, true;
}
}
import { animate, onScroll } from 'animejs';
export class HeroTextMedia {
private $element: HTMLElement;
private $scrollItem: HTMLElement;
private $video: HTMLVideoElement;
constructor($element: HTMLElement) {
this.$element = $element;
this.$scrollItem = $element.querySelector('.supt-scroll-item') as HTMLElement;
this.$video = $element.querySelector('video') as HTMLVideoElement;
void this.setupVideo();
this.setupScrollAnimation();
}
private setupScrollAnimation() {
animate(this.$scrollItem, {
opacity: [1, 0],
autoplay: onScroll({
container: document.body,
target: this.$element,
enter: 'bottom bottom',
leave: 'center bottom',
sync: true,
}),
});
}
private async setupVideo() {
if (!this.$video) {
return;
}
const isHls = this.$video.src?.endsWith('.m3u8') ?? false;
if (isHls && !this.$video.canPlayType('application/vnd.apple.mpegurl')) {
// @ts-expect-error
const { Hls } = await import('https://cdn.jsdelivr.net/npm/hls.js@latest/+esm');
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(this.$video.src as string);
hls.attachMedia(this.$video);
// Smart initial quality selection
hls.on(Hls.Events.MANIFEST_PARSED, (_event: any, data: any) => {
const viewportWidth = window.innerWidth;
const estimatedBandwidth = hls.bandwidthEstimate || 5000000; // fallback 5Mbps
// console.log(`Viewport width: ${viewportWidth}px`);
// console.log(`Estimated bandwidth: ${estimatedBandwidth / 1000} kbps`);
let initialLevel = 0; // fallback to lowest
// Iterate levels to find best fit
data.levels.forEach((level: any, index: number) => {
const fitsViewport = level.width <= viewportWidth * 1.5; // 1.5x to allow for pixel density
const fitsBandwidth = level.bitrate <= estimatedBandwidth * 0.8; // 80% safety margin
if (fitsViewport && fitsBandwidth) {
initialLevel = index; // pick highest level that fits
}
});
// console.log(
// `Starting at level ${initialLevel} (${data.levels[initialLevel].width}x${data.levels[initialLevel].height}, ${data.levels[initialLevel].bitrate / 1000} kbps)`
// );
hls.startLevel = initialLevel;
this.$video.play();
});
} else {
console.error('Your browser does not support HLS');
}
} else {
// Safari native HLS
this.$video.addEventListener('loadedmetadata', () => {
this.$video.play();
});
}
}
}