/*
 *  Julia Hipp — Architektin & Innenarchitektin
 *
 *  Composed at 1560px and carried down from there: section 28 restates the
 *  tokens at each breakpoint rather than writing a second set of components.
 *  Design tokens live in section 02; everything else refers to them.
 *
 *  01  Fonts
 *  02  Tokens
 *  03  Base
 *  04  Loader
 *  05  Typography
 *  06  Grid
 *  07  Media & buttons
 *  08  Header
 *  09  Hero
 *  10  Statement
 *  11  Collage
 *  12  Banner
 *  13  Testimonial
 *  14  Projekte
 *  15  Footer
 *  16  Smooth scroll
 *  17  Page head
 *  18  Masonry
 *  19  Ablauf
 *  20  Contact
 *  21  Parallax
 *  22  Project detail
 *  23  Über mich
 *  24  Gallery & lightbox
 *  25  Beratung panel
 *  26  Leistungen
 *  27  Page transition
 *  28  Responsive
 */

/* 01 ############################### Fonts ############################### */

/* playfair-display-regular - latin */
@font-face {
  font-display: swap;
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 400;
  src: url('fonts/playfair-display-v40-latin-regular.woff2') format('woff2');
}

/* playfair-display-italic - latin */
@font-face {
  font-display: swap;
  font-family: 'Playfair Display';
  font-style: italic;
  font-weight: 400;
  src: url('fonts/playfair-display-v40-latin-regular-italic.woff2') format('woff2');
}

/* playfair-display-500 - latin */
@font-face {
  font-display: swap;
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 500;
  src: url('fonts/playfair-display-v40-latin-500.woff2') format('woff2');
}

/* playfair-display-500italic - latin */
@font-face {
  font-display: swap;
  font-family: 'Playfair Display';
  font-style: italic;
  font-weight: 500;
  src: url('fonts/playfair-display-v40-latin-500italic.woff2') format('woff2');
}

/* playfair-display-600 - latin */
@font-face {
  font-display: swap;
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 600;
  src: url('fonts/playfair-display-v40-latin-600.woff2') format('woff2');
}

/* roboto-300 - latin */
@font-face {
  font-display: swap;
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 300;
  src: url('fonts/roboto-v51-latin-300.woff2') format('woff2');
}

/* roboto-regular - latin */
@font-face {
  font-display: swap;
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  src: url('fonts/roboto-v51-latin-regular.woff2') format('woff2');
}

/* roboto-500 - latin */
@font-face {
  font-display: swap;
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  src: url('fonts/roboto-v51-latin-500.woff2') format('woff2');
}


*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* 02 ############################## Tokens ############################### */

:root {
    --primary:    #FFFFFF;   /* page background */
    --off:        #F2EFE8;   /* recessed surface — placeholder frames, cut-outs */
    --green:      #1C3A2B;   /* brand — hero + testimonial band */
    --green-deep: #122619;   /* footer + cta */
    --green-shade:#060D08;   /* the layer that lifts off the footer */
    --green-soft: #C9D4CB;   /* muted text on green */
    --gold:       #B98C4A;   /* accent — mini-title rule */
    --white:      #ffffff;
    --ink:        #1B1A16;   /* headings / strong text on paper */
    --text:       #3A4038;   /* body copy on paper */
    --muted:      #55534B;   /* secondary copy */
    --line:       rgba(27, 26, 22, 0.14);
    --white-soft: rgba(255, 255, 255, 0.84);   /* muted copy on a photograph */
    --scrim:      rgba(10, 23, 16, 0.5);       /* behind a panel or an overlay */
    --serif:      'Playfair Display', Georgia, serif;
    --sans:       'Roboto', system-ui, sans-serif;

    /* ---- Type ------------------------------------------------------- */
    --fs-small:   0.95rem;   /* everything below body: labels, eyebrows, small print */
    --fs-body:    1.2rem;    /* body copy */
    --fs-h1:      4.6rem;
    --fs-h2:      3.4rem;
    --fs-h3:      1.5rem;
    --fs-display: 4.4rem;    /* hero and project-hero titles */

    --fw-light:   300;
    --fw-regular: 400;
    --fw-medium:  500;

    --track-label: 0.14em;   /* buttons, nav, small caps */
    --track-wide:  0.16em;   /* eyebrows, section labels */
    --lh-tight:    1.12;     /* headings */
    --lh-body:     1.85;

    /* ---- Space ------------------------------------------------------ */
    --page-max:  1560px;
    --overhang:  9rem;       /* how far a banner plate hangs past its band */
    --gutter:    3.5rem;     /* the grid's column and row gap */
    --gutter-lg: 4.5rem;

    --space-xs:  1rem;
    --space-sm:  1.4rem;
    --space-md:  2rem;
    --space-lg:  3rem;
    --space-xl:  4rem;
    --space-2xl: 6rem;
    --space-3xl: 9rem;
    --space-4xl: 12rem;

    /* ---- Motion ----------------------------------------------------- */
    --ease-out:  cubic-bezier(0.22, 1, 0.36, 1);
    --ease-swap: cubic-bezier(0.32, 0.72, 0, 1);   /* the arrow crossing a label */
    --dur-fast: 0.3s;
    --dur-mid:  0.6s;

    /* ---- Layers ----------------------------------------------------- */
    --z-header:   100;
    --z-lightbox: 200;
    --z-transition: 500;
    --z-scrim:    300;
    --z-panel:    301;
    --z-loader:   999;
}

/* 03 ############################### Base ################################ */

html {
    scroll-behavior: smooth;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

body {
    background: var(--primary);
    color: var(--text);
    font-family: var(--sans);
    font-size: var(--fs-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-regular);
}

img {
    max-width: 100%;
    vertical-align: middle;
}

section {
    position: relative;
}

p + p, p + ul, ul + p { margin-top: var(--space-sm); }


/* 04 ############################## Loader ############################### */

/* `is-loading` is only ever set from script, so with JS off nothing locks. */
html.is-loading, html.is-loading body { overflow: hidden; height: 100%; }

.loader { display: none; }

/* The strip sits centred at rest, so it would flash between first paint and
   GSAP's first set. Stay invisible until main.js has parked it off-stage. */
html.is-loading .loader__wrap { opacity: 0; }
html.is-loading.is-loader-ready .loader__wrap { opacity: 1; }

html.is-loading .loader {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: var(--z-loader);
    background: var(--green);
}

.loader__stage {
  --size-font: 1vw;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  font-size: 1vw;
  display: flex;
  position: absolute;
  top: 0;
  left: 0;
  overflow: hidden;
}

.loader__wrap {
  font-size: var(--size-font);
  justify-content: center;
  align-items: center;
  display: flex;
  position: relative;
}

.loader__groups {
  position: relative;
  overflow: hidden;
}

.loader__group {
  justify-content: center;
  align-items: center;
  display: flex;
  position: relative;
}

.loader__single {
  padding-left: 1em;
  padding-right: 1em;
  position: relative;
}

.loader__media {
  justify-content: center;
  align-items: center;
  width: 10em;
  height: 10em;
  display: flex;
  position: relative;
}

.loader__media.is--scaling {
  will-change: transform;
  display: flex;
}

.loader__img {
  object-fit: cover;
  width: 100%;
  height: 100%;
  position: absolute;
}

.loader__group.is--relative {
  position: relative;
  left: 100%;
}

.loader__group.is--duplicate {
  position: absolute;
}

.loader__img.is--scale-down {
  will-change: transform;
}

.loader__fade {
  pointer-events: none;
  background-image: linear-gradient(90deg, var(--green) 20%, rgba(28, 58, 43, 0));
  width: 5em;
  height: calc(100% + 2px);
  position: absolute;
  top: -1px;
  left: -1px;
}
.loader__fade.is--duplicate {
  left: auto;
  right: -1px;
  transform: scaleX(-1);
}


/* 05 ############################ Typography ############################# */

/* Headings are Playfair throughout; the <span> inside one turns italic. */
h1, h2, h3 {
    font-family: var(--serif);
    font-weight: var(--fw-regular);
    line-height: var(--lh-tight);
    color: var(--green);
}

/* Pull quotes stay Playfair — they are a voice, not a heading. */
q {
    font-family: var(--serif);
    font-weight: normal;
    line-height: 1.4;
    color: var(--green);
}

/* Italic emphasis: the one phrase inside a heading that leans. */
h1 span, h2 span, h3 span {
    font-weight: var(--fw-medium);
    font-style: italic;
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }

.statement {
    font-size: 3rem;
    line-height: 1.18;
}

/* Mini-title — the gold-ruled eyebrow above a section heading */
p.mini-title {
    font-family: var(--sans);
    font-size: var(--fs-small);
    font-weight: var(--fw-medium);
    letter-spacing: var(--track-wide);
    text-transform: uppercase;
    color: var(--gold);
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    margin-bottom: var(--space-sm);
}

.mini-title::before {
    content: '';
    display: block;
    width: 60px;
    height: 1px;
    background: var(--gold);
    flex-shrink: 0;
}

/* A rule to the left of the word sets it off against the margin. Centred,
   there is no margin to set it off against — the rule just knocks the word
   off centre — so it comes away. */
.mini-title--center {
    justify-content: center;
}
.mini-title--center::before,
.text-center .mini-title::before { content: none; }

/* On the green bands the rule + label lift to cream */
.on-green .mini-title,
.on-green .mini-title::before { color: var(--green-soft); background-color: var(--green-soft); }
.on-green .mini-title { background: none; }

/* Except where the band is the green itself, with no photograph under it —
   there the gold carries, where cream only goes quiet. */
.on-green .mini-title--gold { color: var(--gold); }
.on-green .mini-title--gold::before { background-color: var(--gold); }

.lede {
    color: var(--muted);
    max-width: 46ch;
}

/* The h1 every page opens with, and the line under it — the same treatment
   whether the page starts with a photograph or with paper. Colour comes from
   the band: a green one lifts them, on paper the heading keeps the colour
   every other heading has. Where a title needs more room the span it is given
   changes, not its size. */
.page-title {
    font-size: var(--fs-display);
    margin-bottom: var(--space-md);
}
.page-lead {
    font-size: 1.4rem;
    font-weight: var(--fw-light);
    margin: 0;
}
.on-green :is(.page-title, .page-title span) { color: var(--white); }
.on-green .page-lead { color: var(--white-soft); }

/* We self-host 300, 400 and 500 — nothing heavier. Left at the browser's
   default a <strong> asks for 700, which it then either fakes or borrows from
   whatever Roboto the visitor happens to have. 500 is the weight this site
   emphasises with everywhere else, and it is one we actually ship. */
strong { font-weight: var(--fw-medium); }

.text-center { text-align: center; }


/* 06 ############################### Grid ################################ */

.row {
    max-width: var(--page-max);
    margin: 0 auto;
    display: grid;
}

.two-c    { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gutter); }
.three-c  { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--gutter); }
.twelve-c { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gutter); }
.sixteen-c{ display: grid; grid-template-columns: repeat(16, 1fr); gap: var(--gutter); }

