/* ==========================================================================
   Linknetz — Profilseite (Design-Review Paket 8)

   Bis Paket 7 stand das alles als eigener <style>-Block in
   src/views/profile.ejs — die einzige Seite ausserhalb der Groessenabnahmen,
   und ausgerechnet die mit den meisten Aufrufen.

   Warum eine eigene Datei statt der Regeln in style.css: am 22.09.2026 im
   Browser gemessen (page.coverage.startCSSCoverage, /team, 390 px und
   1280 px) nutzte die Profilseite 11.592 von 130.961 Zeichen aus style.css
   = 8,9 %. Davon waren 10.452 Zeichen die drei Tokenbloecke und der Rest der
   Grundschicht; eine einzige Komponentenklasse aus style.css nutzte sie
   nicht. Die Seite laedt deshalb tokens.css + profile.css (~5,5 KB) statt
   style.css (32,3 KB uebertragen, brotli). Die Tokens stehen dabei genau
   einmal, in tokens.css — dieselbe Begruendung, mit der Paket 7 die
   Icon-Pfade in eine Datei gelegt hat.

   Masse folgen docs/reference/dark-tech-redesign-mockups/Profile.dc.html.
   Triage jeder einzelnen Regel: docs/reviews/2026-09-22-paket-8-profil-triage.md
   ========================================================================== */

/* Die Seite ist eine zentrierte Spalte, kein Dokumentfluss — deshalb hier
   und nicht in der Grundschicht. */
body {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  /* Seitliche Safe-Area (Befund Mobile-M12): im Querformat schiebt die Notch
     den nutzbaren Bereich ein, 1rem allein laesst die Linkzeilen darunter
     laufen. Gleiche Schreibweise wie .container in style.css. */
  padding: 2.5rem max(var(--space-4), env(safe-area-inset-right))
           max(var(--space-8), env(safe-area-inset-bottom))
           max(var(--space-4), env(safe-area-inset-left));
}

.profile-card-container {
  width: 100%;
  /* Mockup: 480px. Vorher 580px — bei zentrierter Kurzbio wuchs die
     Zeilenlaenge dadurch auf ~63 Zeichen (Befund UI-M13). */
  max-width: 480px;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin: 0 auto;
}

/* <main> um Avatar bis Linkliste (UX-M15). Uebernimmt Breite und Spalte des
   Containers: als Flex-Kind mit align-items:center waere es sonst nur so
   breit wie sein Inhalt, und .links-list (width:100%) schrumpfte mit. */
.profile-main {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Dunkler Kopf auf hellem Koerper (2026-09-23, Variante A). Die Tokens
   liefert .surface-dark am selben Element, hier steht nur die Form. Auf dem
   Desktop eine Karte in der Spalte; auf dem Handy laeuft sie bis an Rand und
   Statusleiste (siehe Media-Query unten), dafuer bleibt theme-color in
   profile.ejs dunkel. */
.profile-head {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--space-8) var(--space-6);
  border-radius: var(--radius-lg);
}

/* a93: Knopf „Kontakt speichern“ unter dem Profiltext. Form und Fuellung
   wie die Links der Vorlage (shape/fill unten), die Kante ist aber immer
   >= 3:1 gegen die Flaeche darunter (WCAG 1.4.11): ohne Kopfkarte die
   Akzentlinie (renderDesign rechnet sie gegen Seite und Knopfflaeche), auf
   der dunklen Kopfkarte (immer auf dem Foto) eine eigene Stufe mit
   --text-muted-2 als Rand wie die Kopfkarte selbst. So steht der Text nie
   direkt auf dem Foto. 44 px Trefferflaeche, Fokusring aus tokens.css.
   Geprueft in test/e2e/visitenkarte-lokal.e2e.js. */
.profile-vcard-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  margin-top: var(--space-4);
  padding: 0 var(--space-4);
  background: var(--bg-surface-elevated);
  border: 1px solid var(--profile-accent-line, var(--border-control));
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-size: var(--type-md);
  font-weight: 600;
  text-decoration: none;
  transition: border-color var(--transition-normal), transform var(--transition-normal);
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.profile-shape-square .profile-vcard-btn { border-radius: 0; }
.profile-shape-pill .profile-vcard-btn { border-radius: var(--radius-pill); }
.profile-fill-filled .profile-vcard-btn {
  background: var(--accent);
  color: var(--profile-on-accent);
}
.profile-fill-outline .profile-vcard-btn {
  background: transparent;
  border-width: 2px;
}
/* Dunkle Kopfkarte: --bg-surface-alt liegt eine Stufe neben beiden
   Kartenflaechen (--bg-base auf hellem, --bg-surface-elevated auf dunklem
   Schema); der Akzent der Vorlage gilt hier nicht (.surface-dark setzt Lime). */
