/* ==========================================================================
   Project Lead GmbH — main.css
   The design system from 02-solution/design-system.md, implemented.
   One file, no build step, readable and editable by hand.
   ========================================================================== */

/* --- 1. Tokens ----------------------------------------------------------- */

:root {
  /* Brand — from Branding-Guidelines.png, verified against the artwork */
  --ink:            #11111f;
  --accent:         #ffc600;

  /* Neutrals — tints of --ink, so the greys keep the brand's blue cast */
  --ink-muted:      #64646d;  /* 5.86:1 on white */
  --border-strong:  #88888f;  /* 3.52:1 on white — clears 1.4.11 */
  --border-hair:    #dbdbdd;  /* decorative only */

  --surface:        #ffffff;
  --surface-sunken: #f7f7f8;
  --surface-dark:   #11111f;

  /* Type */
  --text-xs:   0.875rem;
  --text-sm:   1rem;
  --text-base: 1.125rem;
  --text-lg:   1.375rem;
  --text-xl:   1.75rem;
  --text-2xl:  clamp(2rem, 1.25rem + 3.2vw, 2.75rem);

  --leading-tight: 1.2;
  --leading-body:  1.6;
  --measure:       65ch;

  /* Space */
  --space-1: 0.25rem;  --space-2: 0.5rem;   --space-3: 0.75rem;
  --space-4: 1rem;     --space-6: 1.5rem;   --space-8: 2rem;
  --space-12: 3rem;    --space-16: 4rem;    --space-24: 6rem;

  --container: 72rem;
  --gutter:    var(--space-6);
  /* Fluid, because a fixed 96px reads as a gap on a 1024-1440px laptop while
     being right at 1440+. Floor 3rem keeps the mobile rhythm the owner
     already approved; 5vw is the taper; 4.5rem caps it on wide screens.
     1024 -> 51px, 1280 -> 64px, 1440 -> 72px (was a flat 96px). */
  --section-y: clamp(3rem, 5vw, 4.5rem);
}

@media (max-width: 479px) {
  :root { --gutter: var(--space-4); }
}

/* --- 2. Fonts ------------------------------------------------------------
   Glacial Indifference by Alfredo Marco Pradil, SIL OFL 1.1.
   Licence text shipped at assets/fonts/OFL.txt (OFL clause 2 requires the
   copyright notice to travel with every copy).

   Why the family is renamed "PL Sans" and not called by its real name:
   the font's own name table (nameID 13) declares
       "with Reserved Font Name Glacial Indifference"
   and OFL clause 3 forbids a Modified Version from using the reserved name as
   the primary name presented to users. The .woff2 files are a format
   conversion, which OFL's definitions list explicitly as a Modified Version
   ("...or by changing formats..."), and their FFTM table shows FontForge did
   it. So the reserved name may not be used here. Verified in the binary --
   the licence .txt bundled with the font carries no copyright line at all and
   therefore appears to declare no reserved name.

   Known glyph gap, measured against every character used on this site
   (87 distinct): the face lacks exactly three of them -- U+00AB and U+00BB
   (guillemets) and U+00AD (soft hyphen). All three occur only in
   /datenschutzerklaerung/, 7 guillemet pairs in the law-firm boilerplate.
   Those characters fall back to system-ui; the rest of the site is covered.
   The original .otf lacks them too, so the conversion did not drop them.

   Sifonn Pro is NOT used -- see 02-solution/brand/font-licence-assessment.md
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "PL Sans";
  src: url("../fonts/pl-sans-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "PL Sans";
  src: url("../fonts/pl-sans-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}

/* --- 3. Reset ------------------------------------------------------------ */

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

body, h1, h2, h3, p, ul, ol, figure, blockquote { margin: 0; }
ul[class], ol[class] { list-style: none; padding: 0; }

img { max-width: 100%; height: auto; display: block; }

html { -webkit-text-size-adjust: 100%; }