/* Column Span */
.c-span-1  { grid-column: span 1 / span 1; }
.c-span-2  { grid-column: span 2 / span 2; }
.c-span-3  { grid-column: span 3 / span 3; }
.c-span-4  { grid-column: span 4 / span 4; }
.c-span-5  { grid-column: span 5 / span 5; }
.c-span-6  { grid-column: span 6 / span 6; }
.c-span-7  { grid-column: span 7 / span 7; }
.c-span-8  { grid-column: span 8 / span 8; }
.c-span-9  { grid-column: span 9 / span 9; }
.c-span-10 { grid-column: span 10 / span 10; }
.c-span-11 { grid-column: span 11 / span 11; }
.c-span-12 { grid-column: span 12 / span 12; }
.c-span-13 { grid-column: span 13 / span 13; }
.c-span-14 { grid-column: span 14 / span 14; }
.c-span-15 { grid-column: span 15 / span 15; }
.c-span-16 { grid-column: span 16 / span 16; }

/* Column Start */
.c-start-1  { grid-column-start: 1; }
.c-start-2  { grid-column-start: 2; }
.c-start-3  { grid-column-start: 3; }
.c-start-4  { grid-column-start: 4; }
.c-start-5  { grid-column-start: 5; }
.c-start-6  { grid-column-start: 6; }
.c-start-7  { grid-column-start: 7; }
.c-start-8  { grid-column-start: 8; }
.c-start-9  { grid-column-start: 9; }
.c-start-10 { grid-column-start: 10; }
.c-start-11 { grid-column-start: 11; }
.c-start-12 { grid-column-start: 12; }
.c-start-13 { grid-column-start: 13; }
.c-start-14 { grid-column-start: 14; }
.c-start-15 { grid-column-start: 15; }
.c-start-16 { grid-column-start: 16; }

/* Gaps. One, because one is what the page asks for — the others were written
   for a scale nothing turned out to need. */
.gap-450 { gap: var(--gutter-lg); }

/* Vertical rhythm. padding-block, never the shorthand: these land on a .row
   as often as on a section, and a shorthand would wipe the side padding the
   row is given once the window is narrower than the composition. */
.spacing-400    { padding-block: var(--space-xl); }
.spacing-700    { padding-block: 7rem; }
.spacing-900    { padding-block: var(--space-3xl); }
.spacing-1200   { padding-block: var(--space-4xl); }
.no-spacing-top    { padding-top: 0; }
.no-spacing-bottom { padding-bottom: 0; }

/* A centred measure for text that stands on its own — the intro and the
   closing paragraphs on a project page. */
.header-narrow { max-width: 770px; margin: 0 auto; }

.v-center { display: flex; flex-direction: column; justify-content: center; }


/* 07 ########################## Media & buttons ########################## */

/* Placeholder frame — the tint shows while the photo loads; object-fit fills. */
.ph { position: relative; overflow: hidden; background: var(--off); }
.on-green .ph, .banner.ph { background: var(--green-deep); }
.ph img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* A frame with no shape of its own — the photograph's proportions decide it.
   Without this the image is stretched to the height of its grid row, which in
   a single column there is none of, and the picture disappears. */
.ph--auto img { height: auto; }

/* Both wear the same uppercase label with a crossing arrow. The pill carries a
   border and padding; the text link is the label on its own. */
.btn, .link-more {
    display: inline-grid;
    font-family: var(--sans);
    font-size: var(--fs-body);
    font-weight: var(--fw-medium);
    line-height: 1;
    background: none;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    /* A grid or flex parent would otherwise stretch it to the whole column. */
    width: fit-content;
}

.btn {
    border: 1px solid currentColor;
    border-radius: 2.5em;
    transition: background-color var(--dur-fast) ease, color var(--dur-fast) ease;
}
.btn .swap { --swap-pad-l: 2.4em; --swap-pad-r: 2em; padding: 1.35em var(--swap-pad-r) 1.35em var(--swap-pad-l); }

.btn--light { color: var(--white); border-color: rgba(255, 255, 255, 0.4); }
.btn--light:hover { background: var(--white); color: var(--green); }

.link-more { border: 0; color: var(--ink); transition: opacity var(--dur-fast) ease; }
.link-more:hover { opacity: 0.55; }
.on-green .link-more { color: var(--white); }
/* A flex column does not collapse margins, so a link closing a run of copy
   there needs the gap the paragraphs give each other. */
.v-center :is(p, ul) + :is(.link-more, .btn) { margin-top: var(--space-md); }

/* The arrow crosses the label: the trailing one slides out to the right, the
   leading one arrives from the left, and the text steps aside to make room.
   The travel is derived from the host's own padding, so one rule serves both. */
.swap {
    --swap-icon: 0.875em;
    --swap-gap: 0.5em;
    --swap-pad-l: 0em;
    --swap-pad-r: 0em;

    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--swap-gap);
    position: relative;
    overflow: clip;
    pointer-events: none;
    /* The label's line-height is 1, so the clip box would cut the dots off an
       uppercase Ä. The pill sets its own padding and already has the room. */
    padding-block: 0.22em;
}

.swap__icon {
    width: var(--swap-icon);
    height: var(--swap-icon);
    flex: none;
    transition: translate 0.5s var(--ease-swap), opacity 0.15s ease-out;
}
.swap__icon.is--left {
    position: absolute;
    left: var(--swap-pad-r);
    opacity: 0;
    translate: -1.5em 0 0;
}
.swap__text { flex: none; transition: translate 0.5s var(--ease-swap); }

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
    :is(.btn, .link-more):is(:hover, :focus-visible) .swap__text {
        translate: calc(var(--swap-icon) + var(--swap-gap) + var(--swap-pad-r) - var(--swap-pad-l)) 0 0;
        transition: translate 0.5s 0.075s var(--ease-swap);
    }
    :is(.btn, .link-more):is(:hover, :focus-visible) .swap__icon.is--left {
        translate: 0 0 0;
        opacity: 1;
        transition: translate 0.5s 0.075s var(--ease-swap), opacity 0.15s 0.075s ease-out;
    }
    :is(.btn, .link-more):is(:hover, :focus-visible) .swap__icon.is--right {
        translate: 1.5em 0 0;
        opacity: 0;
        transition: translate 0.5s 0.05s var(--ease-swap), opacity 0.15s 0.075s ease-out;
    }
}

a { color: inherit; text-decoration: none; }


/* ---- Underline link ---------------------------------------------------- */

/* Osmo's underline animation, unaltered: the rule leaves to the right and the
   new one arrives from the left, so it reads as one stroke crossing the word
   rather than as a line switching off and on. */
[data-underline-link] {
  text-decoration: none;
  position: relative;
}

[data-underline-link]::before {
  content: "";
  position: absolute;
  bottom: -0.0625em;
  left: 0;
  width: 100%;
  height: 0.0625em;
  background-color: currentColor;
  transition: transform 0.735s cubic-bezier(0.625, 0.05, 0, 1);
  transform-origin: right;
  transform: scaleX(0) rotate(0.001deg);
}

@media (hover: hover) and (pointer: fine) {
  [data-hover]:hover [data-underline-link]::before,
  [data-underline-link]:hover::before {
    transform-origin: left;
    transform: scaleX(1) rotate(0.001deg);
  }
}


/* 08 ############################## Header ############################### */

.site-header {
    position: absolute;   /* floats over the green hero */
    top: 0; left: 0;
    width: 100%;
    z-index: var(--z-header);
    color: var(--white);
}