.profile-head.surface-dark .profile-vcard-btn {
  background: var(--bg-surface-alt);
  border: 1px solid var(--text-muted-2);
  color: var(--text-primary);
}
.profile-vcard-btn:active { transform: scale(0.97); }
/* Hover: ohne Karte die Textfarbe (>= 4,5:1 gegen die Seite, validateDesign),
   auf der Karte das Lime aus .surface-dark — nicht --profile-accent-line,
   die ist gegen die Seite gerechnet. */
@media (hover: hover) and (pointer: fine) {
  .profile-vcard-btn:hover {
    border-color: var(--text-primary);
    transform: translateY(-2px);
  }
  .profile-head.surface-dark .profile-vcard-btn:hover { border-color: var(--accent); }
}

/* 512 = 480 Spalte + 2 x 16 Rand: darunter fuellt die Spalte den Viewport
   und die negativen Raender treffen genau den Seitenrand. Hochformat, daher
   ist der seitliche Body-Rand hier immer --space-4 (die Safe-Area greift
   erst im Querformat, und das ist breiter als 512). */
@media (max-width: 512px) {
  .profile-head {
    width: calc(100% + 2 * var(--space-4));
    margin: -2.5rem calc(-1 * var(--space-4)) 0;
    padding-top: calc(2.5rem + env(safe-area-inset-top, 0px));
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  }
}

/* a24-Fix A3: .surface-dark (tokens.css) setzt background: var(--bg-base) —
   im dunklen Schema ist das dieselbe Farbe wie der Seitenkoerper (beide
   #0e0f12), die Kopfkarte war also unsichtbar. Nur in diesem einen Fall
   (Karte UND Schema beide dunkel) braucht die Karte die naechste Ebene.
   a24-Nachbesserung Runde 2 (K2): --bg-surface-elevated (#232630) ist gegen
   einen eigenen dunklen Hintergrund (background.type color/gradient) kein
   verlaesslicher Kontrast mehr — der Nutzer kann dort fast dieselbe Farbe
   waehlen (validateDesign prueft nur Textlesbarkeit, keine Flaechenabgrenzung).
   NICHT --border-control (#616470): der schafft gegen die Kartenflaeche
   selbst (--bg-surface-elevated) nur 2,56:1 (a24-Fix A4 hat das schon einmal
   nachgerechnet). Stattdessen --text-muted-2 (#8a8d96): design-tokens.test.js
   prueft jede Textfarbe gegen jede Flaeche des Schemas (>=4,5:1), und
   validateDesign verlangt fuer JEDEN erlaubten Hintergrund im dunklen Schema
   AA (>=4,5:1) fuer alle vier Textfarben inkl. muted-2 (textReadable) — der
   Rand ist damit unabhaengig vom gewaehlten Hintergrund UND gegen die
   Kartenflaeche selbst sichtbar, ohne beim Speichern etwas abzulehnen. */
body.surface-dark .profile-head.surface-dark {
  background: var(--bg-surface-elevated);
  border: 1px solid var(--text-muted-2);
}

/* a64: auf dem Handy laeuft die Karte randlos bis an Statusleiste und
   Seitenrand — dort zeichnete der Rand oben und seitlich einen hellen Strich.
   Nur die Unterkante grenzt an den Koerper. Nach der Grundregel, sonst
   gewinnt deren `border`. */
@media (max-width: 512px) {
  body.surface-dark .profile-head.surface-dark {
    border-width: 0 0 1px;
  }
}

/* Profilspezifische Geometrie: 96px Kreis mit 3px Rand, wie im Mockup.
   KEIN box-shadow (Befund UI-H3). Am 22.09.2026 pixelgenau nachgemessen,
   ausserhalb des Kreises und gegen einen Rauschboden von 0:
     0 4px 20px rgba(0,0,0,.08)      (Bestand)  -> 1 von 255
     0 4px 24px rgba(0,0,0,.4)       (Mockup)   -> 4 von 255
     0 8px 24px -8px rgba(0,0,0,.5)  (--shadow-card) -> 4 von 255
   Auf --bg-base #0e0f12 ist ein schwarzer Schlagschatten nicht darstellbar —
   auch der des Mockups nicht. Die Ebene traegt hier die hellere Flaeche
   (--bg-surface-elevated) plus Rand, wie es der Kommentar an .surface-dark
   in tokens.css beschreibt. */
.profile-avatar-wrapper {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  overflow: hidden;
  /* 20px wie im Mockup; die Skala springt von 16 auf 24. */
  margin-bottom: 1.25rem;
  border: 3px solid var(--border-medium);
  background: var(--bg-surface-elevated);
  display: flex;
  align-items: center;
  justify-content: center;
}