body {
  font-family: "PL Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: var(--text-base);
  line-height: var(--leading-body);
  color: var(--ink);
  background: var(--surface);
  -webkit-font-smoothing: antialiased;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --- 4. Typography ------------------------------------------------------- */

h1, h2, h3 { line-height: var(--leading-tight); font-weight: 700; text-wrap: balance; }
h1 { font-size: var(--text-2xl); letter-spacing: -0.01em; }
h2 { font-size: var(--text-xl); }
h3 { font-size: var(--text-lg); line-height: 1.3; }

p { max-width: var(--measure); }
/* German runs long. Hyphenate body copy — lang="de-CH" gives real German rules —
   and let headings break rather than overflow. */
p, li { overflow-wrap: break-word; hyphens: auto; }
h1, h2, h3 { overflow-wrap: break-word; }
p + p { margin-top: var(--space-4); }

.lead { font-size: var(--text-lg); line-height: 1.5; }
.meta { font-size: var(--text-sm); color: var(--ink-muted); }

/* The family ships no italic; a synthesised oblique looks like a mistake. */
em, i { font-style: normal; font-weight: 700; }

a { color: inherit; text-decoration: underline; text-underline-offset: 0.15em; }
a:hover { text-decoration-thickness: 2px; }

/* --- 5. Focus — never removed ------------------------------------------- */

:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
  border-radius: 2px;
}
.band--dark :focus-visible,
.band--accent :focus-visible { outline-color: var(--surface); }
.band--dark :focus-visible { outline-color: var(--surface); }

/* --- 6. Layout ----------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.band { padding-block: var(--section-y); }
.band--sunken { background: var(--surface-sunken); }
.band--dark   { background: var(--surface-dark); color: var(--surface); }
.band--accent { background: var(--accent); color: var(--ink); }

.band--dark .meta { color: #c9c9cf; }   /* 11.34:1 on --surface-dark */

.stack > * + * { margin-top: var(--space-4); }
.stack-lg > * + * { margin-top: var(--space-8); }

/* Ein einzelner Abstand nach oben, wo der Elternblock kein .stack ist und nur
   ein Kind Abstand braucht. Ersetzt zwoelf style-Attribute im HTML: die CSP
   erlaubt style-src 'self' ohne 'unsafe-inline', und damit sind inline
   style-Attribute geblockt. Gemessen: mit der Richtlinie kam margin-top als
   0px an, die Abstaende fehlten also auf allen sieben Seiten. */
.mt    { margin-top: var(--space-4); }
.mt-lg { margin-top: var(--space-8); }

.skip-link {
  position: absolute; left: var(--space-4); top: var(--space-4);
  background: var(--ink); color: var(--surface);
  padding: var(--space-3) var(--space-4);
  transform: translateY(-200%);
}
.skip-link:focus { transform: translateY(0); }

/* --- 7. Header ----------------------------------------------------------- */

.site-header {
  border-bottom: 1px solid var(--border-hair);
  padding-block: var(--space-4);
}
.site-header .container {
  display: flex; flex-wrap: wrap; gap: var(--space-4);
  align-items: center; justify-content: space-between;
}
.site-header__logo img { width: 180px; height: 108px; }

.nav ul { display: flex; flex-wrap: wrap; gap: var(--space-6); list-style: none; margin: 0; padding: 0; }
.nav a {
  display: inline-block;              /* an inline <a> gives block padding no hit area */
  text-decoration: none;
  padding-block: var(--space-3);      /* WCAG 2.2 SC 2.5.8 wants >= 24px; this gives ~45px */
  border-bottom: 3px solid transparent;
}
.nav a:hover { border-bottom-color: var(--border-strong); }
/* Current page: weight AND a bar. Never colour alone (WCAG 1.4.1). */
.nav a[aria-current="page"] { font-weight: 700; border-bottom-color: var(--accent); }

@media (max-width: 599px) {
  .site-header__logo img { width: 140px; height: 84px; }
  .nav ul { gap: var(--space-4); }
  .nav a { font-size: var(--text-sm); }
}

/* --- 8. Hero ------------------------------------------------------------- */

.hero { padding-block: var(--space-24) var(--space-16); }   /* deliberately NOT
   tied to --section-y: the owner's complaint was about the gaps between content
   blocks on a laptop, and mobile was explicitly approved as it stands. Making the
   hero fluid would have shrunk it from 96px to 60px on a phone, which nobody
   asked for. */
.hero h1 { max-width: 20ch; }

