/* sandrodegen.swiss — quiet, typographically led, warm neutral.
   Palette derived from the portrait backdrop #8C837E. */

:root {
  color-scheme: light;

  --paper:     #F7F5F2;
  --paper-alt: #EFEBE6;
  --ink:       #24211F;
  /* On --paper-alt this is 4.74:1 — AA with almost no headroom.
     Do not lighten it; anything paler fails the hero role line. */
  --ink-soft:  #6E6660;
  --rule:      #DFD8D1;
  --backdrop:  #8C837E;

  --serif: ui-serif, "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* Type ramp — one constant ratio of 1.2 (6:5), anchored at --text-base.
     390px is the reference viewport: that is where each fluid step evaluates
     to its own rung. The ratio is not a convention, it is the largest one this
     page survives — at 1.25 the claim reaches 25px at 390px and breaks
     "Technologieentscheidungen vor." onto a third line, the defect S2-11
     removed. Never insert a value between two steps: a rung that exists in one
     place only is how the previous nine sizes came about. */
  --text-meta:    0.8333rem;  /* 13.33px — labels, dates, secondary lines */
  --text-base:    1rem;       /* 16.00px — running text */
  --text-heading: 1.2rem;     /* 19.20px — entry headings */

  /* Each fluid step's vw coefficient is derived from the rung it must hit at
     390px (23.04/3.9 and 33.18/3.9), so the fluid term is actually engaged
     there instead of resting on a bound. The bounds are rungs of the same
     ladder (1.2^3 and 1.2^6), and --text-lead's ceiling is --text-display's
     floor, so the two can never cross. --text-display sits two rungs above
     --text-lead, not one: a single step would put the name within 4px of the
     claim and read as a mistake rather than a hierarchy. */
  --text-lead:    clamp(1.125rem, 5.908vw, 1.728rem);  /* 18.00 · 23.04 · 27.65 */
  --text-display: clamp(1.728rem, 8.507vw, 2.986rem);  /* 27.65 · 33.18 · 47.78 */

  --measure: 34rem;
  --page: 62rem;
  --gutter: 1.5rem;
}

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--text-base);
  line-height: 1.6;
  overflow-wrap: anywhere;
}

h1, h2, h3 { font-weight: 600; margin: 0; }
p, ul { margin: 0; }
ul { padding: 0; list-style: none; }

/* Emphasis budget: one verifiable number that evidences order of magnitude
   or tempo, four page-wide, hero excluded — it already carries its own
   typographic levels (S2-11). Weight only, nothing else: body text is sans
   at 400, so 600 is the contrast. */
strong { font-weight: 600; }

a { color: inherit; }

/* Must outrank the display declarations below, or the JS-only mail row
   stays visible as an empty label when JavaScript does not run. */
[hidden] { display: none !important; }

.link {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--backdrop);
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
}

.link:hover,
.link:focus-visible { text-decoration-color: var(--ink); }

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

/* ---------- Hero ---------- */

.hero {
  background: var(--paper-alt);
  border-bottom: 1px solid var(--rule);
  padding: 1.5rem var(--gutter);
}

.hero__inner {
  max-width: var(--page);
  margin: 0 auto;
}

.hero__figure { display: block; }

.hero__portrait {
  display: block;
  width: min(9rem, 38vw);
  height: auto;
  background: var(--backdrop);
}

.hero__text { margin-top: 1.375rem; }

/* A gap here, not space-between: below the 56rem breakpoint the hero is one
   column and the eyebrow runs the full container, so space-between pushed the
   switcher to a container edge that no other hero line reaches — at 834px
   "BASEL" and "ENGLISH" stood 771px apart with nothing between or beneath them.
   Keeping the pair together at the left anchors it to the same edge as the
   name, claim, proof and role. Constraining the row to --measure instead was
   tried and rejected: the role's text stops at 525px inside its 544px --measure
   box, so the switcher aligned to a box edge nothing visible reaches.
   space-between returns at 56rem, where the eyebrow sits in a grid column that
   does have a real right edge. --gutter is the gap .hero__actions already uses
   between its two links. */