.profile-avatar {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Platzhalter, wenn kein Bild hochgeladen ist: Lucide `user` aus dem Satz.
   .icon steht auf 18px, hier traegt der Kreis die Groesse. */
.profile-avatar-placeholder {
  width: 44px;
  height: 44px;
  color: var(--text-muted);
}

.profile-name {
  font-size: var(--type-3xl);
  font-weight: 700;
  margin: 0 0 var(--space-2) 0;
  letter-spacing: -0.02em;
}

.profile-bio {
  font-size: var(--type-md);
  color: var(--text-secondary);
  margin: 0;
  /* Mockup: 340px — bei zentriertem Text rund 48 Zeichen je Zeile. */
  max-width: 340px;
  line-height: 1.55;
}

.links-list {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-3-5);
  /* 40px wie im Mockup; die Skala kennt 32 und 48, keine 40. Ein Token fuer
     genau eine Stelle waere die Skala verwaessert. Oben der Abstand zum
     Kopf, den bis 0.13.7 die Bio als margin-bottom trug. */
  margin: var(--space-6) 0 2.5rem;
}

.link-button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  /* Mockup 16px/20px. 20px hat kein --space-Token (die Skala springt von 16
     auf 24) und liegt auf dem 4px-Raster. */
  padding: var(--space-4) 1.25rem;
  /* Weiss auf --bg-base statt --bg-surface: auf dem hellen Grund liegt
     --bg-surface nur ΔL* ~4 darueber, die Zeile laese sich als Flaeche,
     nicht als Schaltflaeche. --border-medium aus demselben Grund. */
  background: var(--bg-surface-elevated);
  border: 1px solid var(--border-medium);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  text-align: left;
  text-decoration: none;
  font-weight: 600;
  font-size: var(--type-lg);
  /* Benannte Eigenschaften statt `all` (Befund UI-M8): `transition: all`
     verschluckt sonst auch die Mutationen, mit denen die Browser-Abnahme
     ihre eigenen Zusicherungen prueft. */
  transition: background var(--transition-normal), border-color var(--transition-normal),
              transform var(--transition-normal), box-shadow var(--transition-normal);
  /* KEIN box-shadow im Ruhezustand: 0 2px 8px rgba(0,0,0,.05) war auf
     --bg-base mit 1 von 255 nicht darstellbar (Befund UI-H3). Der
     Hover-Schatten unten bleibt — er ist gruen, nicht schwarz, und wurde
     mit 20 von 255 gemessen. */
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

/* Richtungshinweis: ohne ihn sind die Zeilen optisch nicht von
   Ueberschriften zu unterscheiden (Befund UI-M13). Bewusst als Icon aus dem
   Satz und nicht als ::after mit mask-image — ein zweiter Mechanismus neben
   dem Icon-System ist genau das, was Paket 7 beim Haken der Preisliste
   abgeschafft hat. */
.profile-link-chevron {
  color: var(--text-muted);
}

/* Ohne @media(hover:hover) blieb der zuletzt angetippte Button nach der
   Rueckkehr aus dem target=_blank-Tab dauerhaft angehoben/umrandet, als waere
   er ausgewaehlt — Befund Mobile-H1, Design-Review 2026-09-20. */
@media (hover: hover) and (pointer: fine) {
  .link-button:hover {
    border-color: var(--accent-cyan-glow);
    transform: translateY(-2px);
    box-shadow: 0 6px 20px color-mix(in oklch, var(--accent) 20%, transparent);
  }

  .link-button:hover .profile-link-chevron {
    color: var(--accent-cyan-glow);
  }
}

.link-button:active {
  transform: scale(0.98);
  background: var(--bg-surface);
}

/* Leerzustand. Stand bis Paket 8 als Inline-Stil im Markup und war damit
   fuer test/design-scale.test.js unsichtbar. */
.links-empty {
  color: var(--text-muted);
  padding: var(--space-8) 0;
  font-size: var(--type-lg);
}

/* a25: Bausteine. Stehen direkt auf dem Seitenhintergrund, auf dem der
   a24-Validator alle vier Textfarben des Schemas gegen AA 4,5:1 prueft —
   deshalb nur --text-primary/--text-secondary, nie eine eigene Farbe. Der
   Abstand zwischen den Eintraegen kommt aus dem gap von .links-list. */
.profile-block-heading {
  /* Oben mehr Luft als unten (gap 14px + 16px vs. 14px): die Ueberschrift
     gehoert zum Abschnitt darunter. */
  margin: var(--space-4) 0 0;
  font-size: var(--type-2xl);
  line-height: 1.25;
  font-weight: 700;
  color: var(--text-primary);
  text-align: center;
  overflow-wrap: anywhere;
}
.profile-block-text {
  /* Breite wie .profile-bio, als Block zentriert. */
  max-width: 340px;
  margin: 0 auto;
  font-size: var(--type-md);
  line-height: 1.55;
  color: var(--text-secondary);
  text-align: center;
  white-space: pre-line;
  overflow-wrap: anywhere;
}
/* Ueber 120 Zeichen oder mit Umbruch (profile.ejs): zentrierter Fliesstext
   liest sich schlecht. */
