/* SENOX – one stylesheet, mobile first. Colours from the new logo (roadmap v2 §3.2), system fonts (nothing loaded from Google). */
:root {
  --blue: #0070d0; /* buttons and links: white text on it reads at 5:1 */
  --blue-dark: #005aa8;
  --blue-soft: #e8f1fa;
  --sky: #0098f8; /* details only (lines, pipe tail): 3:1 on white, so never under text or small icons */
  --accent: #0070d0; /* blue as text, icon and focus colour; --blue stays for fills under white text */
  --ink: #101b24; /* SENOX navy for headings, text and the footer */
  --text: #101b24;
  --line: #e3e7ec;
  --rule: #d7dde4;
  --field-line: #c5cdd6;
  --bg: #fff;
  --bg-alt: #f4f6f8;
  --surface: #fff;
  --header: rgba(255, 255, 255, .94);
  --cta: #0070d0;
  --footer: #101b24;
  --footer-line: #101b24;
  --ok: #14532d; --ok-bg: #ecf8f1; --ok-line: #b9e3c9;
  --err: #7f1d1d; --err-bg: #fdeeee; --err-line: #f3c4c4;
  --radius: 16px;
  --ease: cubic-bezier(.16, 1, .3, 1);
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
/* Dark mode follows the OS; the theme switch pins html[data-theme] (set before first paint by the inline <head> script).
   Screen only, so print stays light; browsers without light-dark() keep the light values above and hide the switch. */
@supports (color: light-dark(#000, #fff)) {
  :root {
    --blue-soft: light-dark(#e8f1fa, #10263d);
    --accent: light-dark(#0070d0, #5eb0ff);
    --ink: light-dark(#101b24, #f1f3f5);
    --text: light-dark(#101b24, #b9c0c8);
    --line: light-dark(#e3e7ec, #2a313a);
    --rule: light-dark(#d7dde4, #3b4450);
    --field-line: light-dark(#c5cdd6, #646f7c);
    --bg: light-dark(#fff, #0e1115);
    --bg-alt: light-dark(#f4f6f8, #13171c);
    --surface: light-dark(#fff, #181d23);
    --header: light-dark(rgba(255, 255, 255, .94), rgba(24, 29, 35, .94));
    --cta: light-dark(#0070d0, #0a5aa6);
    --footer: light-dark(#101b24, #0a0c0f);
    --footer-line: light-dark(#101b24, #2a313a);
    --ok: light-dark(#14532d, #a7e8bf); --ok-bg: light-dark(#ecf8f1, #0f2a1c); --ok-line: light-dark(#b9e3c9, #1f5135);
    --err: light-dark(#7f1d1d, #fbb4b4); --err-bg: light-dark(#fdeeee, #2e1414); --err-line: light-dark(#f3c4c4, #5c2626);
  }
  @media screen {
    :root { color-scheme: light dark; }
    :root[data-theme="light"] { color-scheme: light; }
    :root[data-theme="dark"] { color-scheme: dark; }
    /* Header logo: the white-text version on dark. Both <img> carry the alt (display:none keeps one in the accessibility tree);
       both load eagerly so a dark first paint never waits on the logo (the footer reuses the dark file). */
    :root[data-theme="dark"] .logo img:first-child { display: none; }
    :root[data-theme="dark"] .logo img + img { display: block; }
    @media (prefers-color-scheme: dark) {
      :root:not([data-theme]) .logo img:first-child { display: none; }
      :root:not([data-theme]) .logo img + img { display: block; }
    }
  }
}

*, *::before, *::after { box-sizing: border-box; }
html { scroll-padding-top: 100px; -webkit-text-size-adjust: 100%; }
body { margin: 0; font: 400 1rem/1.65 var(--font); color: var(--text); background: var(--bg); }
img { display: block; max-width: 100%; height: auto; }
a { color: var(--accent); }
h1, h2, h3 { margin: 0 0 .5em; color: var(--ink); line-height: 1.2; letter-spacing: -.015em; text-wrap: balance; }
h1 { font-size: clamp(1.75rem, 1.1rem + 2.6vw, 2.75rem); font-weight: 750; }
h2 { font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2.125rem); font-weight: 700; }
h3, .h3 { font-size: 1.125rem; font-weight: 650; }
p { margin: 0 0 1em; }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.hero :focus-visible, .cta :focus-visible, .site-footer :focus-visible { outline-color: #fff; }

.wrap { width: min(100% - 32px, 1180px); margin-inline: auto; }
.section { padding-block: clamp(56px, 8vw, 104px); }
.alt { background: var(--bg-alt); }
.section-head { max-width: 660px; margin-bottom: clamp(28px, 4vw, 48px); }
.lead { font-size: clamp(1.0625rem, 1rem + .3vw, 1.1875rem); }
.icon { width: 22px; height: 22px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.i-whatsapp { fill: currentColor; stroke: none; }
.rounded { border-radius: var(--radius); }

/* Buttons & links */
.btns { display: flex; flex-wrap: wrap; gap: 12px; }
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 48px; padding: 12px 22px; border: 2px solid transparent; border-radius: 10px; font: inherit; font-weight: 600; line-height: 1.2; text-decoration: none; cursor: pointer; transition: background-color .15s, color .15s, translate .3s var(--ease); }
.btn-primary { background: var(--blue); color: #fff; }
.btn-primary:hover { background: var(--blue-dark); }
.btn-white { background: #fff; color: var(--blue); }
.btn-white:hover { background: #e8f1fa; }
.btn-ghost { border-color: rgba(255, 255, 255, .75); color: #fff; }
.btn-ghost:hover { background: #fff; color: #101b24; }
.btn-line { border-color: var(--accent); color: var(--accent); } /* outline button for light sections (.btn-ghost is for dark ones) */
.btn-line:hover { background: var(--blue-soft); }
@media (max-width: 599px) { .btn { width: 100%; } }
.link-arrow { position: relative; display: inline-flex; align-items: center; gap: 6px; font-weight: 600; text-decoration: none; }
.link-arrow::after { content: ""; position: absolute; left: 0; right: 24px; bottom: 0; height: 0; border-top: 2px solid; scale: 0 1; transform-origin: left; }
.link-arrow:is(:hover, :focus-visible)::after { scale: 1; }
.link-arrow .icon { width: 18px; height: 18px; transition: translate .25s var(--ease); }

/* Header */
.skip { position: absolute; left: -9999px; }
.skip:focus { left: 16px; top: 12px; z-index: 100; padding: 10px 14px; background: var(--surface); border-radius: 8px; }
.site-header { position: sticky; top: 0; z-index: 50; background: var(--header); backdrop-filter: blur(12px); border-bottom: 1px solid var(--line); }
.header-inner { display: flex; align-items: center; gap: 8px; min-height: 64px; }
.logo { margin-right: auto; }
.logo img { width: auto; height: 44px; object-fit: contain; object-position: left; } /* roadmap v2 §3.1: 44px on phones; on very narrow ones only the logo gives way, scaled, never squashed */
.nav { display: flex; gap: 2px; }
.nav > a:not(.btn) { padding: 10px 12px; border-radius: 8px; color: var(--ink); font-weight: 500; text-decoration: none; white-space: nowrap; }
.nav > a:not(.btn):hover, .theme-btn:hover { background: var(--bg-alt); }
.nav a[aria-current="page"] { color: var(--accent); }
.lang { display: flex; flex: none; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; font-size: .875rem; font-weight: 600; }
.lang a { min-width: 40px; padding: 15px 8px; color: var(--text); text-align: center; line-height: 1; text-decoration: none; }
.lang a[aria-current] { background: var(--blue-soft); color: var(--accent); font-weight: 700; box-shadow: inset 0 -3px var(--accent); } /* the bar: not by colour alone */
.lang a:focus-visible { outline-offset: -3px; } /* inside: .lang clips */
.icon-btn { display: grid; place-items: center; flex: none; width: 44px; height: 44px; padding: 0; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); color: var(--ink); cursor: pointer; }
/* Call link (before the header CTA): a solid blue icon button on phones, a plain link from 960px with its number from 1180px. */
.call { font-weight: 700; text-decoration: none; }
.call-num { display: none; white-space: nowrap; }
.menu-btn { order: 1; } /* first in the markup (Tab goes into the menu it opens), rightmost on screen */
.menu-btn[aria-expanded="true"] .i-menu, .menu-btn:not([aria-expanded="true"]) .i-close { display: none; }
.logo img + img { display: none; }
/* Theme switch, in .nav: a sun/moon icon button in the desktop nav row; in the phone menu a labelled row (its aria-label)
   led by an on/off switch drawn by ::before (knob = a radial gradient that slides with aria-pressed). Hidden without JS or light-dark(). */
.theme-btn { display: grid; place-items: center; align-self: center; width: 44px; height: 44px; padding: 0; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); color: var(--ink); font: inherit; cursor: pointer; }
.theme-btn .i-sun, :root[data-theme="dark"] .theme-btn .i-moon { display: none; }

/* Phone menu: a panel under the header (quote + call buttons at its end) over a scrim; the page behind stops scrolling.
   main.js closes it on Escape, a tap outside the header or a menu link. */
@media (max-width: 959px) {
  .header-cta { display: none; }
  .call { border: 0; background: var(--blue); color: #fff; }
  .call:hover { background: var(--blue-dark); }
  .nav { display: none; position: absolute; top: 100%; left: 0; right: 0; flex-direction: column; max-height: calc(100dvh - 65px); overflow-y: auto; overscroll-behavior: contain; padding: 8px 16px 16px; background: var(--surface); border-bottom: 1px solid var(--line); box-shadow: 0 18px 24px -18px rgba(0, 0, 0, .25); }
  .nav-open .nav { display: flex; }
  .nav-open { overflow: hidden; }
  .nav-open::after { content: ""; position: fixed; inset: 0; z-index: 45; background: rgba(0, 0, 0, .45); } /* under the sticky header (50) */
  .nav > a:not(.btn) { padding: 14px 12px; font-size: 1.0625rem; }
  .nav .btn { margin-top: 8px; }
  .nav .lang { align-self: flex-start; margin: 8px 12px 0; }
  .theme-btn { display: flex; gap: 12px; align-self: stretch; width: auto; height: auto; padding: 14px 12px; border: 0; background: none; font-size: 1.0625rem; font-weight: 500; }
  .theme-btn .icon { display: none; }
  .theme-btn::before { content: ""; flex: none; width: 40px; height: 24px; border-radius: 12px; background: radial-gradient(circle, #fff 7.5px, rgba(0, 0, 0, .3) 8px 8.5px, transparent 9px) 3px 50% / 18px 18px no-repeat #77808b; transition: background-color .2s, background-position .3s var(--ease); }
  .theme-btn[aria-pressed="true"]::before { background-color: var(--blue); background-position: 19px 50%; }
  .theme-btn::after { content: attr(aria-label); }
}
@media (min-width: 960px) {
  .header-inner { gap: 16px; min-height: 84px; }
  .logo img { height: 56px; }
  .menu-btn, .nav .btn, .nav .lang { display: none; }
  .header-cta { white-space: nowrap; } /* like the nav links: with wide system fonts only the logo gives way */
  .call { border: 0; background: none; }
  .call:hover { color: var(--accent); }
  :root[data-theme="dark"] .theme-btn .i-sun { display: block; }
}
@media (min-width: 960px) and (max-width: 1179px) { .header-inner { gap: 10px; } .nav > a:not(.btn) { padding-inline: 10px; } } /* room for the call icon at 960px */
@media (max-width: 359px) { .header-inner { gap: 4px; } .lang a { min-width: 36px; } } /* roadmap v2 §3.1: the logo stays >= 38px tall down to 320px */
@media (min-width: 1180px) {
  .call { display: flex; gap: 8px; width: auto; padding-inline: 4px; }
  .call-num { display: inline; }
}
@media (scripting: none) { .theme-btn { display: none; } }
@supports not (color: light-dark(#000, #fff)) { .theme-btn { display: none; } }

/* Phone call bar (foot(), all pages but contact): main.js adds .show while the first action area (hero buttons, else the first section)
   and the CTA band are both off-screen. Hidden = moved out + visibility hidden (not focusable); none on desktop, in print or without JS.
   It also hides once the footer's last line is on screen, so the page end is never covered (no (scripting) query anywhere here: Safari < 17 lacks it).
   The WhatsApp button rides up above the bar while it shows. */
.callbar { display: none; }
@media screen and (max-width: 959px) and (min-height: 500px) { /* not on short (landscape, zoomed) screens: the header call button stays */
  html { scroll-padding-bottom: 80px; } /* focus never lands under the bar */
  .callbar { position: fixed; inset: auto 0 0; z-index: 40; display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 10px 16px calc(10px + env(safe-area-inset-bottom)); background: var(--surface); border-top: 1px solid var(--line); box-shadow: 0 -8px 24px -12px rgba(0, 0, 0, .25); translate: 0 110%; visibility: hidden; transition: translate .3s var(--ease), visibility .3s; }
  .callbar .btn { padding-inline: 8px; font-size: .9375rem; } /* both labels stay on one line down to ~350px */
  body:not(.nav-open) .callbar.show { translate: none; visibility: visible; }
  body:not(.nav-open) .callbar.show + .wa { bottom: calc(85px + env(safe-area-inset-bottom)); } /* 16 + the bar's 69 (10 + 48 + 10 + 1) */
}

/* WhatsApp button (roadmap v2 §4): a plain link fixed bottom right on every page, above the call bar (40), under the open phone menu's scrim (45). */
.wa { position: fixed; right: 16px; bottom: calc(16px + env(safe-area-inset-bottom)); z-index: 42; display: grid; place-items: center; width: 56px; height: 56px; border-radius: 50%; background: #25d366; color: #fff; box-shadow: 0 4px 12px rgba(0, 0, 0, .25); transition: scale .2s var(--ease), bottom .3s var(--ease); }
.wa:hover { scale: 1.06; }
.wa .icon { width: 30px; height: 30px; }
@media (min-width: 960px) { .wa { width: 60px; height: 60px; } .wa .icon { width: 32px; height: 32px; } }
@media print { .wa { display: none; } }
/* The small WhatsApp link beside the 24h number: the icon on the line, a 34px hit area that adds no line height. */
.wa-link { display: inline-block; padding: 8px; margin: -8px 0; line-height: 0; vertical-align: middle; }
.wa-link .icon { width: 18px; height: 18px; }
@media (max-width: 599px) { form > .btn { width: calc(100% - 72px); } } /* the submit button ends before the WhatsApp button (16 + 56) */

/* Hero */
.hero { position: relative; isolation: isolate; overflow: hidden; color: #fff; background: #101b24; }
.hero img { position: absolute; inset: 0; z-index: -2; width: 100%; height: 100%; object-fit: cover; }
/* Overlay: dark behind the text, lighter beside or below it. White text stays >= 4.5:1 even over pure white marble (>= .58 under it
   on phones: 4.6:1; >= .7 from 600px: 7:1). Stops follow the text, not fixed %: phones lighten only in the 88px bottom padding under the buttons,
   wider screens only right of --r, the text column's edge (.wrap's left edge max(16px, 50% - 590px) + the 34em lead at 19px). */
.hero::before { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient(rgba(12, 18, 26, .58), rgba(12, 18, 26, .7) calc(100% - 88px), rgba(12, 18, 26, .25)); }
.hero .wrap { padding-block: clamp(88px, 14vw, 176px); }
.hero h1 { max-width: 20ch; color: #fff; }
.hero .lead { max-width: 34em; margin-bottom: 32px; color: rgba(255, 255, 255, .92); }
.eyebrow { margin-bottom: 14px; font-size: .8125rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; }
@media (min-width: 600px) {
  .hero .eyebrow::before { content: ""; display: inline-block; width: 28px; height: 2px; margin-right: 12px; vertical-align: .3em; background: currentColor; transform-origin: left; }
  .hero::before { --r: max(662px, 50% + 56px); background: linear-gradient(90deg, rgba(12, 18, 26, .85), rgba(12, 18, 26, .7) var(--r), rgba(12, 18, 26, .3) calc(var(--r) + 22%), rgba(12, 18, 26, .05) calc(var(--r) + 42%)); }
}

/* Fact band under the home hero: a static wrapped list; the motion block turns it into a slow ticker and only then shows its pause button. */
.ticker { position: relative; background: var(--blue-soft); color: var(--ink); font-size: .9375rem; font-weight: 600; }
.ticker ul { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 28px; margin: 0; padding: 16px; list-style: none; }
.ticker ul + ul { display: none; }
.ticker li { display: flex; align-items: center; gap: 8px; }
.ticker .icon { width: 18px; height: 18px; color: var(--accent); }
.ticker .motion-toggle { display: none; position: absolute; top: 50%; right: 8px; z-index: 2; translate: 0 -50%; justify-content: center; width: 36px; height: 36px; min-height: 36px; border: 1px solid var(--line); border-radius: 50%; background: var(--surface); }

/* Page title band (all pages except home) */
.page-head { padding-block: clamp(48px, 7vw, 88px); background: var(--bg-alt); border-bottom: 1px solid var(--line); }
.page-head .lead { max-width: 40em; margin: 0; }

/* Cards */
.grid { display: grid; gap: 16px; }
@media (min-width: 800px) { .grid-3 { grid-template-columns: repeat(3, 1fr); } .grid-2 { grid-template-columns: 1fr 1fr; } }
.card { padding: 28px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); }
.card > p:last-child { margin-bottom: 0; }
.card a { font-weight: 600; }
.badge { display: grid; place-items: center; width: 48px; height: 48px; margin-bottom: 20px; border-radius: 12px; background: var(--blue-soft); color: var(--accent); }
.badge .icon { width: 24px; height: 24px; }
.more { margin: 32px 0 0; }

/* Text + image */
.split { display: grid; gap: clamp(32px, 5vw, 72px); align-items: center; }
.split > img, .door img { width: 100%; aspect-ratio: 5 / 4; object-fit: cover; }
.door { position: relative; overflow: hidden; overflow: clip; }
@media (min-width: 900px) { .split { grid-template-columns: 1fr 1fr; } }
.split > div > p:last-of-type { margin-bottom: 28px; }

/* Home "Komplettsanierung" block (roadmap v2 §6): one 760px reading column; the six services as small two-column cards;
   the second half behind a native <details> "Weiterlesen" (phones only: main.js opens it from 800px), so the text is always in the HTML. */
.renov { max-width: 760px; }
.renov .section-head { max-width: none; }
.renov li.card { display: flex; gap: 14px; padding: 20px; margin: 0; }
.renov li.card .badge { width: 40px; height: 40px; margin: 0; }
.renov li.card p { margin: 0; }
.renov li.card strong { color: var(--ink); }
.renov h3 { margin-top: 1.5em; }
.renov ul { padding: 0; list-style: none; }
.renov .renov-sum { color: var(--ink); font-weight: 700; }
.renov .btn { margin-top: 8px; }
.readmore summary { list-style: none; cursor: pointer; }
.readmore summary::-webkit-details-marker { display: none; }
.readmore[open] > summary { display: none; }

/* Steps: one water pipe joins the numbered valves (down the number column on phones, through the circle row from 900px).
   ::before is the water (static: full), ::after the flow dashes (motion only). On phones the pipe runs on beside step 4 to the list's end.
   The li (not its ::before) is raised, so the circles stay above the pipe even mid-reveal, when opacity/translate make the li a stacking context. */
.steps { position: relative; display: grid; gap: 28px; margin: 0; padding: 0; list-style: none; counter-reset: step; }
.steps::before, .steps::after { content: ""; position: absolute; top: 22px; bottom: 0; left: 20px; width: 4px; border-radius: 2px; pointer-events: none; }
.steps::before { background: var(--blue); transform-origin: 0 0; }
.steps li { position: relative; z-index: 1; display: grid; grid-template-columns: 44px 1fr; column-gap: 16px; counter-increment: step; }
.steps li::before { content: counter(step); grid-row: span 2; display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%; background: var(--blue); color: #fff; font-weight: 700; }
.steps h3 { margin: 9px 0 4px; }
.steps p { margin: 0; }
.steps-cta { margin-top: 40px; }
@media (min-width: 900px) {
  .steps { grid-template-columns: repeat(4, 1fr); gap: 32px; }
  .steps::before, .steps::after { top: 20px; bottom: auto; left: 22px; right: calc(25% - 46px); width: auto; height: 4px; }
  .steps li { display: block; }
  .steps li::before { margin-bottom: 20px; }
}

/* Gallery: one large tile + four small ones */
.gallery { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.gallery a { position: relative; aspect-ratio: 1; overflow: hidden; overflow: clip; border-radius: 12px; background: var(--line); }
.gallery a:first-child { grid-column: span 2; }
.gallery img { width: 100%; height: 100%; object-fit: cover; transition: scale .6s var(--ease); }
/* Caption: a sibling of the photo, so zoom and parallax leave it still; later in the tree = above the photo, z-index auto = under the
   door panels (1). The fade runs in the 40px above the text, so label and title sit on the full .75 (>= 8:1 over white marble).
   Label + title clamp to 3 lines; the bottom gap is a transparent border, as padding would show a sliver of the clipped next line. */
.gallery .cap { position: absolute; inset: auto 0 0; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; padding: 40px 16px 0; border-bottom: 14px solid transparent; background: linear-gradient(transparent, rgba(12, 18, 26, .75) 40px) border-box; color: #fff; font-size: .95rem; font-weight: 600; line-height: 1.3; }
.gallery .cap small { display: block; font-size: .6875rem; letter-spacing: .1em; text-transform: uppercase; }
@media (min-width: 900px) {
  .gallery { grid-template-columns: repeat(4, 1fr); gap: 16px; }
  .gallery a:first-child { grid-row: span 2; }
}
.lightbox { max-width: 94vw; max-height: 94vh; padding: 0; border: 0; background: none; overflow: visible; }
.lightbox::backdrop { background: rgba(10, 14, 20, .9); }
.lightbox img { width: auto; max-width: 94vw; max-height: calc(94vh - 60px); border-radius: 10px; } /* room for a two-line caption */
.lightbox p { margin: 10px 0 0; color: #fff; font-size: .875rem; text-align: center; contain: inline-size; } /* contain: wraps to the photo's width instead of widening the dialog */
.lightbox button { position: fixed; top: 16px; right: 16px; display: grid; place-items: center; width: 44px; height: 44px; padding: 0; border: 0; border-radius: 50%; background: #fff; color: #101b24; cursor: pointer; }

/* Call to action band */
.cta { position: relative; isolation: isolate; overflow: hidden; overflow: clip; padding-block: clamp(56px, 7vw, 88px); background: var(--cta); color: rgba(255, 255, 255, .92); }
.cta h2 { color: #fff; }
.cta a:not(.btn) { color: #fff; font-weight: 700; }
.cta-inner { display: grid; gap: 28px; align-items: center; }
.cta-inner p:last-child { margin: 0; }
@media (min-width: 900px) { .cta-inner { grid-template-columns: 1fr auto; gap: 56px; } }
.cta-flow { position: absolute; inset: 0; z-index: -1; width: 100%; height: 100%; fill: none; stroke: rgba(255, 255, 255, .12); stroke-width: 1.5; stroke-linecap: round; }
.cta-flow .glint { display: none; }
/* The pipes cross the text at some widths: a halo in the band colour cuts them around every glyph, so text always sits on --blue (AA). */
.cta :is(h2, p, .btn-ghost:not(:hover)) { text-shadow: 0 0 2px var(--cta), 0 0 2px var(--cta), 0 0 2px var(--cta); }

/* FAQ */
.faq { max-width: 820px; }
.faq details { border-bottom: 1px solid var(--rule); }
.faq summary { position: relative; padding: 20px 44px 20px 0; color: var(--ink); font-weight: 600; cursor: pointer; list-style: none; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; position: absolute; top: 50%; right: 8px; translate: 0 -50%; color: var(--accent); font-size: 1.5rem; font-weight: 400; }
.faq details[open] summary::after { content: "–"; }
.faq details p { margin: 0; padding-bottom: 20px; }

/* Contact */
.contact-grid { display: grid; gap: 24px; align-items: start; }
@media (min-width: 900px) { .contact-grid { grid-template-columns: 5fr 7fr; gap: 32px; } }
.contact-list { display: grid; gap: 22px; margin: 0; padding: 0; list-style: none; }
.contact-list li { display: flex; gap: 14px; }
.contact-list .badge { width: 44px; height: 44px; margin: 0; flex: none; }
.contact-list strong { display: block; color: var(--ink); }
.contact-list a { overflow-wrap: anywhere; }
.field { display: grid; gap: 6px; margin: 0 0 18px; }
.field label { color: var(--ink); font-size: .9375rem; font-weight: 600; }
.field input, .field select, .field textarea { width: 100%; min-height: 48px; padding: 11px 14px; font: inherit; font-size: 16px; color: var(--ink); background: var(--surface); border: 1px solid var(--field-line); border-radius: 10px; }
.field textarea { min-height: 160px; resize: vertical; }
.field :focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(0, 112, 208, .22); }
@media (min-width: 600px) { .field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; } }
.hp { position: absolute; left: -9999px; }
.note { font-size: .875rem; }
.alert { margin: 0 0 20px; padding: 14px 16px; border: 1px solid; border-radius: 10px; }
.alert-ok { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok); }
.alert-error { background: var(--err-bg); border-color: var(--err-line); color: var(--err); }

/* Legal pages */
.prose { max-width: 760px; }
.prose h2 { margin-top: 2em; font-size: 1.25rem; }
.prose ul { padding-left: 1.25em; }
.prose a { overflow-wrap: anywhere; }

/* Footer */
.site-footer { padding-top: 56px; background: var(--footer); border-top: 1px solid var(--footer-line); color: #c4c8ce; font-size: .9375rem; }
.site-footer a { color: #fff; text-decoration: none; }
.site-footer a:hover { text-decoration: underline; }
.footer-grid { display: grid; gap: 32px; }
@media (min-width: 800px) { .footer-grid { grid-template-columns: 1.4fr 1.2fr .6fr; } }
.footer-brand img { width: 200px; margin-bottom: 28px; } /* roadmap v2 §3.1 */
.footer-brand strong { color: #fff; }
.footer-list { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
.footer-bottom { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 8px 24px; margin-top: 48px; padding-block: 20px; border-top: 1px solid rgba(255, 255, 255, .12); font-size: .875rem; }
.footer-bottom p { display: flex; gap: 20px; margin: 0; }
/* "Unterstützt von Sanad Tech Consulting", the footer's last line (roadmap v2 §5); the name never breaks. */
.powered { margin-block: 0; padding-block: 16px; border-top: 1px solid rgba(255, 255, 255, .1); color: rgba(255, 255, 255, .55); font-size: .8125rem; text-align: center; }
.powered a { color: inherit; white-space: nowrap; }
.powered a:hover { color: #fff; }
/* Pause switch for looping motion (WCAG 2.2.2): footer + ticker. main.js unhides it on pages with loops; print hides it. */
.motion-toggle { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0; border: 0; background: none; color: inherit; font: inherit; cursor: pointer; }
.motion-toggle[hidden], .motion-toggle[aria-pressed="true"] .i-pause, .motion-toggle:not([aria-pressed="true"]) .i-play { display: none; }
.motion-toggle .icon { width: 18px; height: 18px; }
.site-footer .motion-toggle:hover { color: #fff; }
@media print { .motion-toggle, .theme-btn { display: none; } }
@media (max-width: 799px) {
  .footer-grid { gap: 16px; }
  .site-footer a:not(.wa-link) { display: inline-block; min-height: 44px; padding-block: 10px; }
  .footer-list { gap: 0; }
  .footer-bottom { gap: 16px 24px; } /* roadmap §5.2: 16px between groups on phones */
}
@media (max-width: 959px) { .site-footer { padding-bottom: calc(88px + env(safe-area-inset-bottom)); } } /* the WhatsApp button (16 + 56 + 16, + the iPhone home bar) never sits on the Sanad line */

/* Motion. Keyframes (and @property) are inert until a rule below uses them; everything that moves lives in the three blocks after them.
   1. @media screen: the scroll-reveal hidden state (only main.js adds .reveal, home page only) and the hero text stagger.
   2. @media screen: the motion, for every screen.
   3. @media screen and (prefers-reduced-motion: reduce): the OS "reduce motion" setting switches off only what zooms, drifts or moves
      with the scroll (vestibular triggers); the rest stays alive and the loops keep their pause switch.
   Print gets the static page; without JS nothing is hidden and loops stay paused.
   Loops use animation-play-state: var(--loop, running); html.motion-paused (pause switch) and .idle (off-screen, main.js) set --loop: paused. */
@keyframes rise { from { opacity: 0; translate: 0 24px; } }
@keyframes pop { from { opacity: 0; scale: .6; } }
@keyframes settle { from { scale: var(--from, 1.06); } }
@keyframes grow { from { scale: 0 1; } to { scale: 1; } }
@keyframes draw { from { stroke-dashoffset: 21; } }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes enter { from { opacity: 0; translate: 0 12px; } }
@keyframes drift { to { scale: 1.04; } }
@keyframes sink { to { translate: 0 15%; } }
@keyframes lift { to { opacity: .3; translate: 0 -40px; } }
@keyframes ticker { to { translate: -50% 0; } }
@keyframes word { from { translate: 0 110%; rotate: 4deg; } }
@keyframes pipe { from { scale: var(--off); } }
@keyframes valve { from { rotate: -90deg; } 70% { rotate: 8deg; } to { rotate: 0deg; } }
@keyframes unlit { from { background-color: var(--surface); color: var(--accent); box-shadow: inset 0 0 0 2px var(--line); } }
@keyframes flow { to { translate: var(--flow); } }
@keyframes ink { to { --p: 1; } }
@keyframes doors { to { scale: 0 1; } }
@keyframes frame { from { translate: 0 calc(var(--d, 4%) * -1); } to { translate: 0 var(--d, 4%); } }
@keyframes write { from { stroke-dashoffset: 1.1; } }
@keyframes rewrite { from { stroke-dashoffset: 1.1; } }
@keyframes tighten { from { rotate: -90deg; } 60% { rotate: 12deg; } 80% { rotate: -4deg; } }
@keyframes retighten { from { rotate: -90deg; } 60% { rotate: 12deg; } 80% { rotate: -4deg; } }
@keyframes glint { from { offset-distance: 0%; } 45% { opacity: 1; } 50%, to { offset-distance: 100%; opacity: 0; } }
@keyframes pool { to { translate: 10% 8%; scale: 1.15; } }
@keyframes ripple { from { opacity: .7; } to { opacity: 0; scale: 1.15 1.6; } }
@keyframes reripple { from { opacity: .7; } to { opacity: 0; scale: 1.15 1.6; } }
@property --p { syntax: "<number>"; inherits: true; initial-value: 0; }

/* 1. Reveal hidden state + hero stagger (these nth-child delays outrank the hero rise shorthand in block 2). */
@media screen {
  .reveal:not(.in, :focus-within) { opacity: 0; }
  .hero .wrap > :nth-child(2) { animation-delay: 80ms; }
  .hero .wrap > :nth-child(3) { animation-delay: 160ms; }
  .hero .wrap > :nth-child(4) { animation-delay: 240ms; }
}

/* 2. Motion for every screen (block 3 trims it for "reduce motion"). */
@media screen {
  .motion-paused, .idle { --loop: paused; }
  @media (scripting: none) { :root { --loop: paused; } } /* no JS = no pause switch, so loops don't start (WCAG 2.2.2) */

  /* Page to page: the header stays put, the content crossfades and rises 12px (Chrome/Edge 126+, Safari 18.2+; others navigate as before). */
  @view-transition { navigation: auto; }
  .site-header { view-transition-name: header; }
  ::view-transition-old(root) { animation: fade .15s ease-in reverse both; }
  ::view-transition-new(root) { animation: enter .3s var(--ease) .05s both; }

  /* Hero: CSS only, from first paint (no hide-then-show). The image settles, then breathes (scale loop); it is never transparent (LCP). */
  .hero img { animation: settle 1.2s var(--ease) backwards, drift 30s ease-in-out 1.2s infinite alternate; animation-play-state: running, var(--loop, running); }
  .hero .wrap > * { animation: rise .8s var(--ease) backwards; }
  .hero .eyebrow::before { animation: grow .9s var(--ease) .2s backwards; }

  /* Scroll-linked chrome and hero depth. Without support: no header shadow/line, no parallax, no text lift. Hero parts use scroll(root):
     .hero is overflow:hidden, so scroll(nearest) would resolve to it and stay inactive. */
  @supports (animation-timeline: scroll()) {
    .site-header::after { content: ""; position: absolute; inset: 100% 0 auto; z-index: -1; height: 16px; background: linear-gradient(rgba(12, 18, 26, .08), transparent); opacity: 0; pointer-events: none; animation: fade 1ms linear both; animation-timeline: scroll(); animation-range: 0 120px; }
    .site-header::before { content: ""; position: absolute; inset: auto 0 -1px; height: 2px; background: var(--sky); scale: 0 1; transform-origin: 0 50%; pointer-events: none; animation: grow 1ms linear both; animation-timeline: scroll(root); }
    .hero::after { content: ""; position: absolute; inset: 0; z-index: -1; background: rgba(12, 18, 26, .45); opacity: 0; pointer-events: none; animation: fade 1ms linear both; animation-timeline: scroll(root); animation-range: 0 80vh; }
    @media (min-height: 600px) { .hero .wrap { animation: lift 1ms linear both; animation-timeline: scroll(root); animation-range: 0 80vh; } } /* shorter screens: the buttons sit below the fold and must not fade before they're reached */
    @media (min-width: 900px) {
      .hero img { animation: settle 1.2s var(--ease) backwards, drift 30s ease-in-out 1.2s infinite alternate, sink 1ms linear both; animation-play-state: running, var(--loop, running), running; animation-timeline: auto, auto, scroll(root); animation-range: normal, normal, 0 100vh; }
    }
  }

  /* Fact ticker: two identical lists of the six facts slide one list width per loop (seamless: each list's 14px side padding = half the 28px gap).
     Each list is at least a viewport wide, so wide screens never see an empty tail; space-around keeps the spacing even across the seam.
     Mouse hover (except over its own button, so a resume click acts at once), the pause switch and .idle stop it. The left edge fades into
     the band; the right one fades over 40px into a solid 56px gutter that holds the pause button, so text never runs under it
     (the gutter is the ::after's solid part: padding would not stop the overflowing track from painting there).
     Only with JS: without it there is no pause switch, so the band stays the static wrapped list. */
  @media (scripting: enabled) {
    .ticker { overflow: hidden; overflow: clip; }
    .ticker::before, .ticker::after { content: ""; position: absolute; inset: 0 auto 0 0; z-index: 1; width: 40px; background: linear-gradient(90deg, var(--blue-soft), transparent); pointer-events: none; }
    .ticker::after { inset: 0 0 0 auto; width: 96px; background: linear-gradient(270deg, var(--blue-soft) 56px, transparent); }
    .ticker-track { display: flex; width: max-content; animation: ticker 55s linear infinite; animation-play-state: var(--loop, running); }
    .ticker ul { flex-wrap: nowrap; justify-content: space-around; min-width: 100vw; padding: 16px 14px; }
    .ticker ul + ul { display: flex; }
    .ticker li { white-space: nowrap; }
    .ticker .motion-toggle:not([hidden]) { display: inline-flex; }
    @media (hover: hover) { .ticker:hover:not(:has(.motion-toggle:hover)) { --loop: paused; } }
  }

  /* Scroll reveal (home page only: main.js adds .reveal, .in and --i). */
  .reveal.in { animation: rise .8s var(--ease) calc(var(--i, 0) * 90ms) backwards; }
  .card.in .badge { animation: pop .6s var(--ease) calc(var(--i, 0) * 90ms + 150ms) backwards; }

  /* Masked word rise: main.js wraps each word of a revealing plain-text h2 in .wm > span; the words slide up out of a clip and replace the block rise.
     Only the y axis is clipped: side bearings (r, k, w) overhang the word box by up to ~1.5px and would be shaved for good. */
  .wm { display: inline-block; overflow: hidden; overflow: visible clip; padding-block: .1em .15em; margin-block: -.1em -.15em; vertical-align: bottom; }
  .wm > span { display: inline-block; transform-origin: 0 100%; }
  h2.reveal.in:has(.wm) { animation: none; }
  h2.in .wm > span { animation: word .6s var(--ease) calc(var(--i, 0) * 90ms + min(var(--w), 8) * 45ms) backwards; }

  /* Steps pipe: empty track (the list's background), water (::before) fills 1→4 with the scroll, each number "valve" turns blue
     with a small overshoot as the water reaches it, then faint dashes (::after) keep flowing (loop, paused by the switch and off-screen). */
  .steps { --off: 1 0; background: linear-gradient(var(--line) 0 0) 20px 22px / 4px calc(100% - 22px) no-repeat; }
  .steps::before { background: linear-gradient(var(--blue) calc(100% - 12px), var(--sky)); }
  .steps::after { --flow: 0 22px; top: 0; bottom: 22px; background: repeating-linear-gradient(rgba(255, 255, 255, .4) 0 6px, transparent 6px 22px); animation: flow 2.4s linear infinite; animation-play-state: var(--loop, running); }
  @media (min-width: 900px) {
    .steps { --off: 0 1; background-position: 22px 20px; background-size: calc(75% + 24px) 4px; }
    .steps::before { background: linear-gradient(90deg, var(--blue) calc(100% - 12px), var(--sky)); }
    .steps::after { --flow: 22px 0; top: 20px; bottom: auto; left: 0; right: calc(25% - 24px); background: repeating-linear-gradient(90deg, rgba(255, 255, 255, .4) 0 6px, transparent 6px 22px); }
  }
  /* Without scroll timelines (e.g. Firefox): the pipe fills once when the steps reveal. If they never get .reveal, it stays full. */
  @supports not (animation-timeline: view()) {
    .steps li.in::before { animation: pop .6s var(--ease) calc(var(--i, 0) * 90ms + 150ms) backwards; }
    .steps:has(.reveal):not(:has(.in))::before { scale: var(--off); }
    .steps:has(.reveal):not(:has(.in))::after { opacity: 0; }
    .steps:has(.in)::before { animation: pipe 1.4s var(--ease) .15s backwards; }
    .steps:has(.in)::after { animation: flow 2.4s linear infinite, fade .6s ease-out 1.5s backwards; animation-play-state: var(--loop, running), running; }
  }
  /* With scroll timelines: everything is scrubbed by the list's own view timeline and completes by cover 64%. */
  @supports (animation-timeline: view()) {
    .steps { view-timeline: --steps; }
    .steps::before { animation: pipe 1ms linear both; animation-timeline: --steps; animation-range: cover 18% cover 58%; }
    .steps::after { animation: flow 2.4s linear infinite, fade 1ms linear both; animation-play-state: var(--loop, running), running; animation-timeline: auto, --steps; animation-range: normal, cover 58% cover 64%; }
    /* The valve turns smoothly; its colour flips in one step at the end of the turn (a crossfade passes through an unreadable mid-tone). */
    .steps li::before { animation: valve 1ms linear both, unlit 1ms steps(1, end) both; animation-timeline: --steps; animation-range: cover 15% cover 21%; }
    .steps li:nth-child(2)::before { animation-range: cover 29% cover 35%; }
    .steps li:nth-child(3)::before { animation-range: cover 41% cover 47%; }
    .steps li:nth-child(4)::before { animation-range: cover 52% cover 58%; }
    @media (min-width: 900px) { .steps::before { animation-range: cover 18% cover 52%; } }
  }

  /* Ink-in (home about paragraph): words darken in reading order with the scroll and lighten again on the way up.
     The .85 floor keeps ghost words >= 4.5:1 on --bg-alt; the blue key phrases stay fully inked. main.js only splits where view() is supported. */
  @supports (animation-timeline: view()) {
    .ink { animation: ink 1ms linear both; animation-timeline: view(); animation-range: entry 25% cover 45%; }
    .ink > span:not(.hl) { opacity: clamp(.85, calc(.85 + (var(--p) * var(--total) - var(--n)) * .15), 1); }
    .ink .hl { color: var(--accent); }
  }

  /* Photo doors (home): two brand-blue panels part like cabinet doors while the photo settles from 1.15x (gallery: <= ~1s cascade).
     The panels exist only while main.js keeps .reveal, so no-JS, print, reduced motion and keyboard focus show the plain photo. */
  .door img, .gallery img { --from: 1.15; }
  .gallery a:is(:hover, :focus-visible) img { scale: 1.05; }
  :is(.door, .gallery a).reveal::before, :is(.door, .gallery a).reveal::after { content: ""; position: absolute; inset: 0 50% 0 0; z-index: 1; background: var(--blue); transform-origin: 0 50%; pointer-events: none; }
  :is(.door, .gallery a).reveal::after { inset: 0 0 0 50%; background: var(--blue-dark); transform-origin: 100% 50%; }
  :is(.door, .gallery a).reveal.in { animation: fade .3s ease-out calc(var(--i, 0) * 80ms) backwards; }
  :is(.door, .gallery a).in::before, :is(.door, .gallery a).in::after { animation: doors .6s var(--ease) calc(var(--i, 0) * 80ms + 150ms) both; }
  :is(.door, .gallery a).in img { animation: settle .9s var(--ease) calc(var(--i, 0) * 80ms + 150ms) backwards; }

  /* Frame parallax (desktop, scroll timelines): photos glide a few % inside their frames, each tile at its own depth.
     The 1.12x overscan hides the edges (|--d| <= 5% < 6%); settle starts above it and hover zooms above it (this 1.17 must stay after the 1.05 above).
     One comma list keeps settle + frame together. */
  @supports (animation-timeline: view()) {
    @media (min-width: 900px) {
      .door img, .gallery img { --from: 1.27; scale: 1.12; animation: frame 1ms linear both; animation-timeline: view(); }
      :is(.door, .gallery a).in img { animation: settle .9s var(--ease) calc(var(--i, 0) * 80ms + 150ms) backwards, frame 1ms linear both; animation-timeline: auto, view(); }
      .gallery a:is(:hover, :focus-visible) img { scale: 1.17; }
      .door img, .gallery a:nth-child(3) img, .gallery a:nth-child(5) img { --d: 4%; }
      .gallery a:nth-child(1) img { --d: 3%; }
      .gallery a:nth-child(2) img, .gallery a:nth-child(4) img { --d: 5%; }
    }
  }

  /* Service cards: the icons act out their job once on reveal and again on hover/focus/tap (plan lines write, wrench tightens, shield check draws). */
  .card .ln { stroke-dasharray: 1 2; }
  .card.in .ln { animation: write .7s ease-out calc(var(--i, 0) * 90ms + 450ms) backwards; }
  .card.in .i-wrench { animation: tighten .9s var(--ease) calc(var(--i, 0) * 90ms + 450ms) backwards; }
  .card:is(:hover, :focus-within) .ln { animation: rewrite .7s ease-out; }
  .card:is(:hover, :focus-within) .i-wrench { animation: retighten .9s var(--ease); }
  .card.in:is(:hover, :focus-within) .ln { animation: write .7s ease-out calc(var(--i, 0) * 90ms + 450ms) backwards, rewrite .7s ease-out; }
  .card.in:is(:hover, :focus-within) .i-wrench { animation: tighten .9s var(--ease) calc(var(--i, 0) * 90ms + 450ms) backwards, retighten .9s var(--ease); }
  /* Fine pointer: the card lifts, leans at most 3° toward the cursor (main.js writes --x/--y/--rx/--ry) and a soft glow follows; it springs back on leave.
     Tilt uses transform, the reveal uses translate: no clash. Keyboard focus gets the centred glow. */
  @media (hover: hover) and (pointer: fine) {
    .grid-3 .card { position: relative; isolation: isolate; transition: transform .6s var(--ease); }
    .grid-3 .card::before { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit; background: radial-gradient(280px circle at var(--x, 50%) var(--y, 50%), color-mix(in srgb, var(--blue) 12%, transparent), transparent 70%); opacity: 0; transition: opacity .3s; pointer-events: none; }
    .grid-3 .card:is(:hover, :focus-within) { transform: perspective(900px) translateY(-4px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); transition-duration: .15s; }
    .grid-3 .card:is(:hover, :focus-within)::before { opacity: 1; }
    .grid-3 .card::after { content: ""; position: absolute; inset: -4px -4px -8px; z-index: -1; } /* hit area: the lifted/leaning edge can't slip from under the pointer (hover flicker) */
  }

  /* Living CTA band (every page with cta()): a droplet of light runs along each pipe in turn (8s cycle, staggered) and two dark light pools
     drift (both loops: paused by the switch and off-screen). Darker pools only raise text contrast. The call button sends out 3 ripples
     when it comes into view (again each time it returns) and 2 on hover/focus, unless paused. Each burst is < 5s.
     The droplet moves on offset-path (no repaint per frame). It is a straight dash, so it stays a few units short: a longer one
     sticks out of the pipe as a tangent at the rounded bends. Without offset-path support it stays parked outside the viewBox. */
  .cta-flow .glint { display: inline; stroke: rgba(255, 255, 255, .75); stroke-width: 3; offset-rotate: auto; animation: glint 8s linear calc(var(--k) * -1.6s) infinite; animation-play-state: var(--loop, running); }
  .cta::before, .cta::after { content: ""; position: absolute; z-index: -1; width: 70vmax; aspect-ratio: 1; border-radius: 50%; background: radial-gradient(closest-side, rgba(0, 45, 100, .35), transparent); pointer-events: none; animation: pool 24s ease-in-out infinite alternate; animation-play-state: var(--loop, running); }
  .cta::before { top: -45%; left: -25%; }
  .cta::after { right: -30%; bottom: -55%; animation-delay: -12s; }
  .cta .btn-ghost { position: relative; }
  .cta .btn-ghost::after { content: ""; position: absolute; inset: -2px; border: 2px solid #fff; border-radius: inherit; opacity: 0; pointer-events: none; }
  :root:not(.motion-paused) .cta .btn-ghost:not(.idle, .reveal:not(.in))::after { animation: ripple 1.5s ease-out .4s 3; }
  :root:not(.motion-paused) .cta .btn-ghost:not(.idle, .reveal:not(.in)):is(:hover, :focus-visible)::after { animation: ripple 1.5s ease-out .4s 3, reripple 1.5s ease-out 2; }

  /* Hover and keyboard focus */
  .btn:is(:hover, :focus-visible) { translate: 0 -2px; }
  .btn:active { translate: none; transition-duration: .1s; }
  .link-arrow::after { transition: scale .35s var(--ease); }
  .link-arrow:is(:hover, :focus-visible) .icon { translate: 4px 0; }

  /* Lightbox: fade only on the dialog (a transform would make it the containing block of its fixed close button). The photo rises. */
  .lightbox[open], .lightbox[open]::backdrop { animation: fade .3s ease-out; }
  .lightbox[open] img { animation: rise .4s var(--ease); }
}

/* 3. OS "reduce motion": no zoom, drift or scroll-linked movement of photos and the hero text, and pages only crossfade.
   Reveals, ticker, steps pipe, photo doors, icon stories and the CTA flow stay; main.js skips the card tilt. */
@media screen and (prefers-reduced-motion: reduce) {
  ::view-transition-new(root) { animation: fade .25s ease-out .05s both; }
  .hero img, .hero .wrap { animation: none; }
  .door img, .gallery img, :is(.door, .gallery a).in img { animation: none; scale: none; }
  .gallery a:is(:hover, :focus-visible) img { scale: 1.05; }
}