.hero__eyebrow {
  display: flex;
  align-items: center;
  gap: var(--gutter);
  font-size: var(--text-meta);
  line-height: 1;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.5rem;
}

/* Language switcher. Same tap-target mechanism as .hero__actions .link — an
   inline-block whose line box plus vertical padding clears 44px — but it stays
   on the eyebrow's own --text-meta rung rather than taking the base rung the
   way .hero__actions does: the switcher sits beside "Basel" and a size of its
   own would read as a second level in a row that has only one. The padding
   carries the target instead. The eyebrow's line-height:1 leaves a 13.33px line
   box, so 1rem either side measures 45.3px. The eyebrow row is allowed to grow
   to that height; pulling it back with negative margins would put the hit area
   over the name's glyphs. */
.hero__lang .link {
  display: inline-block;
  padding: 1rem 0;
}

.hero__name {
  font-family: var(--serif);
  font-size: var(--text-display);
  line-height: 1.08;
  letter-spacing: -0.015em;
}

/* --- Hero claim and proof (S2-11, sized by the ramp in S2-08) ---
   The local literals that stood here until the ramp existed are gone. The
   claim takes --text-lead and the proof --text-base, so the hero adds no rung
   of its own: the two levels are told apart by family and colour, not by a
   size that exists nowhere else on the page. */

/* Hyphenation stays off across the whole hero: the previous .hero__lead used
   hyphens:auto and produced two-letter remnants (S2-10). overflow-wrap:normal
   overrides the body-level `anywhere` so no German compound is ever broken
   mid-word without a hyphen; the claim is sized to fit "Technologie-
   entscheidungen" on one line down to a 320px viewport. */
.hero__claim,
.hero__proof {
  hyphens: manual;
  overflow-wrap: normal;
}

/* "Länder-CTOs" carries a real hyphen, and a real hyphen is a line-break
   opportunity: at 390px the proof broke it into "Länder-" / "CTOs" and left a
   four-character line. Held together here rather than with a non-breaking
   hyphen character, which would risk a missing glyph on non-Apple systems. */
.hero__nobr { white-space: nowrap; }

.hero__claim {
  font-family: var(--serif);
  /* --text-lead carries the 1.125rem floor that keeps "Technologie-
     entscheidungen vor." on one line at 320px, and a ceiling that keeps it on
     one line in the narrow 896–930px column too. Both are stated once, in
     :root, so this rule needs no size of its own. Verified by render. */
  font-size: var(--text-lead);
  line-height: 1.25;
  letter-spacing: -0.01em;
  margin-top: 0.875rem;
  text-wrap: balance;
}

/* Separated from the claim by colour and family, not by a near-identical
   size, so it does not add a rung to the ramp. */
.hero__proof {
  font-size: var(--text-base);
  line-height: 1.55;
  margin-top: 0.75rem;
  text-wrap: pretty;
}

.hero__role {
  font-size: var(--text-meta);
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: var(--measure);
  margin-top: 1rem;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  margin-top: 1.375rem;
  /* --text-base, not --text-meta, and not for typographic reasons: the tap
     target here is line box plus 0.6875rem of padding either side. At the meta
     rung that comes to ~43px and drops under the 44px minimum; at the base
     rung it measures 47.6px. */
  font-size: var(--text-base);
}

.hero__actions .link {
  display: inline-block;
  padding: 0.6875rem 0;
}