.profile-block-text--long { text-align: left; }
.profile-social {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  /* 8px zwischen den 44px-Zielen (Mindestabstand Touch). */
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
/* 44px: Mindest-Trefferflaeche fuer Touch. Fokusring kommt produktweit aus
   tokens.css. */
.profile-social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  color: var(--text-primary);
  transition: transform var(--transition-normal), color var(--transition-normal);
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.profile-social-link:active { transform: scale(0.94); }
.profile-social-link .icon { width: 24px; height: 24px; }
/* Mit Wort (showLabel, LinkedIn): breiter statt Kreis, ohne Rahmen wie die
   anderen Icons; Hoehe bleibt 44px. */
.profile-social-link--label {
  width: auto;
  gap: var(--space-2);
  padding: 0 var(--space-2);
  border-radius: var(--radius-pill);
  font-size: var(--type-md);
  font-weight: 600;
}
.profile-block-divider {
  width: 100%;
  margin: var(--space-2) 0;
  border: 0;
  border-top: 1px solid currentColor;
  color: var(--text-muted);
  opacity: 0.4;
}
@media (hover: hover) and (pointer: fine) {
  .profile-social-link:hover { color: var(--profile-accent-line, var(--accent)); }
}

.profile-footer {
  margin-top: auto;
  padding-top: var(--space-8);
  font-size: var(--type-sm);
  color: var(--text-muted);
  display: flex;
  flex-direction: column;
  gap: var(--space-3-5);
  align-items: center;
  width: 100%;
}

/* a75 (UI-K14): Zeilen statt Flex, damit text-wrap: balance greift — vorher
   stand bei 1280 px „Verträge hier kündigen“ allein in Zeile 2 (3 + 1), jetzt
   2 + 2; gilt für jede Anzahl Links. Ohne balance (Firefox ESR 115) bricht es
   um wie zuvor. a37: ohne Trennpunkte — Abstand nur über den Außenrand. */
.profile-legal-links {
  text-align: center;
  text-wrap: balance;
}

/* Trefferhoehe der DSGVO-/DDG-Pflichtlinks war 20,8px (0.8125rem ohne
   Polsterung) — Befund Mobile-H7, Design-Review 2026-09-20. */
.profile-legal-links a {
  color: var(--text-muted);
  text-decoration: none;
  transition: color var(--transition-fast);
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--space-1);
  margin: var(--space-1) var(--space-1-5);
}

/* a82/a132: Label „Linknetz:“ steht vorn in derselben Zeile wie unsere Links;
   mit dem ersten Link zusammen unteilbar, damit es nie allein am Zeilenende steht. */
.profile-legal-first {
  white-space: nowrap;
}
.profile-legal-label {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin: var(--space-1) 0 var(--space-1) var(--space-1-5);
  font-size: var(--type-xs);
  font-weight: 600;
}

/* Abstand zwischen den Links des Inhabers und unserer Gruppe größer als
   innerhalb einer Gruppe — sonst trennt das Label nicht sichtbar. */
.profile-legal-links + .profile-legal-platform {
  margin-top: var(--space-4);
}

/* N21: Hover nur fuer echte Zeigegeraete, s. .link-button:hover. */
@media (hover: hover) and (pointer: fine) {
  .profile-legal-links a:hover {
    color: var(--text-primary);
    text-decoration: underline;
  }
}

/* a91 (Rechtsrecherche a13): der Kündigungslink (§ 312k BGB) hebt sich von den
   gedämpften Nachbarn ab — Textfarbe, Schnitt 600 und Unterstrich. --text-primary ist eine
   der Palettenfarben, die textReadable (profile-design.js) auf jedem erlaubten
   Hintergrund mit 4,5:1 prüft; auf dem Foto steht der Fuß auf der Platte. */
.profile-legal-links .profile-legal-cancel {
  color: var(--text-primary);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
}
/* Ruhezustand gleicht schon dem Hover der Nachbarn — Rückmeldung über die Linie. */
@media (hover: hover) and (pointer: fine) {
  .profile-legal-links .profile-legal-cancel:hover {
    text-decoration-thickness: 2px;
  }
}

/* Trefferhoehe war 34,5px (padding 6px 12px bei 0.8rem) — Badge ist der
   einzige Wachstumskanal der Plattform, fast nur mobil gesehen.
   Befund Mobile-H7, Design-Review 2026-09-20. */
.created-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1-5);
  padding: var(--space-2-5) var(--space-4);
  min-height: 44px;
  border-radius: var(--radius-pill);
  background: var(--bg-hover);
  /* --border-strong: --border-subtle #e6e4de ist auf --bg-base #e9e7e0
     heller als die Seite, --border-medium kaeme nur auf 1,23:1. */
  border: 1px solid var(--border-strong);
  color: var(--text-secondary);
  text-decoration: none;
  font-size: var(--type-sm);
  font-weight: 600;
  transition: background var(--transition-fast), color var(--transition-fast);
}

/* N21: Hover nur fuer echte Zeigegeraete, s. .link-button:hover. Textfarbe
   var(--text-primary) statt --accent-cyan-glow (a24-Fix A5): der Akzent
   erreicht auf --bg-hover/--bg-active nicht immer AA. Die Marke daneben
   (.created-badge-mark) ist kein Text und behaelt ihre Linienfarbe. */