/* The mark holds the left edge; the links and the call to action sit together
   against the right, divided by a hairline rather than by a button. */
.site-header__inner {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    height: 96px;
}
.site-header__inner .brand   { justify-self: start; }
.site-header__inner .site-nav { justify-self: end; }
.site-header__inner .nav-cta { justify-self: end; }

/* Two files, one for each masthead colour — picked in site_head(). */
.brand { display: inline-block; }
.brand img { max-width: 130px; height: auto; display: block; }

.site-nav { display: flex; gap: 2.6rem; }
.site-nav a { font-weight: var(--fw-medium); }
/* Hovering draws the underline; the page you are on keeps its own, quieter
   mark so the two never say the same thing. */
.site-nav a.is-active { opacity: 0.6; }

/* Inner pages have no dark hero — flip the masthead to ink on paper */
body:not(.has-hero) .site-header { position: relative; color: var(--ink); border-bottom: 1px solid var(--line); }

/* …except where a page runs a picture up behind it: still ink, but floating,
   and without the rule that would draw a line across the picture. */
body.has-overlay-header .site-header { position: absolute; border-bottom: 0; }


/* 09 ############################### Hero ################################ */

/* Full viewport height so the loader's centre frame scales straight into it. */
.hero {
    position: relative;
    min-height: 100dvh;
    display: flex;
    align-items: stretch;
    background: var(--green);
    color: var(--white);
    overflow: hidden;
}

.hero__media { position: absolute; inset: 0; background: var(--green); }

/* The veil runs on the diagonal: deepest in the bottom-left corner where the
   copy sits, and gone entirely by the top-right, so that corner of the
   photograph is shown as shot. The far stop is a transparent green rather than
   `transparent`, which some engines fade through black and leave a grey cast. */
.hero__scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(
        to top right,
        rgba(18, 38, 25, 0.88) 0%,
        rgba(18, 38, 25, 0.62) 35%,
        rgba(18, 38, 25, 0.28) 65%,
        rgba(18, 38, 25, 0) 100%);
}

/* The banner rides the twelve-column grid, so the copy block is placed with the
   same c-span-* / c-start-* utilities as everything else and can be moved
   without touching this file. `align-items: stretch` lets the vertical
   modifiers below decide where in the band it sits. */
.hero__inner {
    position: relative;
    width: 100%;
    padding-top: 96px;
    align-items: stretch;
    min-height: 100dvh;
}

/* subgrid: the block's columns ARE the page's, so eyebrow, headline, lede and
   button each take their own c-span-* / c-start-* and stay locked to the page
   grid while they move. Column numbers inside the block are relative to the
   columns it spans (1..7 as it stands). */
.hero__content {
    display: grid;
    grid-template-columns: subgrid;
    align-content: center;
}

/* The home banner is a portrait, and centred the copy lands across her face.
   It drops to the foot of the band instead — the only page where the picture
   behind the words is a person. */
.page-home .hero__content { align-content: end; }
.page-home .hero__inner  { padding-bottom: var(--space-4xl); }

/* Zero-specificity default so every child spans the block unless a utility says
   otherwise — a plain `.c-span-4` on a child has to be able to win. */
:where(.hero__content) > * { grid-column: 1 / -1; }

/* The copy fills its columns, the button does not — and it carries the space
   above it, so the lead can stay the same paragraph on every banner. */
.hero__content .btn { justify-self: start; margin-top: var(--space-md); }


@supports not (grid-template-columns: subgrid) {
    .hero__content { grid-template-columns: repeat(7, 1fr); column-gap: var(--gutter); }
}


/* Grid items are stretched to the column width, so the copy no longer needs a
   measure of its own — the span sets it. */
.hero__content .lede { max-width: none; }



/* 10 ############################# Statement ############################# */

.statement { color: var(--green); margin: 0 auto; max-width: 22ch; }


/* 11 ############################## Collage ############################## */

.collage-section {
  font-size: 21px;           /* the em basis for the whole composition */
  justify-content: center;
  align-items: center;
  display: flex;
  overflow: clip;
}

.collage {
  width: 63.75em;
  height: 37em;
  flex: none;
}

.collage__list {
  width: 100%;
  height: 100%;
  position: relative;
}

.collage__item {
  z-index: 0;
  position: absolute;
  top: 0;
  left: 0;
}

.collage__item.is--1 {top: 6em;}
.collage__item.is--2 {top: 17em; left: 5em;}
.collage__item.is--3 {z-index: 1;top: 14em;left: 13.5em;}
.collage__item.is--4 {left: 24em;}
.collage__item.is--5 {z-index: 3;top: auto;bottom: 0;left: 26em;}
.collage__item.is--6 {z-index: 2;top: 9em;left: 31.5em;}
.collage__item.is--7 {z-index: 1;top: 16em;left: 39em;}
.collage__item.is--8 {top: 5em;left: auto;right: 0;}

/* Only the marquee below 768 has anywhere to put it. */
.collage__item.is--lap-two { display: none; }

.collage__card {
  aspect-ratio: 1;
  width: 16.25em;
  position: relative;
  background: var(--off);
  overflow: hidden;
}

.collage__card.is--landscape {
  aspect-ratio: 4 / 3;
  width: 20em;
}

.collage__card.is--portrait {
  aspect-ratio: 3 / 4;
  width: 15em;
}

.collage__img {
  object-fit: cover;
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  border: 10px solid var(--white);
}


/* 12 ############################## Banner ############################### */

.banner { aspect-ratio: 21 / 8; }


/* 13 ############################ Testimonial ############################ */

.testimonial {
    background: var(--green);
    color: var(--white);
    /* faint marble veining, subtle enough to keep text crisp */
    background-image:
        radial-gradient(120% 80% at 82% 8%, rgba(255, 255, 255, 0.06), transparent 60%),
        radial-gradient(90% 60% at 8% 92%, rgba(0, 0, 0, 0.28), transparent 60%);
}

/* The quotes take the left half of the row, a photograph the right. One quote
   at a time: the slides are stacked in one grid cell, so the block is as tall
   as the longest of them and nothing shifts as they change. */
/* The frame owns the right half of the band outright: it is placed against the
   section rather than the row, so the section's own top and bottom padding does
   not hold it off and it carries on past the grid to the window edge. */
.voices__visual {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    left: 50%;
}

.voices__head {
    display: flex;
    align-items: center;
    gap: var(--space-md);
    margin-bottom: var(--space-2xl);
}
.voices__head .mini-title { margin-bottom: 0; margin-right: auto; }

.voices__count {
    font-size: var(--fs-small);
    color: var(--green-soft);
}
.voices__count [data-current] { display: inline-block; width: 1ch; color: var(--white); }

.voices__controls { display: flex; gap: 0.6rem; }
.voices__button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.8rem;
    height: 2.8rem;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: 50%;
    background: none;
    color: var(--white);
    cursor: pointer;
    transition: background-color var(--dur-fast) ease, color var(--dur-fast) ease;
}
.voices__button:hover { background: var(--white); color: var(--green); }
.voices__button svg { width: 0.8rem; height: 0.8rem; display: block; }

/* Read as a list until script says otherwise: every quote on the page, no
   controls to press. `is--sliding` is what stacks them into one cell. */
.voices__list {
    display: flex;
    flex-direction: column;
    gap: var(--space-3xl);
}
.voices__count, .voices__controls { display: none; }

.voices__item {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-xl);
}

.voices.is--sliding .voices__count { display: block; }
.voices.is--sliding .voices__controls { display: flex; }
.voices.is--sliding .voices__list { display: grid; gap: 0; }
.voices.is--sliding .voices__item { grid-area: 1 / 1; visibility: hidden; opacity: 0; }
.voices.is--sliding .voices__item.is--active { visibility: visible; opacity: 1; }

/* The quote is the one thing here set in the serif; the attribution under it
   stays in the sans, like every other name on the site. */
.voices__quote {
    font-family: var(--serif);
    font-size: var(--fs-h3);
    line-height: 1.45;
    color: var(--white);
}
/* The marks a <q> adds itself become lines of their own once the text is split,
   so they sit alone above and below the quote. The name underneath is what
   says this is someone speaking. */
.voices__quote::before, .voices__quote::after { content: none; }
.voices__name { font-weight: var(--fw-medium); color: var(--white); }
/* The speaker's line and their role are two paragraphs, so the general
   `p + p` rhythm would push them apart; they belong together. */
.voices__role { color: var(--green-soft); font-size: var(--fs-small); margin-top: 0; padding-top: 0; }

/* SplitText's masks clip a descender or an accent without this. */
.text-line-mask { padding-bottom: 0.15em; margin-bottom: -0.15em; }


/* 14 ############################# Projekte ############################## */

/* A project still being built says so where the year would be. */
.project__wip { color: var(--gold); }

.projects__head { align-items: end; margin-bottom: var(--space-xl); }
.projects__head .link-more { justify-self: end; align-self: end; }

/* Two columns, filled top to bottom — the plates' differing crops are what give
   the columns their uneven edges. They are two boxes rather than a column
   count, so the right one can start lower than the left: level tops read as a
   row of two, and the offset is what makes it a masonry. */
.projects__masonry {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--gutter);
    align-items: start;
}
/* Measured off the reference: the right column starts about a third of the
   way down the first plate, not a sixth. */