@media (min-width: 56rem) {
  .hero { padding: 5rem var(--gutter) 4.5rem; }

  .hero__eyebrow { justify-content: space-between; }

  .hero__inner {
    display: grid;
    grid-template-columns: 22rem 1fr;
    gap: 4rem;
    align-items: center;
  }

  .hero__portrait { width: 22rem; }
  .hero__text { margin-top: 0; }
  /* 28ch, not the 20ch the old four-line lead used: at 20ch the claim breaks
     after "Technologieentscheidungen" and orphans "vor." onto a third line.
     No size override any more. Entering this breakpoint makes the text column
     *narrower* (432px at a 896px viewport, because the 22rem portrait and the
     4rem gap come out of it), which used to orphan "vor." in the 896–930px
     band because the old clamp was already at its 2rem ceiling. --text-lead
     tops out at 1.728rem instead, which clears the narrowest column on its
     own — so the ramp replaces the override rather than fighting it. */
  .hero__claim {
    max-width: 28ch;
    margin-top: 1.25rem;
  }

  /* The text column runs to 36rem here, far wider than the mobile container,
     so the proof needs a measure of its own. Mobile sets none: there the
     container is already the measure. */
  .hero__proof { margin-top: 1rem; max-width: 38ch; }

  .hero__role { margin-top: 1.75rem; }
  .hero__actions { margin-top: 2rem; }
}

/* The padding on these two carries their tap target, so it may only be dropped
   where a tap target is not what is being served. Width alone does not say
   that: an iPad in landscape is 1024–1366px and touch, and dropping the padding
   there leaves the switcher a 13.33px hit area. pointer:fine is the condition
   the padding was actually guarding against — a mouse — so it is the one that
   gates the reset. */
@media (min-width: 56rem) and (pointer: fine) {
  .hero__actions .link,
  .hero__lang .link { padding: 0; }
}

/* ---------- Sections ---------- */

.section {
  max-width: var(--page);
  margin: 0 auto;
  padding: 3.25rem var(--gutter);
  border-bottom: 1px solid var(--rule);
}

.section--contact { border-bottom: 0; }

/* This label used to be smaller *and* paler than the .entry__role it heads —
   0.9375rem in --ink-soft over a 1.1875rem --ink heading. It stays nominally
   smaller than the role (16px against 19.2px) because the 12rem desktop label
   column caps it: "Schwerpunkte" measures 177px at --text-base and would need
   ~211px at --text-heading, wrapping to two lines. The rank is restored
   optically and by contrast instead. All-caps in this serif stands at a cap
   height of ~0.70em = 11.2px, while the role's mixed-case word shapes are
   carried by lowercase at ~0.45em = 8.6px, so the label's letterforms are the
   taller ones; 0.16em of tracking adds the width. --ink rather than --ink-soft
   takes it from 5.17:1 to 14.70:1 on --paper, level with the role. */
.section__title {
  font-family: var(--serif);
  font-size: var(--text-base);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: 1.75rem;
}

@media (min-width: 56rem) {
  .section {
    display: grid;
    grid-template-columns: 12rem 1fr;
    gap: 4rem;
    padding: 4.5rem var(--gutter);
  }

  .section__title { margin-bottom: 0; padding-top: 0.4rem; }
}

/* ---------- Prose ---------- */

.prose {
  max-width: var(--measure);
  font-size: var(--text-base);
  line-height: 1.7;
}

/* Collapsing the old 1.0625rem onto the base rung rewraps the Profil text and
   left "Länder." alone on a 54px last line. Same remedy as .hero__proof, but
   note it only softens: measured, the last line goes 54px -> 90px ("acht
   Länder."), still shorter than the 234px the old 1.0625rem happened to give.
   pretty balances the last few lines, it does not guarantee a full one. */
.prose p { text-wrap: pretty; }

.prose p + p { margin-top: 1.125rem; }

/* ---------- Entries (Werdegang, Engagement) ---------- */

.entries > .entry + .entry {
  margin-top: 2.5rem;
  padding-top: 2.5rem;
  border-top: 1px solid var(--rule);
}