@media (hover: hover) and (pointer: fine) {
  .created-badge:hover {
    background: var(--bg-active);
    color: var(--text-primary);
  }
}

/* Die Wortmarke ist bewusst KEIN .icon: sie traegt ihre Strichstaerke 2.5
   wie im Favicon und in den Mockups. Marke, nicht Bedienelement — dieselbe
   Ausnahme wie in app-sidebar.ejs (siehe Kommentar am Icon-System in
   tokens.css). 12px wie im Mockup; im Bestand standen 14px inline. */
.created-badge-mark {
  width: 12px;
  height: 12px;
  flex: none;
  color: var(--accent-cyan-glow);
}

/* ==========================================================================
   Impressum eines Nutzers, /<slug>/impressum (P16). Gleiche Huelle wie die
   Profilseite: dunkler Kopf, darunter eine helle Karte wie die Linkzeilen.
   ========================================================================== */

.profile-head-compact {
  padding: var(--space-4) var(--space-6);
  align-items: flex-start;
}

@media (max-width: 512px) {
  .profile-head-compact {
    padding-bottom: var(--space-4);
  }
}

.imprint-back {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  color: var(--text-secondary);
  text-decoration: none;
  font-size: var(--type-md);
  font-weight: 600;
}

/* N21: Hover nur fuer echte Zeigegeraete, s. .link-button:hover. */
@media (hover: hover) and (pointer: fine) {
  .imprint-back:hover {
    color: var(--text-primary);
    text-decoration: underline;
  }
}