/* Hero mit Foto -- nur auf der Startseite, als Test (Klasse .hero--photo).
   Das Bild ist dekorativ: die Aussage steht im Text, deshalb CSS-Hintergrund
   und kein <img> mit alt. --surface-dark bleibt als background-color stehen,
   damit die Gestaltung auch hält, wenn das Bild nicht lädt.

   Der Verlauf ist kein Geschmacksurteil, sondern gerechnet: die obere Bildhaelfte
   ist heller Himmel, und genau dort steht der Text. Werte siehe Messung im PR. */
.hero--photo {
  background-color: var(--surface-dark);
  background-image:
    linear-gradient(180deg, rgba(17,17,31,0.88) 0%, rgba(17,17,31,0.80) 55%, rgba(17,17,31,0.74) 100%),
    url("../img/hero.webp");
  background-size: cover;
  background-position: center 62%;
  background-repeat: no-repeat;
}
.hero__kicker {
  font-size: var(--text-lg);
  color: var(--accent);          /* 11.86:1 on --surface-dark */
  margin-top: var(--space-4);
}

/* --- 9. Grid ------------------------------------------------------------- */

.grid { display: grid; gap: var(--space-8); }
/* Grid items default to min-width:auto, so a long word sets the track's minimum.
   German compounds ("Digitalisierungsprojekte") then push the grid past the
   viewport. Measured: 2px of horizontal scroll at exactly 768px before this. */
.grid > * { min-width: 0; }
@media (min-width: 768px) {
  .grid--3 { grid-template-columns: repeat(3, 1fr); }
  .grid--2 { grid-template-columns: repeat(2, 1fr); }
  .grid--portrait { grid-template-columns: 1fr 2fr; gap: var(--space-12); align-items: start; }
}

/* --- 10. Service card ---------------------------------------------------- */

.card {
  display: block;
  text-decoration: none;
  padding: var(--space-8);
  background: var(--surface);
  border: 1px solid var(--border-hair);
  border-top: 4px solid var(--ink);
}
.card:hover { border-top-color: var(--accent); background: var(--surface-sunken); }
.card img { width: 64px; height: 56px; margin-bottom: var(--space-4); }
.card h3 { margin-bottom: var(--space-3); }
.card p { color: var(--ink-muted); max-width: none; }
.card__more { display: inline-block; margin-top: var(--space-4); font-weight: 700; text-decoration: underline; }

/* --- 10b. Credential list -------------------------------------------------
   Design system 5.6. The fix for the three image-only sections of /wer/:
   the NAME is text and mandatory, so certifications and clients are readable
   by a screen reader and by a search engine, not only visible as logos. */

.credlist { list-style: none; margin: var(--space-6) 0 0; padding: 0; display: grid; gap: var(--space-4); }
@media (min-width: 768px) { .credlist { grid-template-columns: repeat(2, 1fr); gap: var(--space-6); } }
.credlist > li {
  min-width: 0;
  padding: var(--space-4) var(--space-6);
  background: var(--surface);
  border-left: 4px solid var(--accent);   /* a fill, not a hairline — 1.4.11 safe as decoration */
}
.band--sunken .credlist > li { background: var(--surface); }
.credlist strong { display: block; font-size: var(--text-base); }
.credlist .meta { display: block; margin-top: var(--space-1); }
.credlist a { display: inline-block; padding-block: var(--space-1); }

/* --- 10c. Logo grid ------------------------------------------------------
   Owner instruction: show the logos; keep the names available to screen
   readers and search engines but not visible.

   The name lives in each image's alt attribute. That IS the invisible text:
   alt is announced by screen readers, read by crawlers, and shown if the
   image fails — and it adds no duplicate announcement the way a second
   visually-hidden label next to the image would. */

.logo-grid {
  list-style: none; margin: var(--space-8) 0 0; padding: 0;
  display: grid; gap: var(--space-8);
  grid-template-columns: repeat(2, 1fr);
  align-items: center;
}
@media (min-width: 600px) { .logo-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 900px) { .logo-grid { grid-template-columns: repeat(4, 1fr); } }

/* The cell height is FIXED. Logos are lazy-loaded, and with an auto height the
   row grew as each image arrived — measured at 68px of cumulative layout shift
   across this section, which fails quality bar Q-E. A fixed cell reserves the
   space whatever the image's aspect ratio, so nothing moves. */
