83726ed040
Phones were downloading the full 1.5 MB / 2 MB hero JPEG. Now the image macro emits a `srcset` ladder (480/768/1200/1600/2560/3840/5345 w) with `sizes="100vw"`, so a 480 px-wide phone picks the ~19 KB variant and 5K screens still get the native resolution. Also drop `loading="lazy"` on the first slide — it is the LCP element and should load eager. Macro changes: - Add `srcset_widths` (CSV string, since Tera macros only allow scalar defaults) and `sizes` parameters. - Add `lazy=true` parameter; callers can pass `lazy=false` for above-the-fold images. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
129 lines
4.8 KiB
HTML
129 lines
4.8 KiB
HTML
{% extends "base.html" %}
|
|
{% import "macros/image.html" as image %}
|
|
{% import "macros/section_title.html" as st %}
|
|
|
|
{% block content %}
|
|
|
|
{# ───────── Carousel ───────── #}
|
|
<div id="x-corp-carousel" class="carousel slide hero-slide" data-ride="carousel">
|
|
<ol class="carousel-indicators">
|
|
{% for slide in section.extra.slides %}
|
|
<li data-target="#x-corp-carousel"
|
|
data-slide-to="{{ loop.index0 }}"
|
|
class="{% if loop.first %}active{% endif %}"></li>
|
|
{% endfor %}
|
|
</ol>
|
|
<div class="carousel-inner" role="listbox">
|
|
{% for slide in section.extra.slides %}
|
|
<div class="item{% if loop.first %} active{% endif %}">
|
|
{# Bootstrap-3 only constrains hero images via `max-width: 100%`,
|
|
so the natural image width has to cover the widest viewport
|
|
we care about. We hand the browser a `srcset` ladder up to
|
|
5345 px (the source's native width) and let it pick — phones
|
|
grab ~480-800 px, desktops 1600-2560 px, 5K screens the full
|
|
file. The first slide is the LCP element, so it loads eager. #}
|
|
{{ image::render(
|
|
src=slide.image,
|
|
alt=slide.alt,
|
|
srcset_widths="480,768,1200,1600,2560,3840,5345",
|
|
sizes="100vw",
|
|
lazy=not loop.first) }}
|
|
<div class="carousel-caption">
|
|
<p>{{ slide.heading }}</p>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
<a class="left carousel-control" href="#x-corp-carousel" role="button" data-slide="prev">
|
|
<i class="fa fa-angle-left" aria-hidden="true"></i>
|
|
<span class="sr-only">Previous</span>
|
|
</a>
|
|
<a class="right carousel-control" href="#x-corp-carousel" role="button" data-slide="next">
|
|
<i class="fa fa-angle-right" aria-hidden="true"></i>
|
|
<span class="sr-only">Next</span>
|
|
</a>
|
|
</div>
|
|
|
|
{# ───────── News (loaded from content/news.md) ───────── #}
|
|
{# Mirror live's news.html.twig: section-title always wraps the h2 in
|
|
`.anchor` (with empty id), and the content lands inside an outer <p>
|
|
tag — the auto-close on the first inner <p> yields one leading
|
|
empty <p></p>, which contributes the 10px-margin that Grav's markdown
|
|
pipeline produces. #}
|
|
{% set news_page = get_page(path="news.md") %}
|
|
<section class="news-text ptb-text" id="aktuelles">
|
|
{{ st::section_title(title="Aktuelles", with_anchor=true) }}
|
|
<div class="container">
|
|
<div class="row">
|
|
<p>{{ news_page.content | safe }}</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{# ───────── About columns ───────── #}
|
|
<section class="about-text ptb-100">
|
|
{{ st::section_title(title="Willkommen in unserer Praxis am Lienhardplatz") }}
|
|
<div class="container">
|
|
<div class="row">
|
|
{% for col in section.extra.about_columns %}
|
|
<div class="col-md-6">{{ col.text | markdown(inline=false) | safe }}</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{# ───────── Service grid ───────── #}
|
|
<section class="x-services ptb-100 gray-bg">
|
|
{{ st::section_title(title="Unsere Leistungen") }}
|
|
<div class="container">
|
|
{% for card in section.extra.service_cards %}
|
|
{% if loop.index0 % 2 == 0 %}<div class="row">{% endif %}
|
|
<div class="col-md-6">
|
|
<div class="thumbnail clearfix">
|
|
<a href="{{ card.url | safe }}">
|
|
{{ image::render(src=card.image, alt=card.title) }}
|
|
</a>
|
|
<div class="caption">
|
|
<h3><a href="{{ card.url | safe }}">{{ card.title }}</a></h3>
|
|
<p>{{ card.description }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% if loop.index0 % 2 == 1 or loop.last %}</div>{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
|
|
{# ───────── Team preview ───────── #}
|
|
{% if section.extra.team_preview %}
|
|
<section class="team ptb-100">
|
|
{{ st::section_title(title="Ihr Ärzteteam") }}
|
|
<div class="container">
|
|
<div class="row">
|
|
{% for member in section.extra.team_preview %}
|
|
<div class="col-md-4">
|
|
<figure class="thumbnail">
|
|
<a href="{{ member.url | safe }}">
|
|
{{ image::render(src=member.image, width=400, alt=member.name) }}
|
|
</a>
|
|
<figcaption class="caption text-center">
|
|
<h3>{{ member.name }} -
|
|
<small>{{ member.position }}</small>
|
|
</h3>
|
|
</figcaption>
|
|
</figure>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
{% endif %}
|
|
|
|
{# Notfall block intentionally skipped — was `published: false` in Grav. #}
|
|
|
|
{# Map embed removed — third-party iframes trigger GDPR cookie consent.
|
|
The live site had it removed for this reason; do not add back without
|
|
a consent gate. #}
|
|
|
|
{% endblock %}
|