.imprint-card {
  width: 100%;
  margin: var(--space-6) 0 2.5rem;
  padding: var(--space-6);
  background: var(--bg-surface-elevated);
  border: 1px solid var(--border-medium);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

.imprint-title {
  font-size: var(--type-3xl);
  font-weight: 700;
  line-height: 1.2;
  margin: 0 0 var(--space-1) 0;
  letter-spacing: -0.02em;
}

.imprint-lead {
  font-size: var(--type-sm);
  color: var(--text-muted);
  margin: 0 0 var(--space-4) 0;
}

.imprint-address {
  font-style: normal;
  font-size: var(--type-lg);
  line-height: 1.55;
  margin: 0 0 var(--space-4) 0;
}

.imprint-list {
  margin: 0;
  font-size: var(--type-md);
  line-height: 1.55;
}

.imprint-list dt {
  color: var(--text-muted);
  font-size: var(--type-sm);
  margin-top: var(--space-3);
}

.imprint-list dd {
  margin: 0;
}

.imprint-list a {
  color: var(--text-primary);
}

/* Rechtspaket 2026-09-23, Spec Abschnitt 5.4: Verantwortungshinweis unter den Angaben. */
.imprint-source-note {
  margin-top: 1.5rem;
  font-size: var(--type-sm);
  color: var(--text-muted);
}

/* ==========================================================================
   Profil-Baukasten Etappe 1 (a24). Die Klassen und Variablen setzt
   public/js/profile-design.js (renderDesign) am <body>; die Vorschau in den
   Einstellungen setzt dieselben per applyDesign. Groessen nur ueber Tokens.
   ========================================================================== */

/* Klasse + Element schlaegt .surface-light/.surface-dark (tokens.css), die
   am selben <body> var(--bg-base) setzen. Zwei Variablen statt einer
   (a24-Fix A6): background-image traegt bei einem Verlauf den Uebergang,
   background-color bei einer Einzelfarbe die Flaeche UND bei einem Verlauf
   dessen untere Farbe — so bleibt unter dem Bild (z. B. bei nicht ganz
   deckendem Verlaufsende) keine harte Kante zu var(--bg-base) stehen. */
body.profile-page {
  background-color: var(--profile-bg-color, var(--bg-base));
  background-image: var(--profile-bg-image, none);
  background-repeat: no-repeat;
}

.profile-font-lora {
  --font-main: 'Lora', Georgia, 'Times New Roman', serif;
}

.profile-font-nunito {
  --font-main: 'Nunito', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

.profile-font-space-grotesk {
  --font-main: 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

/* „Eckig" heisst wirklich eckig: 0, keine Rundung. design-scale.test.js
   erlaubt ein literales 0 ausdruecklich (a24-Fix A7 — vorher stand hier
   --radius-xs/8px, das sich auf einem 44px-Knopf kaum von "rounded"
   unterscheidet). */
.profile-shape-square .link-button {
  border-radius: 0;
}

.profile-shape-pill .link-button {
  border-radius: var(--radius-pill);
}

/* Text auf der Akzentflaeche: --profile-on-accent ist Schwarz oder Weiss,
   was mehr Kontrast hat (mindestens 4,58:1). */
.profile-fill-filled .link-button {
  background: var(--accent);
  border-color: var(--profile-accent-line);
  color: var(--profile-on-accent);
}

.profile-fill-filled .profile-link-chevron {
  color: inherit;
}

.profile-fill-filled .link-button:active {
  background: var(--accent);
}

/* Umriss: Text steht direkt auf dem Seitenhintergrund (dort geprueft), die
   Linie ist der Akzent, wenn er 3:1 schafft, sonst die Textfarbe. */
.profile-fill-outline .link-button {
  background: transparent;
  border: 2px solid var(--profile-accent-line);
}

.profile-fill-outline .link-button:active {
  background: var(--bg-hover);
}

@media (hover: hover) and (pointer: fine) {
  .profile-fill-filled .link-button:hover .profile-link-chevron {
    color: inherit;
  }
}

/* a26: Links mit Vorschaubild und Beschreibung (nur mit Pro im Markup). Das
   Bild ist 40px und macht den Knopf mit Beschreibung etwas hoeher (Titel plus
   Unterzeile); die Beschreibung bleibt eine Zeile. */
.link-button--thumb {
  padding-left: var(--space-3);
}
.link-thumb {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  object-fit: cover;
  border-radius: var(--radius-xs);
}
.link-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.link-desc {
  overflow: hidden;
  font-size: var(--type-md);
  font-weight: 400;
  color: var(--text-secondary);
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* Auf der Akzentflaeche gilt --profile-on-accent auch fuer die Unterzeile. */
.profile-fill-filled .link-desc {
  color: inherit;
}

/* a26: Bild-Baustein. Hoechstens Spaltenbreite und 70 % der Bildschirmhoehe
   (ein Hochformat bis 1:2 fuellte sonst das ganze Telefon), Seitenverhaeltnis
   aus width/height — verkleinert wird proportional, nichts beschnitten. */
.profile-block-image {
  margin: 0;
}
.profile-block-image-img {
  display: block;
  width: auto;
  max-width: 100%;
  height: auto;
  max-height: 70vh;
  margin-inline: auto;
  object-fit: contain;
  border-radius: var(--radius-md);
}
.profile-block-image-link {
  display: block;
  border-radius: var(--radius-md);
}
.profile-shape-square .profile-block-image-img,
.profile-shape-square .profile-block-image-link,
.profile-shape-square .link-thumb {
  border-radius: 0;
}
/* Pille: das Vorschaubild wird rund und sitzt so konzentrisch in der
   Pillenkante statt eckig dagegen. */
.profile-shape-pill .link-thumb {
  border-radius: var(--radius-pill);
}

/* a26: Hintergrundbild. Das Foto liegt fest hinter der Seite (position:
   fixed statt background-attachment: fixed, das iOS nicht kann), darueber ein
   Schleier in der Grundfarbe. Lesbarkeit haengt NICHT am Foto: jeder Text
   steht auf einer blickdichten Platte in --bg-base bzw. auf der Kopfkarte
   (renderDesign schaltet sie ein) oder einem Knopf mit eigener Flaeche — dort
   prueft design-tokens.test.js jede Textfarbe gegen AA. */
/* Feste Hoehe 100lvh statt inset: 0 — sonst waechst die Flaeche in iOS
   Safari beim Ein-/Ausblenden der Adressleiste mit und das Foto springt. */
body.profile-bg-photo::before {
  content: '';
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  height: 100lvh;
  z-index: -1;
  background-image:
    linear-gradient(color-mix(in srgb, var(--bg-base) 55%, transparent), color-mix(in srgb, var(--bg-base) 55%, transparent)),
    var(--profile-photo, none);
  background-position: center;
  background-size: cover;
}
.profile-bg-photo .profile-block-heading,
.profile-bg-photo .profile-block-text,
.profile-bg-photo .profile-social,
.profile-bg-photo .links-empty,
.profile-bg-photo .profile-footer {
  padding: var(--space-3) var(--space-4);
  background: var(--bg-base);
  border-radius: var(--radius-md);
}
.profile-bg-photo .profile-block-text {
  margin: 0 auto;
}
.profile-bg-photo .profile-social {
  align-self: center;
}
.profile-bg-photo.profile-fill-outline .link-button {
  background: var(--bg-base);
}
/* Gedrueckt bleibt sichtbar: der Hover-Ton liegt ueber der blickdichten
   Grundfarbe statt sie zu ersetzen. */
.profile-bg-photo.profile-fill-outline .link-button:active {
  background: linear-gradient(var(--bg-hover), var(--bg-hover)), var(--bg-base);
}
/* Fokusring auf dem Foto: Ring in Fokusfarbe ueber einem Saum in der
   Grundfarbe, damit er auf jedem Bild sichtbar bleibt. */
.profile-bg-photo :where(a, button, [tabindex]):focus-visible {
  box-shadow: 0 0 0 6px var(--bg-base);
}

/* a27/a50: Video/Musik, Zwei-Klick — die Karte sieht vor dem Klick aus wie
   ein Player (Spec 2026-10-05-a50-einbettung-wie-ein-player-design.md).
   Die Karte traegt .surface-dark (profile.ejs): innen gelten die dunklen
   Tokens, deren Textkontraste design-tokens.test.js prueft. Flaeche und Rand
   wie die Kopfkarte (oben, a24-Fix A3/K2): --text-muted-2 bleibt gegen jeden
   erlaubten Hintergrund und gegen die Karte sichtbar. Blickdicht, daher auch
   auf dem Hintergrundfoto ohne Sonderregel lesbar. Die Buehne hat die Groesse
   des Players — Video 16:9 vor und nach dem Klick (nichts springt), Musik
   kompakt und waechst beim Klick auf die Hoehe, die die Plattform angibt
   (Spotify 152/352, SoundCloud 166/450). Der Hinweis steht IN der Karte und
   bleibt nach dem Klick stehen. */
/* Seit a113 folgt die Karte den Tokens der Vorlage (wie die Links), nur die
   Buehne ist .surface-dark. */
.profile-embed {
  margin: 0;
  overflow: hidden;
  background: var(--bg-surface-elevated);
  border: 1px solid var(--border-medium);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  text-align: start;
}
.profile-embed-stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto 1fr auto;
  gap: var(--space-2);
  box-sizing: border-box;
  aspect-ratio: 16 / 9;
  padding: var(--space-3) var(--space-4);
  background: var(--bg-base);
  container-type: inline-size;
}
.profile-embed-platform {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1-5);
  margin: 0;
  font-size: var(--type-sm);
  font-weight: 600;
  color: var(--text-secondary);
}
.profile-embed-title {
  grid-row: 3;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  margin: 0;
  font-size: var(--type-lg);
  font-weight: 600;
  overflow-wrap: anywhere;
}
.profile-embed-play {
  grid-row: 2;
  place-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 56px;
  padding: 0 var(--space-6) 0 var(--space-4-5);
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--text-primary);
  color: var(--bg-base);
  font: inherit;
  white-space: nowrap;
  font-size: var(--type-md);
  font-weight: 600;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--transition-normal), background-color var(--transition-normal);
}
/* a84: Pille mit Symbol (.icon, 18 px aus tokens.css) und sichtbarem „Abspielen“. */
.profile-embed-play[hidden] {
  display: none;
}
/* Wie .link-button: Hover nur fuer echte Zeigegeraete (N21), gedrueckt kleiner. */
@media (hover: hover) and (pointer: fine) {
  .profile-embed-play:hover {
    background: var(--text-secondary);
    transform: scale(1.06);
  }
}
.profile-embed-play:active {
  transform: scale(0.94);
}