.projects__col + .projects__col { margin-top: 12rem; }

/* The wrapper holds the place in the column flow; the plate inside is what
   drifts, so the columns keep their spacing however far it travels. */
.project-float {
    break-inside: avoid;
    margin-bottom: 3.5rem;
}

.project { display: block; }

.project__img { overflow: hidden; }
.project.is--tall   .project__img.ph { aspect-ratio: 2 / 3; }
.project.is--wide   .project__img.ph { aspect-ratio: 4 / 3; }
.project.is--square .project__img.ph { aspect-ratio: 5 / 4; }
.project__img img { transition: transform var(--dur-mid) var(--ease-out); }
.project:hover .project__img img { transform: scale(1.04); }

/* Understated caption — title + year sitting quietly below the image */
.project__cap { margin-top: 1.2rem; }
.project__title { font-family: var(--serif); font-size: var(--fs-h3); color: var(--green); display: block; }
.project__year { font-size: var(--fs-small); letter-spacing: 0.06em; color: var(--muted); margin-top: 0.3rem; display: block; }


/* 15 ############################## Footer ############################### */

/* The footer drifts up into place as it enters, with a dark layer lifting off
   it. The wrapper clips that travel; without JS the footer just sits still. */
.footer-wrap {
    position: relative;
    overflow: hidden;
}

.footer-wrap__dark {
    position: absolute;
    inset: 0;
    background: var(--green-shade);
    opacity: 0;
    pointer-events: none;
}

.site-footer {
    background: var(--green-deep);
    color: var(--white);
    border-top: 1px solid rgba(255, 255, 255, 0.1);
}

/* Invitation on the left, menu and contact stacked to its right. */
.site-footer__top {
    grid-template-columns: 2fr 1fr 1fr;
    gap: var(--gutter);
    padding-block: 7rem 5rem;
}

.footer-cta__title {
    color: var(--white);
    font-size: 3rem;
    max-width: 15ch;
    margin: 0 0 var(--space-md);
}
.footer-cta__title span { color: var(--white); }
.footer-cta__mail {
    font-size: 1.05rem;
    color: var(--white);
    border-bottom: 1px solid rgba(255, 255, 255, 0.4);
    padding-bottom: 0.2rem;
    transition: border-color var(--dur-fast) ease;
}
.footer-cta__mail:hover { border-color: var(--white); }

.footer-col { display: flex; flex-direction: column; }
.footer-col :where(a, span) { color: var(--green-soft); font-size: var(--fs-small); line-height: 2; transition: color var(--dur-fast) ease; }
.footer-col a:hover { color: var(--white); }
.footer-col__title {
    font-size: var(--fs-small);
    text-transform: uppercase;
    letter-spacing: var(--track-wide);
    color: var(--white);
    margin-bottom: 0.9rem;
}

.footer-meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    padding-block: var(--space-md) 2.5rem;
    font-size: var(--fs-small);
    color: var(--green-soft);
}
.footer-meta .brand img { max-width: 96px; }
.footer-meta__legal { display: flex; gap: 1.6rem; }
.footer-meta a:hover { color: var(--white); }


/* 16 ########################### Smooth scroll ########################### */

/* lenis@1.2.3 dist/lenis.css, inlined verbatim. `scroll-behavior: smooth` on
   <html> stays as the fallback for when Lenis is not running. */
html.lenis,html.lenis body{height:auto}.lenis.lenis-smooth [data-lenis-prevent]{overscroll-behavior:contain}.lenis.lenis-stopped{overflow:clip}.lenis.lenis-smooth iframe{pointer-events:none}


/* 17 ############################# Page head ############################# */

/* Every page except home opens with the same block: eyebrow, title, lede. It
   sits on the twelve-column grid, indented one column like the banner title
   on Leistungen, so the span is what decides how wide the title runs — not a
   character count. */
.page-head__lede { max-width: 52ch; }


/* 18 ############################## Masonry ############################## */

[data-masonry] {
  --masonry-col: 3;
  --masonry-gap: 2.5rem;
}

.masonry-list {
  grid-column-gap: var(--masonry-gap);
  grid-row-gap: var(--masonry-gap);
  flex-flow: wrap;
  justify-content: flex-start;
  align-items: flex-start;
  display: flex;
  position: relative;
}

.masonry-item {
  width: calc(((100% - 1px) - (var(--masonry-col) - 1) * var(--masonry-gap)) / var(--masonry-col));
}

.masonry-item__figure {
  width: 100%;
  overflow: hidden;
}

.masonry-item__figure.is--square { aspect-ratio: 1; }
.masonry-item__figure.is--wide   { aspect-ratio: 3 / 2; }
.masonry-item__figure.is--tall   { aspect-ratio: 2 / 3; }

.masonry-item__img {
  object-fit: cover;
  width: 100%;
  height: 100%;
  transition: transform var(--dur-mid) var(--ease-out);
}
.masonry-item:hover .masonry-item__img { transform: scale(1.04); }


/* ---- Legal pages -------------------------------------------------------- */

/* Impressum and Datenschutz: one column at a reading measure, the site's own
   h3 marking the sections. A long text needs air between its parts, not a
   second type scale. */
.legal > * + h3 { margin-top: var(--space-2xl); }
.legal h3 { margin-bottom: var(--space-md); }
/* The bullets belong inside the column the text sets, not on its edge. */
.legal ul { margin-top: var(--space-sm); margin-left: var(--space-xs); padding-left: 1.4em; }
.legal li { line-height: 1.9; }
.legal__stand { margin-top: var(--space-xl); color: var(--muted); font-size: var(--fs-small); }


/* 19 ############################### Ablauf ############################### */

/* The three steps of a project are said in one paragraph beside a photograph,
   and the page that is about them carries them in full. Nothing of its own to
   style: the block is the grid, the mini-title and the lede, the same pieces
   as the block above it. */


/* 20 ############################## Contact ############################## */

/* The number and the address first and large — the page exists to be acted
   on, and what follows them is detail. */
.contact-direct {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.6rem;
    margin-bottom: var(--space-2xl);
}
.contact-direct__line {
    font-family: var(--serif);
    font-size: 2.1rem;   /* the size the panel and the lightbox titles use */
    line-height: 1.2;
    color: var(--green);
    border-bottom: 1px solid var(--line);
    transition: border-color var(--dur-fast) ease;
}
.contact-direct__line:hover { border-color: var(--green); }

.contact-block { margin-bottom: 2.6rem; }
.contact-block__title {
    display: block;
    font-size: var(--fs-small);
    font-weight: var(--fw-medium);
    letter-spacing: var(--track-wide);
    text-transform: uppercase;
    color: var(--gold);
    margin-bottom: 0.7rem;
}
.contact-block p { line-height: 1.9; }

.contact-img { aspect-ratio: 4 / 5; }


/* 21 ############################# Parallax ############################## */

.ph__drift {
    position: absolute;
    top: -20%;
    left: 0;
    width: 100%;
    height: 140%;
}
.ph__drift img { width: 100%; height: 100%; object-fit: cover; display: block; }


/* 22 ########################## Project detail ########################### */

/* Banner — the photograph fills the band, the title and the facts sit on the
   bottom edge of it, split across the sixteen-column grid. */
.project-hero { min-height: 80dvh; }
.project-hero__inner {
    align-content: end;
    min-height: 80dvh;
    padding-bottom: 5rem;
}


/* Facts read as a three-column list, label above value. */
.project-stats ul { list-style: none; }
.project-stats li { line-height: 1.6; }
.project-stats span {
    display: block;
    font-size: var(--fs-small);
    font-weight: var(--fw-medium);
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--gold);
    margin-bottom: 0.25rem;
}

/* Bottom-aligned, so the facts finish level with the lead beside them. */
.project-stats--banner { align-self: end; }
.project-stats--banner ul { column-gap: var(--space-md); row-gap: 2.5rem; }
.project-stats--banner li { color: var(--white); }
.project-stats--banner span { color: var(--green-soft); }

/* Intro and closing — one measure, centred, nothing beside it. */
#project-description h2 {
    color: var(--green);
    margin-bottom: var(--space-md);
}

/* The three plates under the intro. A uniform crop reads as one row rather
   than three unrelated pictures. */
.project-trio .gallery-item__img { aspect-ratio: 4 / 5; }

/* Zwei Bilder statt drei: das zweite laeuft ueber die restlichen zwei
   Spalten. Seine Hoehe gibt das hohe Bild daneben vor, deshalb faellt dort
   das feste Seitenverhaeltnis weg und der Ausschnitt fuellt die Zeile. */
.project-trio.is--eins-zwei > :last-child { grid-column: span 2 / span 2; }
.project-trio.is--eins-zwei > :last-child .gallery-item__button {
    height: 100%;
    position: relative;
}
.project-trio.is--eins-zwei > :last-child .gallery-item__img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    aspect-ratio: auto;
}

/* Body text, offset from the left edge. `--project-plan` draws the floor plan
   faintly behind it; without one the block renders plain. */
#project-plan { position: relative; }
#project-plan.has-plan::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image: var(--project-plan);
    background-repeat: no-repeat;
    background-position: right center;
    background-size: contain;
    opacity: 0.35;
    pointer-events: none;
}
#project-plan .row { position: relative; z-index: 1; }
.project-plan__text p { color: var(--muted); }