/* Two sections put a .prose lead above a list: Publikationen above .entries and
   Kontakt above .rows. Neither list class carries a margin on that edge, so the
   lead's last line sat directly on the first row. 1.75rem is the gap
   .section__title already uses below itself — no new spacing value. */
.prose + .entries,
.prose + .rows { margin-top: 1.75rem; }

.entry__role {
  font-family: var(--serif);
  font-size: var(--text-heading);
  line-height: 1.3;
}

.entry__period {
  font-size: var(--text-meta);
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  margin-top: 0.25rem;
}

/* text-wrap: pretty for the same reason as .prose p — dropping 0.9375rem onto
   the meta rung left "Steering" alone on a 52px last line. Measured effect is
   again partial: 52px -> 65px ("& Steering"), two lines either way. The one
   that clearly benefits is the KPMG row, 60px -> 84px. */
.entry__org {
  font-size: var(--text-meta);
  color: var(--ink-soft);
  margin-top: 0.375rem;
  text-wrap: pretty;
}

.entry__text {
  max-width: var(--measure);
  margin-top: 0.875rem;
  line-height: 1.65;
}

.points {
  max-width: var(--measure);
  margin-top: 1rem;
}

.points > li {
  position: relative;
  padding-left: 1.125rem;
  line-height: 1.6;
}

.points > li + li { margin-top: 0.5rem; }

.points > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.7em;
  width: 0.375rem;
  height: 1px;
  background: var(--backdrop);
}

.points--plain > li { padding-left: 0; }
.points--plain > li::before { content: none; }

/* ---------- Key / value rows (Ausbildung, Schwerpunkte, Kontakt) ---------- */

/* :not([hidden]) so the JS-only mail row leaves no orphan rule behind it. */
.rows > .row:not([hidden]) + .row {
  margin-top: 1.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
}

.row {
  display: grid;
  gap: 0.25rem;
}

.row__key { font-weight: 600; }

.row__val {
  color: var(--ink-soft);
  max-width: var(--measure);
}

@media (min-width: 40rem) {
  .row {
    grid-template-columns: 18rem 1fr;
    gap: 2rem;
    align-items: baseline;
  }
}

.rows--contact .row__val { color: var(--ink); }

/* ---------- Footer ---------- */

.footer {
  max-width: var(--page);
  margin: 0 auto;
  padding: 2.5rem var(--gutter) 3.5rem;
  border-top: 1px solid var(--rule);
  font-size: var(--text-meta);
  color: var(--ink-soft);
}

.footer__owner { color: var(--ink); }

.footer__note {
  max-width: var(--measure);
  margin-top: 0.5rem;
}

/* ---------- Print ---------- */

@media print {
  :root { --paper: #fff; --paper-alt: #fff; --ink: #000; --ink-soft: #333; --rule: #bbb; }

  html { font-size: 10.5pt; }
  body { line-height: 1.45; }

  .hero {
    border-bottom: 1px solid var(--rule);
    padding: 0 0 1.25rem;
  }

  .hero__portrait { width: 5rem; }
  .hero__actions,
  .hero__lang { display: none; }

  .section,
  .footer { padding-left: 0; padding-right: 0; }

  .section { padding-top: 1.25rem; padding-bottom: 1.25rem; }

  .entry,
  .row { break-inside: avoid; }

  .section { break-inside: auto; }

  .link { text-decoration: none; }

  /* On paper a link is just text, so the publication entries lost their only
     route back to the source. Expanding from attr(href) keeps that automatic —
     the hand-written expansion that stood here pointed at a footer link that
     does not exist. No word-breaking is stated: the longest of these is the
     77-character FHNW publication URL, one unbreakable token, and the
     overflow-wrap:anywhere set on body inherits into this pseudo-element, which
     wraps it inside the text column on its own. Measured in print emulation at
     794px: the expanded link ends at 454px inside a 476px column. */
  .entry__text .link::after { content: " (" attr(href) ")"; }
}