/* Schmale Buehne (Telefon): 16:9 ist dort nur rund 160 px hoch — Plattform,
   Knopf und zwei Titelzeilen passen nicht, der Titel bekommt eine Zeile. Den
   ganzen Titel nennt der Knopfname. */
@container (max-width: 399px) {
  .profile-embed-title {
    -webkit-line-clamp: 1;
    line-clamp: 1;
  }
}

/* Musik: eine Zeile — Plattform und Titel links, Knopf rechts. */
.profile-embed:not(.profile-embed--video) .profile-embed-stage {
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto auto;
  align-content: center;
  column-gap: var(--space-4);
  row-gap: var(--space-0-5);
  aspect-ratio: auto;
  height: 80px;
  transition: height var(--transition-normal);
}
.profile-embed:not(.profile-embed--video) .profile-embed-title {
  grid-row: 2;
  -webkit-line-clamp: 1;
  line-clamp: 1;
  font-size: var(--type-md);
}
/* Ohne Titel steht die Plattform mittig (Klasse aus profile.ejs: zaehlt keine
   Geschwister — in der Vorschau fehlt der Knopf; kein :has, Firefox ESR 115). */
.profile-embed:not(.profile-embed--video) .profile-embed-platform--solo {
  grid-row: 1 / span 2;
  align-self: center;
}
.profile-embed:not(.profile-embed--video) .profile-embed-play {
  grid-row: 1 / span 2;
  grid-column: 2;
  min-height: 48px;
  padding: 0 var(--space-4) 0 var(--space-3);
  font-size: var(--type-sm);
}
/* Schmale Musik-Karte: die Pille liess dem Titel bei 320 px nur 124 px. Das
   Wort wird nur optisch ausgeblendet (Technik wie .visually-hidden), der Name
   bleibt „Abspielen: …“; uebrig bleibt ein runder 44-px-Knopf. */
@container (max-width: 399px) {
  .profile-embed:not(.profile-embed--video) .profile-embed-play {
    width: 44px;
    min-height: 44px;
    padding: 0;
    justify-content: center;
    position: relative;
    border-radius: var(--radius-pill);
  }
  .profile-shape-square .profile-embed:not(.profile-embed--video) .profile-embed-play {
    border-radius: 0;
  }
  .profile-embed:not(.profile-embed--video) .profile-embed-play-text {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}
.profile-embed--h152.is-playing .profile-embed-stage { height: 152px; }
.profile-embed--h166.is-playing .profile-embed-stage { height: 166px; }
.profile-embed--h352.is-playing .profile-embed-stage { height: 352px; }
.profile-embed--h450.is-playing .profile-embed-stage { height: 450px; }
@media (prefers-reduced-motion: reduce) {
  .profile-embed-stage,
  .profile-embed-play {
    transition: none;
  }
}
.profile-embed.is-playing .profile-embed-stage {
  display: block;
  padding: 0;
}
.profile-embed-frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}
/* Unten Abstand: overflow: hidden an der Karte schnitte sonst den Fokusring
   der Textlinks (2px + 2px Versatz) ab. */