/* Full-bleed plate between the two text blocks. */
.editorial-full { aspect-ratio: 2 / 1; }

/* The page hands off with a banner of its own: the next project's photograph
   full width, its name centred on top. */
.project-next {
    position: relative;
    display: grid;
    place-items: center;
    min-height: 52dvh;
    overflow: hidden;
    background: var(--green);
    color: var(--white);
    margin-top:7rem;
}
.project-next__media { position: absolute; inset: 0; }
.project-next__inner { position: relative; z-index: 1; }
.project-next__title {
    color: var(--white);
    font-size: var(--fs-h1);
    transition: opacity var(--dur-fast) ease;
}
.project-next:hover .project-next__title { opacity: 0.75; }
.project-next:hover .project-next__media img { transform: scale(1.03); }
.project-next__media img { transition: transform 1.2s var(--ease-out); }


/* 23 ############################# Über mich ############################# */

/* Sticky steps: the career passes the centre of the screen a step at a time
   while one portrait holds still beside it. Only the steps change — the frame
   is a single element for the whole run, not one per step. */
.sticky-steps {
  min-height: 100dvh;
  position: relative;
  overflow: clip;
}

.sticky-steps__container {
  max-width: var(--page-max);
  margin-left: auto;
  margin-right: auto;
}

.sticky-steps__collection {
  min-height: 100dvh;
  display: flex;
  position: relative;
}

.sticky-steps__list {
  grid-column-gap: 22dvh;
  grid-row-gap: 22dvh;
  flex-flow: column;
  flex: 1;
  padding-top: calc(30dvh - 7.5em);
  padding-bottom: calc(50dvh - 7.5em);
  display: flex;
}

.sticky-steps__text {
  grid-column-gap: var(--space-sm);
  grid-row-gap: var(--space-sm);
  flex-flow: column;
  align-items: flex-start;
  width: 40%;
  padding-right: var(--gutter-lg);
  display: flex;
}

/* One size for all three steps, whether the step opens with the page's h1 or
   with an h2 of its own — they are the same kind of thing in the column. */
.sticky-steps__title {
  margin: 0 0 var(--space-xs);
  font-size: var(--fs-h2);
}

.sticky-steps__p { margin: 0; color: var(--muted); }

/* The right half of the window, top to bottom — outside the centred
   container, so it owns the edge rather than sitting in a box. */
.sticky-steps__media {
  width: 50vw;
  height: 100%;
  position: absolute;
  top: 0;
  right: 0;
}

.sticky-steps__sticky {
  width: 100%;
  height: 100dvh;
  display: flex;
  position: sticky;
  top: 0;
}

.sticky-steps__visual {
  width: 100%;
  height: 100%;
  position: relative;
}

.sticky-steps__cover-image {
  object-fit: cover;
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
}

/* The dimming is scoped to the class the script sets, so with none of it
   running every step reads at full strength instead of three-quarters faded. */
@media screen and (min-width: 992px) {
  .is-driven [data-sticky-steps-item-status] .sticky-steps__text {
    transition: opacity 0.5s ease-in-out;
    opacity: 0.25;
  }

  .is-driven [data-sticky-steps-item-status="active"] .sticky-steps__text {
    opacity: 1;
  }
}

@media screen and (max-width: 991px) {
  /* Stacked there is no scroll to carry the eye from one step to the next,
     so the distance between them was doing nothing but pushing the career
     apart. What is left is the gap between two blocks of one text. */
  .sticky-steps__list {
    grid-column-gap: var(--space-2xl);
    grid-row-gap: var(--space-2xl);
    padding-top: 10em;
    padding-bottom: var(--space-3xl);
  }

  .sticky-steps__text {
    width: 100%;
    padding-bottom: 0;
    padding-right: 0;
  }

  /* Stacked, a frame the height of the window at the foot of the page reads
     as an afterthought. It comes first instead, as a band of its own, running
     up behind the masthead the way this page is built to — the crop below
     keeps her clear of it. */
  .sticky-steps { display: flex; flex-direction: column; min-height: auto; }
  .sticky-steps__media { order: -1; margin-bottom: var(--space-2xl); }
  .sticky-steps__list { padding-top: 0; }

  .sticky-steps__sticky {
    min-height: auto;
    height: 300px;
    position: relative;
    top: auto;
  }

  /* A band this wide cut from a standing portrait takes its middle, which is
     her waist — the head falls out of the top. The photograph has wall to
     spare above her, so the crop starts there instead and she sits in the
     frame rather than being sliced by it. */
  .sticky-steps__cover-image { object-position: top; }

  .sticky-steps__media {
    width: 100%;
    height: auto;
    padding-left: 0;
    position: relative;
    top: auto;
    right: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .is-driven [data-sticky-steps-item-status] .sticky-steps__text { transition-duration: 1ms; }
}


/* 24 ######################## Gallery & lightbox ######################### */

.gallery-grid { --masonry-col: 3; --masonry-gap: 2.5rem; }

.gallery-item__button {
    display: block;
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
    outline-offset: 4px;
}
.gallery-item__button:focus-visible { outline: 1px solid var(--green); }

.gallery-item__img {
    width: 100%;
    height: auto;
    display: block;
    object-fit: cover;
    background: var(--off);
    transition: opacity 0.4s ease;
}
.gallery-item__img.is--tall   { aspect-ratio: 2 / 3; }
.gallery-item__img.is--wide   { aspect-ratio: 3 / 2; }
.gallery-item__img.is--square { aspect-ratio: 1; }
.gallery-item__button:hover .gallery-item__img { opacity: 0.82; }

.lightbox {
    z-index: var(--z-lightbox);
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100dvh;
    display: none;
    position: fixed;
    inset: 0 0 auto;
}
.lightbox.is-active { display: flex; }

.lightbox__wrap { width: 88vw; height: calc(100dvh - 11rem); }
.lightbox__list {
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100%;
    display: flex;
    position: relative;
}
.lightbox__item {
    visibility: hidden;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100%;
    display: flex;
    position: absolute;
}
.lightbox__item.is-active { visibility: visible; }
/* The image that arrives here is the grid's own, Flipped across, so it brings
   the gallery crop with it — drop the fixed ratio and the loading tint so the
   full frame shows instead of a letterboxed one. */
.lightbox__item img {
    object-fit: contain !important;
    aspect-ratio: auto !important;
    background: none;
    min-width: auto;
    width: auto;
    max-width: 100%;
    max-height: 100%;
}

.lightbox__nav {
    z-index: 2;
    color: var(--white);
    justify-content: space-between;
    align-items: center;
    display: flex;
    position: absolute;
    bottom: 2.5rem;
    left: 60px;
    right: 60px;
}
.lightbox__col { width: 33.333%; display: flex; align-items: center; }
.lightbox__col.is--start  { justify-content: flex-start; }
.lightbox__col.is--center { justify-content: center; gap: 2.5rem; }
.lightbox__col.is--end    { justify-content: flex-end; }

.lightbox__text {
    margin: 0;
    font-size: var(--fs-small);
    font-weight: var(--fw-medium);
    letter-spacing: var(--track-wide);
    text-transform: uppercase;
}

.lightbox__button {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: -1rem;
    padding: var(--space-xs);
    border: 0;
    background: none;
    color: inherit;
    cursor: pointer;
    transition: opacity var(--dur-fast) ease;
}
.lightbox__button:hover { opacity: 0.65; }

.lightbox__dot {
    background-color: currentColor;
    border-radius: 10em;
    width: 0.35rem;
    height: 0.35rem;
    transition: transform 0.45s cubic-bezier(0.625, 0.05, 0, 1);
}
.lightbox__button:hover .lightbox__dot { transform: scale(1.8); }


/* 25 ########################## Beratung panel ########################### */

/* The masthead's call to action. The bars fold to a cross while the panel is
   open — driven from panel.js. */
/* Outlined in whatever colour the masthead is wearing, so it works white over
   a banner and ink on the inner pages without a second rule. */
.nav-cta {
    position: relative;
    display: inline-flex;
    align-items: center;
    margin-left: 2.6rem;
    padding-left: 2.6rem;
    background: none;
    color: inherit;
    cursor: pointer;
    font-family: var(--sans);
}
/* The hairline between the menu and the call to action. It wears the
   masthead's own colour, held back so it divides without drawing the eye. */
.nav-cta::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    height: 1.1rem;
    width: 1px;
    background: currentColor;
    opacity: 0.35;
    transform: translateY(-50%);
}
/* Size, weight and line box all match the menu beside it: the underline is
   drawn off the element's own box, so a tighter line here would pull the
   rule closer to the word than it sits on the links. */
.nav-cta__label {
    font-weight: var(--fw-medium);
    white-space: nowrap;
}

.panel-scrim {
    position: fixed;
    inset: 0;
    z-index: var(--z-scrim);
    background: var(--scrim);
    visibility: hidden;
    opacity: 0;
    cursor: pointer;
}

.panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: var(--z-panel);
    width: 32rem;
    max-width: 92vw;
    background: var(--primary);
    visibility: hidden;
    overflow-y: auto;
    box-shadow: -30px 0 80px rgba(10, 23, 16, 0.18);
}
.panel__inner { display: flex; flex-direction: column; min-height: 100%; }