.logo-grid > li {
  min-width: 0; height: 72px;
  display: flex; justify-content: center; align-items: center;
}
.logo-grid img {
  max-width: 100%;
  max-height: 56px;
  width: auto;
  height: auto;
  object-fit: contain;
}
/* The link is the target, so it carries the 24px minimum, not the image.
   Filling the cell also makes the whole tile clickable. */
.logo-grid a {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%; padding: var(--space-2);
}

/* Screen-reader-only. Unlike display:none this IS announced — use it for text
   that must reach assistive technology but not the page. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* --- 10d. Prose ----------------------------------------------------------
   Long-form legal text. The only place on the site with sustained reading,
   so the measure cap and the vertical rhythm matter most here. */

.prose { max-width: var(--measure); }
.prose h2 { margin-top: var(--space-12); margin-bottom: var(--space-4); }
.prose h3 { margin-top: var(--space-8);  margin-bottom: var(--space-3); }
.prose h2:first-child, .prose h3:first-child { margin-top: 0; }
.prose p { margin-bottom: var(--space-4); }
.prose ul, .prose ol { margin: 0 0 var(--space-6); padding-left: var(--space-6); }
.prose li { margin-bottom: var(--space-3); max-width: var(--measure); }
.prose a { overflow-wrap: anywhere; }   /* long URLs must not push the page wide */

/* --- 11. Buttons --------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px;                       /* WCAG 2.5.8 */
  padding: var(--space-3) var(--space-6);
  font-weight: 700; text-decoration: none;
  border: 2px solid transparent;
}
.btn--primary { background: var(--ink); color: var(--surface); }
.btn--primary:hover { background: #000; }
.btn--on-accent { background: var(--ink); color: var(--surface); }
.btn--secondary { border-color: var(--border-strong); color: var(--ink); }
.btn--secondary:hover { border-color: var(--ink); }
.band--accent .btn--secondary { border-color: var(--ink); }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--space-4); margin-top: var(--space-6); }

/* A link that is a whole paragraph is not "inline" under 2.5.8's exception,
   so it carries the 24px minimum like any other standalone target. */
.link-block { display: inline-block; padding-block: var(--space-1); font-weight: 700; }

/* SC 2.5.8: a link that is alone in its paragraph, or the only content of a
   list item outside a nav, is not "inline in a sentence" -- the inline
   exception does not apply and it needs a real hit area. Measured at 22px
   before this rule on /impressum/, /datenschutzerklaerung/ and /404.html. */
.prose p > a:only-child,
.linklist a { display: inline-block; padding-block: var(--space-1); }

/* --- Contact masking -----------------------------------------------------
   Owner instruction: phone and e-mail must not be trivially scrapable.

   .nospam holds decoy text that is never rendered. Screen readers skip
   display:none and text selection excludes it, so assistive technology and
   copy-paste get the correct value while a tag-stripping harvester gets a
   poisoned one. The href is HTML-entity encoded, which defeats a regex run
   over the raw HTML.

   Neither trick survives a scraper that actually renders the page, and that
   is stated rather than implied — see 02-solution/contact-masking.md.
   No JavaScript is involved, so the charter's "readable without JS" holds. */
.nospam { display: none; }

/* --- 12. Footer ---------------------------------------------------------- */

.site-footer { font-size: var(--text-sm); }
.site-footer a {
  display: inline-block;
  padding-block: var(--space-1);      /* 16px x 1.5 line + 8px = 32px */
  text-decoration: none;
}
.site-footer a:hover { text-decoration: underline; }
.site-footer h2 { font-size: var(--text-lg); margin-bottom: var(--space-4); }
.site-footer__cols { display: grid; gap: var(--space-8); }
@media (min-width: 768px) { .site-footer__cols { grid-template-columns: 2fr 1fr 1fr; } }
/* Kein margin hier: der Reset in Abschnitt 3 setzt ul auf 0, und diese Regel
   waere spezifischer als .mt - die Adressliste verlore ihren Abstand wieder. */
.site-footer ul { list-style: none; padding: 0; }
.site-footer li + li { margin-top: var(--space-2); }
.site-footer__legal {
  margin-top: var(--space-12); padding-top: var(--space-6);
  border-top: 1px solid #33333d;
  color: #c9c9cf;
}