.profile-embed-note {
  padding: var(--space-2) var(--space-4);
  font-size: var(--type-xs);
  color: var(--text-secondary);
}
.profile-embed-note p {
  margin: 0;
}
.profile-embed-note .profile-embed-links {
  display: flex;
  flex-wrap: wrap;
  column-gap: var(--space-4);
  margin-inline: calc(-1 * var(--space-1));
}
/* Tippflaeche 44 px fuer beide Textlinks. */
.profile-embed-links a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 44px;
  padding-inline: var(--space-1);
  color: var(--text-primary);
  font-weight: 600;
}
/* a113 (UI-W2): Karte und Abspielen-Knopf folgen der Form der Vorlage wie die
   Links. Die Buehne bleibt dunkel (Player), deshalb behaelt der Knopf seine
   helle Fuellung — eine Akzentfarbe erreichte dort nicht bei jeder Vorlage 3:1. */
.profile-shape-square .profile-embed,
.profile-shape-square .profile-embed-play {
  border-radius: 0;
}
.profile-shape-pill .profile-embed {
  border-radius: var(--radius-lg);
}
.profile-shape-pill .profile-embed-play {
  border-radius: var(--radius-pill);
}

/* a92: hervorgehobener Link (Spec 2026-10-24-a92-links-hervorheben-design.md).
   Akzentflaeche mit --profile-on-accent (renderDesign: mind. 4,58:1) und ein
   abgesetzter Ring in --profile-accent-line (renderDesign prueft ihn mit 3:1
   gegen jeden Hintergrundpunkt, bei Foto gegen --bg-base) — der Ring traegt
   die Hervorhebung auch bei Fuellung „gefuellt“, wo alle Knoepfe schon die
   Akzentfarbe haben. outline statt box-shadow: der Abstand bleibt
   durchsichtig, auch ueber einem Verlauf. .profile-page davor, damit die
   Regel die Fuellungs- und Foto-Regeln oben schlaegt. */
.profile-page .link-button.link-button--highlight,
.profile-page .link-button.link-button--highlight:active {
  background: var(--accent);
  border-color: var(--profile-accent-line);
  color: var(--profile-on-accent);
  outline: 2px solid var(--profile-accent-line);
  outline-offset: 4px;
}
.profile-page .link-button--highlight .profile-link-chevron,
.profile-page .link-button--highlight .link-desc {
  color: inherit;
}
/* Fokus: Ring und Abstand bleiben (sonst rueckte er an den Nachbarn), dazu
   ein Innenring in --profile-on-accent — gegen die Akzentflaeche mind.
   4,58:1, also deutlich anders als Ruhe (WCAG 2.4.13). */
.profile-page .link-button.link-button--highlight:focus-visible {
  box-shadow: inset 0 0 0 3px var(--profile-on-accent);
}
/* Auf dem Foto liegt der Ring auf einer Platte in der Grundfarbe — 8px:
   Ring reicht 6px hinaus, der Abstand zum Nachbarn ist 14px, dessen
   Ring/Fokusring bleibt frei. */
.profile-page.profile-bg-photo .link-button.link-button--highlight {
  box-shadow: 0 0 0 8px var(--bg-base);
}
.profile-page.profile-bg-photo .link-button.link-button--highlight:focus-visible {
  box-shadow: inset 0 0 0 3px var(--profile-on-accent), 0 0 0 8px var(--bg-base);
}
/* Zwei hervorgehobene direkt untereinander (Pro): ohne Zusatzabstand standen
   die Ringe bei 14px Lücke nur 2px auseinander und lasen sich als Doppellinie. */
.profile-page .link-button--highlight + .link-button--highlight {
  margin-top: var(--space-2);
}

/* a98: Buchungsknopf — ganz oben, groesser als die Links darunter; Farben,
   Ring und Fokus aus dem a92-Block (dort geprueft). Der Abstand nach unten
   haelt den Ring (6px) deutlich von der Liste getrennt. */
.profile-page .link-button--booking {
  min-height: 64px;
  padding-block: var(--space-4);
  font-size: var(--type-xl);
  margin-bottom: var(--space-4);
}
.profile-booking-icon {
  flex-shrink: 0;
}
.profile-page .link-button--booking .link-title {
  line-height: 1.25;
  overflow-wrap: anywhere; /* 17px: ein langes Wort lief bei 320px sonst ueber den Pfeil */
}
/* Der Buchungsknopf ist selbst --highlight: die a92-Zusatzluft zum
   naechsten hervorgehobenen entfaellt, margin-bottom traegt sie schon. */
.profile-page .link-button--booking + .link-button--highlight {
  margin-top: 0;
}