/* Sits over the panel's own image, so it is white and needs no backdrop. */
.panel__close {
    position: absolute;
    top: 1.6rem;
    right: 1.6rem;
    z-index: 2;
    width: 2.6rem;
    height: 2.6rem;
    display: grid;
    place-items: center;
    border: 0;
    background: none;
    color: var(--white);
    cursor: pointer;
}
.panel__close-bar {
    grid-area: 1 / 1;
    width: 1.1rem;
    height: 1px;
    background: currentColor;
    transition: transform var(--dur-fast) ease;
}
.panel__close-bar:first-child { transform: rotate(45deg); }
.panel__close-bar:last-child  { transform: rotate(-45deg); }
.panel__close:hover .panel__close-bar:first-child { transform: rotate(135deg); }
.panel__close:hover .panel__close-bar:last-child  { transform: rotate(45deg); }

.panel__img { aspect-ratio: 16 / 10; flex: none; }
.panel__body { padding: 2.6rem 2.6rem var(--space-lg); }

.panel__title, .service-box__title, .block-title {
    font-size: 2.1rem;
    color: var(--green);
    margin-bottom: 1.2rem;
    max-width: 16ch;
}
.panel__text { color: var(--muted); }

/* The heading a block inside a section carries — a step under the section's
   own h2, so the two read in order rather than compete. It takes the size
   above; the measure there belongs to the panel. */
.block-title { max-width: none; margin-bottom: var(--space-md); }

.panel__contact { display: flex; flex-direction: column; gap: 0.5rem; margin: var(--space-lg) 0 var(--space-md); }
/* Like the two lines on the contact page, a step down: the panel is narrower
   and they sit under a paragraph rather than opening the page. */
.panel__contact a {
    font-family: var(--serif);
    font-size: var(--fs-h3);
    line-height: 1.3;
    color: var(--green);
    border-bottom: 1px solid var(--line);
    align-self: flex-start;
    transition: border-color var(--dur-fast) ease;
}
.panel__contact a:hover { border-color: var(--green); }

/* Nothing here should animate for a visitor who asked for stillness — the
   panel still opens and closes, it just arrives. */
@media (prefers-reduced-motion: reduce) {
    .panel, .panel-scrim { transition: none; }
}


/* 26 ############################ Leistungen ############################# */

/* The banner is the home page's, with one difference: the plate on it drops
   past the bottom edge of the green. The section stops clipping so it can —
   the drifting photograph behind it is still clipped by its own frame. */
.service-hero { min-height: auto; overflow: visible; }
.service-hero__inner {
    min-height: auto;
    align-items: start;
    padding-top: 18rem;
    row-gap: var(--space-2xl);
}

/* A negative bottom margin takes the overhang out of the row's height, so the
   band ends that much higher than the picture does. The row is the last thing
   in the banner and carries no padding under it, so the whole margin shows. */
.service-hero__plate {
    margin-bottom: calc(-1 * var(--overhang));
}

.service-hero__aside { padding-top: var(--space-lg); }
.service-hero__text {
    color: var(--white-soft);
    margin-bottom: var(--space-lg);
}

/* Credo — the quote and the two columns of copy beside it. It opens under the
   overhanging plate, so its top spacing carries that distance as well. */
.credo {
    padding-top: calc(var(--space-3xl) + var(--overhang));
    padding-bottom: var(--space-3xl);
}
.credo__quote { text-align: right; }
.credo__quote q { font-size: var(--fs-h3); display: block; }
/* Small print under the quote and under the columns: the attribution and the
   signature, both a step down from the copy they close. */
.credo__cite, .credo__name, .credo__role { display: block; font-size: var(--fs-small); }
.credo__cite { font-style: normal; color: var(--muted); margin-top: var(--space-md); }
.credo__name { font-weight: var(--fw-medium); color: var(--ink); }
.credo__role { color: var(--muted); }

.credo__sign { margin-top: var(--space-lg); }
.credo__sign-img { display: block; width: 170px; height: auto; margin-bottom: 0.6rem; }

/* Overview — the heading column beside the list, its picture below the copy. */
.service-overview__text { color: var(--muted); margin: var(--space-sm) 0 var(--space-md); }
.service-overview__img { aspect-ratio: 4 / 3; margin-top: var(--space-2xl); }

/* The list itself. Every row is a heading, a line about it and an arrow; the
   button lies over the whole row so the arrow is not the only target. */
.service-list { border-top: 1px solid var(--line); }

.service {
    position: relative;
    display: grid;
    grid-template-columns: 17rem 1fr 2rem;
    align-items: center;
    gap: var(--space-md);
    padding: 2rem 0;
    border-bottom: 1px solid var(--line);
    transition: background-color var(--dur-fast) ease;
}

.service__lead  { color: var(--muted); }

/* Only offered once script is there to open it — see .is-boxed below. */
.service__open {
    display: none;
    position: absolute;
    inset: 0;
    align-items: center;
    justify-content: flex-end;
    padding: 0;
    border: 0;
    background: none;
    color: var(--green);
    cursor: pointer;
}
.service__open:focus-visible { outline: 1px solid var(--green); outline-offset: 4px; }

.service__mark {
    width: 16px;
    height: 16px;
    transition: transform var(--dur-fast) var(--ease-out);
}
.service__open:hover .service__mark { transform: translate(2px, -2px); }

/* The detail is written into the row so it is readable as plain HTML. Script
   marks the list and folds it away, having a lightbox to show it in instead. */
.service__detail { grid-column: 1 / -1; padding-top: var(--space-sm); }
.service__detail p { color: var(--muted); }

.service-list.is-boxed .service__open   { display: flex; }
.service-list.is-boxed .service__detail { display: none; }
.service-list.is-boxed .service:hover .service__title { opacity: 0.6; }
.service-list.is-boxed .service__title  { transition: opacity var(--dur-fast) ease; }

/* The overlay a row opens into: a photograph on one side, the service on the
   other. Hidden by visibility rather than display, so it fades and so nothing
   inside it can be tabbed to while it is closed. The visibility switch itself
   is instant — delayed on the way out so the fade is seen, immediate on the
   way in so the close button can take focus at once. */
.service-box {
    position: fixed;
    inset: 0;
    z-index: var(--z-lightbox);
    display: flex;
    align-items: center;
    justify-content: center;
    visibility: hidden;
    opacity: 0;
    transition: opacity var(--dur-mid) ease, visibility 0s linear var(--dur-mid);
}
.service-box.is-active {
    visibility: visible;
    opacity: 1;
    transition: opacity var(--dur-mid) ease, visibility 0s;
}

.service-box__scrim { position: absolute; inset: 0; background: var(--scrim); }

.service-box__panel {
    position: relative;
    display: grid;
    grid-template-columns: 1.05fr 1fr;
    width: min(1180px, 84vw);
    max-height: 86dvh;
    background: var(--primary);
    translate: 0 1.5rem;
    transition: translate var(--dur-mid) var(--ease-out);
}
.service-box.is-active .service-box__panel { translate: 0 0; }

/* The picture fills its half however tall the copy beside it makes the panel.
   Taking it out of the flow is what stops its own proportions from deciding
   that height — the frame stretches, the photograph covers it. */
.service-box__media img { position: absolute; inset: 0; }

.service-box__body {
    padding: var(--space-2xl) var(--space-xl);
    overflow-y: auto;
}

.service-box__close {
    position: absolute;
    top: var(--space-md);
    right: var(--space-md);
    width: 2.5rem;
    height: 2.5rem;
    padding: 0.6rem;
    border: 0;
    background: none;
    color: var(--green);
    cursor: pointer;
    transition: opacity var(--dur-fast) ease;
}
.service-box__close:hover { opacity: 0.55; }
.service-box__close svg { width: 100%; height: 100%; display: block; }

.service-box__title { margin-bottom: var(--space-md); }
.service-box__text p { color: var(--muted); }

@media (prefers-reduced-motion: reduce) {
    .service-box, .service-box__panel { transition: none; }
}


/* 27 ######################### Page transition ########################### */

/* A straight-edged panel that rises over the page being left, and on the next
   page carries on upward to uncover it. The two halves happen in different
   documents, so the arriving page opens with the panel already covering it —
   `is-entering`, set in the head before anything is painted — and the script
   takes it from there. Without script nothing here is ever shown. */
.transition {
    position: fixed;
    inset: 0;
    z-index: var(--z-transition);
    pointer-events: none;
    overflow: clip;
    visibility: hidden;
}

.transition__panel {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    /* `transform`, not `translate`: the script drives transform, and the two
       are separate properties that would otherwise add up. */
    background: var(--green-deep);
    transform: translateY(100%);
}

/* The mark rides up out of its own box rather than fading. */
.transition__mark {
    display: block;
    width: 11em;
    overflow: clip;
}
.transition__mark img {
    display: block;
    width: 100%;
    height: auto;
    transform: translateY(110%);
}

/* The state the arriving page is painted in. */
html.is-entering .transition { visibility: visible; }
html.is-entering .transition__panel { transform: none; }
html.is-entering .transition__mark img { transform: none; }


/* 28 ############################# Responsive ############################# */

/* The page is composed at 1560. Everything below that is handled here, in one
   place, from the widest adjustment down to the smallest phone:
     ≤1559  the row stops touching the window edge
     ≤1400  laptop — the display sizes come down a step
     ≤1200  tablet — type, gutters and multi-column blocks come down
     ≤1100  the menu folds into the slide-out
     ≤768   phone — every grid becomes one column
     ≤380   the last tightening
   Most of it is done by restating the tokens in section 02: the type scale,
   the gutter and the vertical rhythm are all derived from them, so a handful
   of declarations moves the whole page rather than a rule per component. */

/* Hidden until the menu folds — see ≤1100 below. */
.nav-toggle, .mobile-menu { display: none; }

/* The page holds still behind the open panel. */
html.is-menu-open, html.is-menu-open body { overflow: hidden; }


/* ---- ≤1559  the composition no longer fits the window ------------------ */

@media screen and (max-width: 1559px) {
    /* The sticky-steps container is centred on the page like a row but is not
       one, so it takes the same treatment by hand. */
    .row,
    .header-narrow,
    .sticky-steps__container { padding-left: 2rem; padding-right: 2rem; }
}


/* ---- ≤1400  laptop ----------------------------------------------------- */

@media screen and (max-width: 1400px) {
    :root {
        --fs-h1: 4rem;
        --fs-h2: 3rem;
        --fs-display: 3.8rem;
        --gutter: 3rem;
        --gutter-lg: 3.5rem;
        --overhang: 7rem;
    }

    .statement { font-size: 2.6rem; }
    .site-nav { gap: 2rem; }

    /* The collage is a fixed composition sized in em off this one value, so
       tying it to the window is all it takes to keep it whole. */
    .collage-section { font-size: 1.45vw; }
    .service-hero__inner { padding-top: 14rem; }
}


/* ---- ≤1200  tablet ----------------------------------------------------- */

@media screen and (max-width: 1200px) {
    :root {
        --fs-body: 1.1rem;
        --fs-h1: 3.4rem;
        --fs-h2: 2.6rem;
        --fs-h3: 1.4rem;
        --fs-display: 3.2rem;
        --gutter: 2.5rem;
        --gutter-lg: 3rem;
        --space-3xl: 6rem;
        --space-4xl: 8rem;
        --overhang: 5rem;
    }

    .spacing-700 { padding-block: 5rem; }

    .statement { font-size: 2.2rem; }

    [data-masonry] { --masonry-col: 2; --masonry-gap: 1.5rem; }
    .gallery-grid  { --masonry-col: 2; --masonry-gap: 1.5rem; }

    .service { grid-template-columns: 13rem 1fr 2rem; }
    .service-hero__inner { padding-top: 11rem; }

    .service-box__panel { width: 92vw; }
    .service-box__body  { padding: var(--space-xl) var(--space-lg); }

    .project-hero, .project-hero__inner { min-height: 70dvh; }

    .site-footer__top { padding-block: 5rem 4rem; }
    .footer-cta__title { font-size: 2.4rem; }

    .contact-direct__line { font-size: 1.8rem; }

    .lightbox__nav { left: 2rem; right: 2rem; }
}


/* ---- ≤1100  the menu folds into a slide-out ---------------------------- */

@media screen and (max-width: 1100px) {

    /* The masthead keeps the mark and gains the button; the links and the
       call to action move into the panel the button opens. */
    .site-header__inner { grid-template-columns: 1fr auto; }
    .site-nav, .nav-cta { display: none; }

    /* Without script the button opens nothing, so the links stay where they
       are and the masthead grows to hold them. */
    html:not(.has-js) .site-header__inner {
        grid-template-columns: 1fr;
        justify-items: start;
        height: auto;
        row-gap: 1rem;
        padding-top: 1.2rem;
        padding-bottom: 1.2rem;
    }
    html:not(.has-js) .site-nav {
        display: flex;
        flex-wrap: wrap;
        gap: 1.2rem;
        justify-self: start;
    }
    html:not(.has-js) .nav-cta {
        display: inline-flex;
        justify-self: start;
        margin-left: 0;
        padding-left: 0;
    }
    html:not(.has-js) .nav-cta::before { content: none; }
    html:not(.has-js) .nav-toggle { display: none; }

    /* Holding the links makes the masthead several rows tall, which is more
       than the banner leaves for it. So it stops floating over the banner and
       takes its own place above it — in the banner's own green, so the two
       still read as one band. */
    html:not(.has-js) .site-header { position: relative; }
    html:not(.has-js) .has-hero .site-header { background: var(--green-deep); }
    html:not(.has-js) .hero__inner { padding-top: 0; }

    .nav-toggle {
        display: grid;
        place-items: center;
        justify-self: end;
        width: 2.75rem;
        height: 2.75rem;
        margin-right: -0.6rem;
        padding: 0;
        border: 0;
        background: none;
        color: inherit;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
    }

    .nav-toggle__icon {
        position: relative;
        display: block;
        width: 1.55rem;
        height: 0.8rem;
    }

    .nav-toggle__icon span {
        position: absolute;
        left: 0;
        width: 100%;
        height: 1px;
        background: currentColor;
        transition: transform var(--dur-fast) var(--ease-out),
                    opacity var(--dur-fast) ease,
                    top var(--dur-fast) var(--ease-out);
    }
    .nav-toggle__icon span:nth-child(1) { top: 0; }
    .nav-toggle__icon span:nth-child(2) { top: 50%; }
    .nav-toggle__icon span:nth-child(3) { top: 100%; }

    /* The panel lies over the masthead, so the cross would be drawn beneath
       it — the bars were never the wrong colour, they were covered. The
       masthead comes up over the panel for as long as it is open, in ink
       because the panel is paper, and its mark steps aside: the panel
       carries its own. */
    html.is-menu-open .site-header {
        z-index: calc(var(--z-panel) + 1);
        /* Raised, its full width lies across the top of the panel and would
           swallow everything under it — the mark in the panel among them.
           Only the cross is meant to be there, so only the cross answers. */
        pointer-events: none;
    }
    html.is-menu-open .nav-toggle { color: var(--ink); pointer-events: auto; }
    html.is-menu-open .brand {
        opacity: 0;
        pointer-events: none;
        transition: opacity var(--dur-fast) ease;
    }

    /* Open: the three bars fold to a cross, the same gesture the panel's own
       close button makes. */
    .nav-toggle.is-active .nav-toggle__icon span:nth-child(1) { top: 50%; transform: rotate(45deg); }
    .nav-toggle.is-active .nav-toggle__icon span:nth-child(2) { opacity: 0; }
    .nav-toggle.is-active .nav-toggle__icon span:nth-child(3) { top: 50%; transform: rotate(-45deg); }

    /* The panel itself. Closed it is inert rather than hidden, so it can be
       animated; nothing inside it is reachable until it is open. */
    .mobile-menu {
        display: block;
        position: fixed;
        inset: 0;
        z-index: var(--z-panel);
        visibility: hidden;
        pointer-events: none;
    }
    .mobile-menu.is-open { visibility: visible; pointer-events: auto; }

    .mobile-menu__backdrop {
        position: absolute;
        inset: 0;
        background: var(--scrim);
        opacity: 0;
        transition: opacity var(--dur-mid) ease;
    }
    .mobile-menu.is-open .mobile-menu__backdrop { opacity: 1; }

    .mobile-menu__panel {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        width: min(88%, 24rem);
        display: flex;
        flex-direction: column;
        gap: var(--space-lg);
        padding: 2.2rem 2rem 2.6rem;
        background: var(--primary);
        overflow-y: auto;
        transform: translateX(100%);
        transition: transform var(--dur-mid) var(--ease-out);
        box-shadow: -30px 0 80px rgba(10, 23, 16, 0.18);
    }
    .mobile-menu.is-open .mobile-menu__panel { transform: none; }

    .mobile-menu__brand img {
        display: block;
        width: 104px;
        height: auto;
    }

    .mobile-menu__nav { display: flex; flex-direction: column; }
    .mobile-menu__nav a {
        font-family: var(--serif);
        font-size: 1.7rem;
        line-height: 1.2;
        color: var(--green);
        padding: 0.6rem 0;
        border-bottom: 1px solid var(--line);
        transition: opacity var(--dur-fast) ease;
    }
    .mobile-menu__nav a:hover, .mobile-menu__nav a.is-active { opacity: 0.55; }

    /* The call to action the masthead gave up, in the form the contact page
       uses: the number and the address, to be acted on directly. */
    .mobile-menu__contact { margin-top: auto; display: flex; flex-direction: column; gap: 0.4rem; }
    .mobile-menu__contact a {
        font-family: var(--serif);
        font-size: var(--fs-h3);
        line-height: 1.3;
        color: var(--green);
        align-self: flex-start;
        border-bottom: 1px solid var(--line);
    }

    @media (prefers-reduced-motion: reduce) {
        .mobile-menu__panel, .mobile-menu__backdrop { transition: none; }
    }
}


/* ---- ≤768  phone ------------------------------------------------------- */

@media screen and (max-width: 768px) {
    :root {
        --fs-body: 1rem;
        --fs-small: 0.85rem;
        --fs-h1: 2.4rem;
        --fs-h2: 2rem;
        --fs-h3: 1.25rem;
        --fs-display: 2.3rem;
        --gutter: 1.6rem;
        --gutter-lg: 1.8rem;
        --lh-body: 1.75;
        --space-2xl: 3.5rem;
        --space-3xl: 4rem;
        --space-4xl: 5rem;
        --overhang: 0rem;
    }

    .row,
    .header-narrow,
    .sticky-steps__container { padding-left: 1.5rem; padding-right: 1.5rem; }
    .spacing-700 { padding-block: 3.5rem; }

    /* Every grid becomes one column, and the placement utilities stand down
       with it — a c-start-9 in a single-column grid would otherwise push its
       block into a column that no longer exists. */
    .two-c, .three-c, .twelve-c, .sixteen-c { grid-template-columns: 1fr; }
    [class*="c-start-"] { grid-column-start: auto; }
    [class*="c-span-"]  { grid-column: span 1 / span 1; }

    /* Das breite Bild der Eins-zwei-Reihe hat hier keine zweite Spalte mehr,
       ueber die es laufen koennte — es steht untereinander und bekommt sein
       eigenes Format zurueck. */
    .project-trio.is--eins-zwei > :last-child { grid-column: span 1 / span 1; }
    .project-trio.is--eins-zwei > :last-child .gallery-item__button {
        height: auto;
        position: static;
    }
    .project-trio.is--eins-zwei > :last-child .gallery-item__img {
        position: static;
        aspect-ratio: 3 / 2;
        height: auto;
    }

    .site-header__inner { height: 72px; }
    .brand img { max-width: 104px; }

    .mini-title::before { width: 36px; }
    .statement { font-size: 1.7rem; max-width: none; }
    .page-lead { font-size: 1.15rem; }

    /* Hero: the masthead is shorter, so the copy starts higher. */
    .hero__inner { padding-top: 72px; }

    /* Collage: the composition is laid out in absolute em positions, which
       cannot shrink this far. Below this width it reads as what it is — a set
       of photographs — two to a row. */
    /* One row that drifts past, rather than four rows of scrolling: the same
       eight photographs at the height of one card.

       The second lap is what makes it seamless — sixteen cards, the last
       eight identical to the first, and the track travels exactly half its
       width before starting over. That comes out even only if every card
       carries its own margin: a flex `gap` puts one between the two halves
       as well, and the loop lands a gap short. */
    .collage-section, .collage { overflow: clip; }
    .collage-section { font-size: 16px; }
    .collage, .collage__list { width: 100%; height: auto; }
    .collage__list {
        display: flex;
        width: max-content;
        animation: collage-lap 38s linear infinite;
    }
    .collage__item.is--lap-two { display: block; }
    .collage__item, .collage__item[class*="is--"] {
        position: static;
        top: auto; right: auto; bottom: auto; left: auto;
        flex: none;
        margin-right: 1rem;
        /* A tap asks the script to open a card out of the composition, which
           is not what a moving strip is. Nothing to tap here. */
        pointer-events: none;
    }
    .collage__card,
    .collage__card.is--landscape,
    .collage__card.is--portrait { width: 56vw; aspect-ratio: 1; }
    .collage__img { border-width: 5px; }

    @keyframes collage-lap {
        to { transform: translateX(-50%); }
    }

    /* Asked to hold still it holds still, and becomes a strip you push
       yourself rather than one that is taken away from you. */
    @media (prefers-reduced-motion: reduce) {
        .collage__list { animation: none; }
        .collage { overflow-x: auto; }
    }

    .banner { aspect-ratio: 3 / 2; }

    /* Testimonial: the frame leaves the right half and takes its own band
       under the quotes. */
    .voices__visual {
        position: relative;
        inset: auto;
        height: 60vw;
        margin-top: var(--space-2xl);
    }

    .projects__masonry { grid-template-columns: 1fr; }
    .projects__col + .projects__col { margin-top: 0; }
    .projects__head .link-more { justify-self: start; margin-top: var(--space-md); }



    /* One project to a row — two would leave each plate too small to read the
       room in. The gallery's thumbnails are a different thing and stay paired. */
    [data-masonry] { --masonry-col: 1; --masonry-gap: 1.5rem; }
    .gallery-grid  { --masonry-col: 2; --masonry-gap: 1rem; }

    /* Leistungen */
    /* Stacked, the row gap is the only thing between the title, the plate and
       the copy — so the h1's own margin under it would double the first one.
       And with the overhang gone the band needs an end of its own, or the
       link sits on the edge of the green. */
    .service-hero__inner {
        padding-top: 8rem;
        padding-bottom: var(--space-2xl);
        row-gap: var(--space-md);
    }
    .service-hero .page-title { margin-bottom: 0; }
    .service-hero__plate { margin-bottom: 0; }
    .service-hero__aside { padding-top: 0; }
    .credo { padding-top: var(--space-3xl); }
    .credo__quote { text-align: left; }
    .service-overview__img { margin-top: var(--space-lg); }
    .service { grid-template-columns: 1fr; gap: 0.5rem; padding: 1.4rem 0; }
    .service__open { justify-content: flex-end; align-items: flex-start; padding-top: 1.4rem; }

    /* A panel inset in the window leaves too little of it for the copy, which
       then ends mid-sentence. It takes the whole screen instead and scrolls
       as one piece, picture and all.

       The close button holds still through that: it goes `fixed`, and the
       panel drops the `translate` it slides in on — a transform, even at
       zero, would have made the panel the button's containing block again
       and taken it along for the ride. The panel keeps `position: relative`,
       or the scrim beside it would paint over the whole thing. */
    .service-box__panel,
    .service-box.is-active .service-box__panel {
        translate: none;
        /* Not a grid here: with a fixed height its two auto rows shared the
           window between them and squeezed the picture's row to nothing,
           which the picture then overflowed onto the copy. Stacked as
           blocks, each takes the height it needs and the panel scrolls. */
        display: block;
        width: 100vw;
        height: 100dvh;
        max-height: none;
        overflow-y: auto;
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
    }
    .service-box__media { position: relative; aspect-ratio: 4 / 3; }
    .service-box__body  { padding: var(--space-lg) var(--space-md); overflow: visible; }
    /* It now sits on the photograph rather than on paper. */
    .service-box__close {
        position: fixed;
        top: var(--space-sm);
        right: var(--space-sm);
        background: var(--primary);
        border-radius: 50%;
    }

    /* Project detail */
    .project-hero, .project-hero__inner { min-height: 60dvh; }
    .project-hero__inner { padding-bottom: 2.5rem; }
    .project-stats ul { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-md); }
    #project-plan.has-plan::before { display: none; }
    .editorial-full { aspect-ratio: 3 / 2; }
    .project-next { min-height: 34dvh; margin-top: 4rem; }

    /* Contact */
    .contact-direct__line { font-size: 1.5rem; }

    /* Footer */
    .site-footer__top { grid-template-columns: 1fr; gap: var(--space-xl); padding-block: 4rem 3rem; }
    .footer-cta__title { font-size: 2rem; max-width: none; }
    .footer-meta { flex-direction: column; align-items: flex-start; gap: 1rem; padding-bottom: 2rem; }
    .footer-meta__legal { flex-wrap: wrap; gap: 1rem; }

    /* Lightbox */
    .lightbox__wrap { width: 92vw; height: calc(100dvh - 9rem); }
    .lightbox__nav { left: 1.2rem; right: 1.2rem; bottom: 1.2rem; flex-wrap: wrap; row-gap: 1rem; }
    /* Three equal columns leave each label about a hundred pixels, which the
       words do not fit in. The count and the close keep the row; the two step
       buttons take a line of their own under it. */
    .lightbox__col { width: auto; }
    .lightbox__col.is--start  { order: 1; }
    .lightbox__col.is--end    { order: 2; margin-left: auto; }
    .lightbox__col.is--center { order: 3; width: 100%; gap: 2rem; }
    .lightbox__text { font-size: 0.75rem; }

    /* The loading screen sizes itself off the viewport width, which leaves the
       frames the size of postage stamps on a phone. */
    .loader__stage { font-size: 3.6vw; }
    .transition__mark { width: 8em; }

    .panel__body { padding: 1.8rem 1.8rem var(--space-md); }
}


/* ---- ≤767  where the parallax stops running ---------------------------- */

/* To the pixel the width the script disables it at — `mobileLandscape` in
   main.js is (max-width: 767px). One pixel wider and the photographs still
   drift, and they need the room the rules below take away. */
@media screen and (max-width: 767px) {

    /* A drifting photograph sits in a frame 40% taller than the band it fills,
       so it has somewhere to drift to. With nothing drifting, that height only
       crops the picture — a third of the home banner was outside the band. */
    .ph__drift { top: 0; height: 100%; }

    /* And the hero stops measuring itself against a viewport that moves. On a
       phone the address bar collapses as you scroll down, `dvh` follows it,
       and the hero grows by the height of the bar under your finger — which
       the covered photograph answers by scaling up. That is the zoom. `svh`
       is the height with the bar showing, and it holds still. */
    .hero, .hero__inner { min-height: 100svh; }
}


/* ---- ≤380  the smallest phones ----------------------------------------- */

@media screen and (max-width: 380px) {
    :root { --fs-h1: 2.1rem; --fs-h2: 1.8rem; --fs-display: 2rem; }

    .row,
    .header-narrow,
    .sticky-steps__container { padding-left: 1.2rem; padding-right: 1.2rem; }
    .project-stats ul { grid-template-columns: 1fr; }
    .statement { font-size: 1.5rem; }
}
