/*
 * The public part — the brochure.
 *
 * This is the company's own website rebuilt on this project's tokens:
 * `docs/referenzen/steinmann-website.md` is the survey it follows, and every
 * component below appears there under the same name. Where a value here has
 * no obvious reason, the reason is that the site does it that way.
 *
 * The working area shares the colours, the families and the shapes and
 * differs in density — `app.css` runs the sans one weight heavier and one
 * step smaller, because a table of forty rows is read at a different distance
 * from a paragraph of prose.
 *
 * Every value is a token from `tokens.css`. There is no literal colour here,
 * gradients included; `tests/test_design_system.py` reads this file and
 * reports one.
 */

/* The public part is read in sentences and the working area in glances, and
 * that is the whole of the "two densities" this design was accepted on. Here
 * the body is the site's own 1.0375 rem at weight 300, leading 1.75; behind
 * the login it is 15 px at weight 400, leading 1.5.
 *
 * On `.site` rather than on `body`, because `body` is shared: both shells
 * hang off it, and a value set there would drag the working area along.
 *
 * Measured before this: every paragraph of every public page was 15 px at
 * 400 — the application's own figure — because `body` in `custom.css` is the
 * only rule that had ever set one. The difference is a third more air per
 * line, and it is what the reference reads like. */
.site {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-size: var(--text-md);
  font-weight: var(--font-weight-light);
  line-height: var(--text-md-line);
  background-color: var(--color-ground);
}

/* ------------------------------------------------------------------ *
 * The display sizes of the public part.
 *
 * These are the site's own clamps, and they are what the page reads like
 * from across a desk. `custom.css` sets every heading at a fixed step from
 * the text scale, which is right in the working area and half the size here:
 * measured at 1440 px, an `h1` was 36 px against the reference's 74 and an
 * `h2` 24 against 50. The tokens for it existed from the start and nothing
 * used them.
 *
 * On `.site` so that the working area keeps its own, and after the shell
 * rules so that a page hero can still override the top of the scale.
 * ------------------------------------------------------------------ */

/* Every display heading may hyphenate. The clamps are in `rem`, so they
 * follow the reader's font setting — and at 150 per cent the *minimum* of
 * `--display-hero` is 64.8 px, at which "Aussergewöhnliche" is 482 px wide
 * in a 320 px view. The page has `lang="de-ch"`, so the browser breaks it
 * correctly at "Ausser-gewöhnliche" rather than anywhere.
 *
 * `overflow-wrap` is the fallback for the engine that hyphenates nothing: a
 * broken word is ugly, a page that scrolls sideways is unusable. */
.site h1,
.site h2,
.site h3 {
  hyphens: auto;
  /* Only a long word, and never a stub on either side: without the limit a
   * heading broke as "ver-schieben" and "Zuständigkei-ten" on the desktop,
   * where the word had room on the next line. */
  hyphenate-limit-chars: 10 4 4;
  overflow-wrap: break-word;
}

/* `:where()` rather than `.site`, and the zero specificity it carries is the
 * whole point. `.site h2` is one class and one element, which outweighs a
 * bare `.panel__title` -- so every panel heading on a public page was set at
 * `--display-md`, 50 px where the component asks for 20. It showed as soon as
 * a panel stood in a column 320 px wide: "IHRE LIEGENSCHAFT" broke over four
 * lines in the rail of an article, and "Breitenmoser Partner AG" was the
 * largest thing on the contact page.
 *
 * At zero specificity these three are the page's default size for a heading
 * that says nothing else about itself, and any component that names its own
 * size wins without having to fight for it. */
:where(.site) h1 {
  font-size: var(--display-lg);
}

:where(.site) h2 {
  font-size: var(--display-md);
}

:where(.site) h3 {
  font-size: var(--display-sm);
}

/* A label is not prose and is never hyphenated: upper case at this tracking
 * broke as "VER-FASST VON" the moment the rail was narrow. */
.toc__title,
.aside-card__title,
.section-label,
.eyebrow {
  hyphens: none;
}

/* A hero heading is the one step above the rest, and it is set lighter: 300
 * rather than 400, which is what stops five rem of serif reading as a shout. */
.hero h1 {
  font-size: var(--display-hero);
  font-weight: var(--font-weight-light);
}

.page-hero h1 {
  font-size: var(--display-lg);
  font-weight: var(--font-weight-light);
}

/* What is interface rather than prose keeps the interface weight: 400 rather
 * than the 300 of the running text around it. A field or a table cell set at
 * 300 reads as a sentence somebody wrote rather than as a value.
 *
 * `.btn` is deliberately **not** in this list, and it was, and that was a
 * defect: a button in this design is 500, which is the heaviest weight the
 * family owns and the whole of how a button says it is one. `public.css`
 * loads after `custom.css`, so this rule quietly won over `--bs-btn-font-
 * weight` and every button on the public part ran at 400 — measured against
 * the reference, which has 500. Nothing else finds that; it looks like a
 * button either way until the two are put side by side. */
.site .form-control,
.site .form-select,
.site .form-label,
.site table {
  font-weight: var(--font-weight-regular);
}

.site-main {
  flex: 1;
}

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

/* The path of a page that has no photograph over it. The header is fixed and
 * 92 px tall, so a container at the very top of the document lies under it:
 * measured at y = 0 on the article and on the search page of the guide, where
 * the path was drawn behind the bar and never seen by anybody. Where a hero
 * follows the path instead, the path goes under the hero -- pushing a hero
 * down by 92 px would take the photograph out from behind the header, which
 * is where the design puts it. */
.crumb-bar {
  padding-top: calc(var(--header-height) + var(--space-6));
}

/* ------------------------------------------------------------------ *
 * The eyebrow — a small capital line in gold with a 44 px rule before it.
 *
 * It appears above nearly every heading on the site and is the single most
 * recognisable thing about the design. `.section-label` is what the templates
 * already call it; `.eyebrow` is the name the survey uses, and both work.
 *
 * The rule is a flex child rather than a border so that it keeps its 44 px
 * whatever the text does, and `flex: none` is what stops it collapsing when
 * the label wraps on a narrow screen.
 * ------------------------------------------------------------------ */

.section-label,
.eyebrow {
  display: flex;
  align-items: center;
  /* The rule and the words go on separate lines rather than off the side
   * when there is no room for both. At 150 per cent type on a 320 px screen
   * "Ausgewählte Objekte" tracked at 0.42em is 259 px wide in a 164 px
   * column, and a flex row that may not wrap takes the page with it. */
  flex-wrap: wrap;
  gap: var(--space-4);
  font-family: var(--font-family-base);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--color-brand-ink);
}

/* The tracking is the signature of this design and it is also what makes the
 * line unaffordable on a narrow screen at a large type size. Below 30 rem it
 * comes down to the label step — still unmistakably spaced, and 21 per cent
 * narrower. */
@media (max-width: 30rem) {
  .section-label,
  .eyebrow {
    letter-spacing: var(--tracking-label);
    overflow-wrap: break-word;
  }
}

.section-label::before,
.eyebrow::before {
  content: "";
  flex: none;
  width: 44px;
  height: var(--border-hairline);
  background-color: var(--color-brand);
}

.section-label--center,
.eyebrow--center {
  justify-content: center;
}

.section-label--center::after,
.eyebrow--center::after {
  content: "";
  flex: none;
  width: 44px;
  height: var(--border-hairline);
  background-color: var(--color-brand);
}

/* On a dark ground the eyebrow is the brand gold itself — it reaches 7.93:1
 * there and needs no correction. */
.section--accent .section-label,
.section--dark .section-label,
.hero .section-label,
.page-hero .section-label,
.on-dark .section-label {
  color: var(--color-brand-soft);
}

/* ------------------------------------------------------------------ *
 * Header.
 *
 * Fixed, and transparent while the page sits at the top of a hero: the
 * photograph runs underneath it and the wordmark stands on the veil. After
 * 40 px of scroll `app.js` adds `is-scrolled` and it turns solid with the
 * gold hairline under it.
 *
 * Below 960 px it is solid from the start, and that is not a shortcut. A
 * transparent bar over a photograph nobody chose is a bar whose legibility
 * depends on the picture, and on a phone the picture fills the frame. The
 * site makes the same exception at the same width.
 * ------------------------------------------------------------------ */

.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  background-image: var(--veil-header);
  transition: background-color var(--motion-slow) var(--motion-ease),
    box-shadow var(--motion-slow) var(--motion-ease);
}

/* A page without a hero has nothing dark under the bar: white navigation
 * on the paper ground, the active item in gold on cream. */
.site:not(:has(.hero, .page-hero)) .site-header,
.site-header.is-scrolled,
.site-header--solid {
  background-color: var(--black);
  background-image: none;
  box-shadow: 0 var(--border-hairline) 0 var(--color-brand-line);
}

.site-header__inner {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--space-gutter);
  min-height: var(--header-height);
  display: flex;
  align-items: center;
  gap: var(--space-6);
}

.site-header__spacer {
  flex: 1;
}

/* A bar that sticks has to be affordable. At the reflow condition of WCAG 2.2
 * 1.4.10 — 320 by 256 CSS pixels, which is 400 % zoom on a small laptop — a
 * fixed 92 px bar takes more than a third of the viewport and, because it is
 * fixed, scrolling does not give the space back.
 *
 * So on a viewport short enough for that to bite, the bar scrolls away with
 * the page instead. Nothing is removed and nothing moves: it is the same bar
 * in the same place, and it comes back the moment there is room for it. The
 * condition asks only about height, which is the closest thing to "share of
 * the viewport" that CSS can express. */
@media (max-height: 480px) {
  .site-header {
    position: static;
    background-color: var(--black);
    background-image: none;
  }
}

/* The word mark. Text rather than an image: it is translatable, selectable,
 * readable aloud and it scales with the reader's font setting. The logo file
 * is `static/img/brand/`, and the mark below is what stands where a page has
 * no room for it. */
.wordmark {
  font-family: var(--font-family-base);
  font-size: var(--text-sm);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-wordmark);
  text-transform: uppercase;
  color: var(--color-on-accent);
  text-decoration: none;
  white-space: nowrap;
}

.wordmark:hover,
.wordmark:focus-visible {
  color: var(--color-brand-soft);
}

/* The word mark is 678 by 128, so its width is 5.3 times its height. Set in
 * `rem` alone it follows the reader's font setting — and at 150 per cent on a
 * 320 px screen that is 318 px of logo in a 320 px bar, which pushed every
 * public page sideways. Measured on all eight of them.
 *
 * It still scales, because a reader who raised the type wants the mark
 * legible too; what it may not do is take the whole bar. Below the cap the
 * height gives way instead, which keeps the mark proportional. */
.wordmark__logo {
  display: block;
  height: 2.5rem;
  width: auto;
  max-width: min(13.25rem, 42vw);
  object-fit: contain;
  object-position: left center;
}

.wordmark__logo--light { display: var(--show-in-light, block); }
.wordmark__logo--dark { display: var(--show-in-dark, none); }

/* Navigation. The underline grows from the left rather than fading in, which
 * costs one pseudo-element and is most of what makes the bar feel made. */
.site-nav ul {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-nav a {
  position: relative;
  display: block;
  padding-block: var(--space-2);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-regular);
  letter-spacing: var(--tracking-nav);
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--color-on-accent);
  text-decoration: none;
}

.site-nav a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 100%;
  bottom: 0;
  height: var(--border-hairline);
  background-color: var(--color-brand);
  transition: right var(--motion-nav) var(--motion-ease);
}

.site-nav a:hover::after,
.site-nav a:focus-visible::after,
.site-nav a[aria-current="page"]::after {
  right: 0;
}

.site-nav a[aria-current="page"] {
  color: var(--color-brand-soft);
}

/* The handle. A link to the panel's own fragment, so that it opens without
 * JavaScript through `:target` and with it from the Alpine component. */
.site-nav__toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: var(--target-primary);
  height: var(--target-primary);
  margin-inline-start: calc(var(--space-2) * -1);
  color: var(--color-on-accent);
  background: none;
  border: none;
}

.site-nav__toggle:hover,
.site-nav__toggle:focus-visible {
  color: var(--color-brand-soft);
}

/* A cross while the panel is open, the three bars otherwise. */
.site-nav__toggle:not([aria-expanded="true"]) .site-nav__icon-close,
.site-nav__toggle[aria-expanded="true"] .site-nav__icon-open {
  display: none;
}

/* ------------------------------------------------------------------ *
 * Hero.
 *
 * A photograph under the three-stop veil, the whole viewport tall. The
 * heading is the serif at weight 300 with one italic gold line in it, which
 * is the site's own construction: `Aussergewöhnliche Immobilien.` in ivory,
 * `Schweizer Vertrauen.` in italic gold underneath.
 *
 * `min-height: 100svh` and not `100vh`: on a phone the browser's own bar
 * counts against `vh` and the scroll hint ends up under it.
 * ------------------------------------------------------------------ */

.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  min-height: 100svh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background-color: var(--black);
}

.hero__bg {
  position: absolute;
  inset: 0;
  z-index: -2;
}

.hero__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.06);
  animation: hero-settle 9s var(--motion-ease) forwards;
}

@keyframes hero-settle {
  to {
    transform: scale(1);
  }
}

.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: var(--veil-hero);
}

.hero__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  align-self: stretch;
  width: 100%;
  max-width: 62rem;
  margin-inline: auto;
  padding: calc(var(--header-height) + var(--space-8)) var(--space-6)
    var(--space-8);
}

.hero__signet {
  height: 4.625rem;
  width: auto;
  margin: auto auto var(--space-8);
}

.hero__mark {
  display: block;
  margin-bottom: var(--space-6);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-eyebrow-wide);
  text-transform: uppercase;
  color: var(--color-brand-soft);
}

.hero h1 {
  margin-bottom: var(--space-6);
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--display-hero);
  font-weight: var(--font-weight-light);
  line-height: 1.08;
  color: var(--color-on-accent);
}

/* The one italic line. It is gold and it is the second half of the sentence,
 * which is why the heading reads as a claim rather than as a label. */
.hero h1 em {
  font-style: italic;
  color: var(--color-brand-soft);
}

.hero__lead {
  max-width: 35rem;
  margin: 0 auto var(--space-12);
  font-size: var(--text-md);
  font-weight: var(--font-weight-light);
  letter-spacing: 0.06em;
  color: var(--color-on-accent-muted);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  justify-content: center;
}

/* Text on a photograph. The veil does most of the work; this is what covers
 * the case where the picture is bright exactly where the heading sits. */
.hero__mark,
.hero h1,
.hero__lead,
.hero__scroll,
.page-hero h1,
.page-hero .section-label,
.page-hero__meta {
  text-shadow: 0 2px 26px var(--scrim-deep), 0 1px 5px var(--scrim);
}

.hero__scroll {
  margin-top: auto;
  padding-top: var(--space-10);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--color-on-accent-muted);
}

.hero__scroll::after {
  content: "";
  width: var(--border-hairline);
  height: 3.25rem;
  background-image: var(--wash-rule);
  animation: scroll-pulse 2.2s ease-in-out infinite;
}

@keyframes scroll-pulse {
  0%,
  100% {
    opacity: 0.35;
  }
  50% {
    opacity: 1;
  }
}

/* A hint to scroll is worth nothing on a screen with no room for it. */
@media (max-height: 700px), (max-width: 700px) {
  .hero__scroll {
    display: none;
  }
}

/* The hero of a subpage: shorter, bottom-aligned, and the photograph does not
 * move. A zoom that runs on every page of a visit stops being an entrance and
 * becomes a tic. */
.page-hero {
  position: relative;
  isolation: isolate;
  min-height: 62vh;
  display: flex;
  align-items: flex-end;
  background-color: var(--black);
}

.page-hero .hero__bg img {
  animation: none;
  transform: none;
}

.page-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: var(--veil-page);
}

.page-hero__inner {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding: calc(var(--header-height) + var(--space-10)) var(--space-gutter)
    var(--space-10);
}

.page-hero h1 {
  margin-top: var(--space-5);
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-weight: var(--font-weight-light);
  color: var(--color-on-accent);
}

.page-hero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-10);
  margin-top: var(--space-5);
  font-size: var(--text-sm);
  letter-spacing: 0.1em;
  color: var(--color-on-accent-muted);
}

.page-hero__price {
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--text-2xl);
  color: var(--color-brand-soft);
}

/* A page with no photograph of its own still needs its heading to sit
 * somewhere. This is the same block on the paper ground. */
.page-head {
  max-width: var(--page-max);
  margin-inline: auto;
  padding: calc(var(--header-height) + var(--space-12)) var(--space-gutter)
    var(--space-4);
}

.page-head h1 {
  margin-top: var(--space-5);
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-weight: var(--font-weight-light);
}

.page-head__lead {
  max-width: var(--prose-max);
  margin-top: var(--space-4);
  font-size: var(--text-md);
  line-height: var(--text-md-line);
  color: var(--color-muted);
}

.page-head__count {
  font-size: var(--text-sm);
  letter-spacing: 0.08em;
  color: var(--color-muted);
}

/* ------------------------------------------------------------------ *
 * Sections. Four grounds, and the ground carries meaning at section level:
 * paper is the page, white is a block that holds objects, and the two dark
 * ones are the blocks that speak for the company rather than about a
 * property.
 * ------------------------------------------------------------------ */

.section {
  padding-block: var(--space-section);
}

/* A section that follows something which has already paid the distance --
 * the path above an article, a heading block above a list. */
.section--flush {
  padding-top: var(--space-6);
}

.section__inner {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--space-gutter);
}

.section--tinted {
  background-color: var(--color-surface);
}

.section--solution {
  background-color: var(--color-surface-tinted);
}

.section--accent,
.section--dark {
  background-color: var(--color-band);
  color: var(--text-cool-soft);
}

.section--accent h1,
.section--accent h2,
.section--accent h3,
.section--dark h1,
.section--dark h2,
.section--dark h3 {
  color: var(--color-on-accent);
}

.section--accent .lead,
.section--dark .lead,
.section--accent p,
.section--dark p {
  color: var(--text-cool-soft);
}

/* 54 rem rather than the 48.75 it was. The block is a heading with a lead
 * under it, and at 780 px it was 66 per cent of the 1184 the page has --
 * enough of a difference to read as a column rather than as the top of the
 * section. The lead itself is set in the serif at a size where 864 px is
 * still about seventy characters. */
.section__head {
  max-width: 54rem;
  margin-bottom: var(--space-section-sm);
}

.section__head h2 {
  margin-top: var(--space-5);
}

.section__head p {
  margin-top: var(--space-5);
  color: var(--color-muted);
}

.section__head--center {
  margin-inline: auto;
  text-align: center;
}

/* The lead paragraph is the serif, which is what marks it as language rather
 * than as interface. */
.lead {
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--display-sm);
  line-height: var(--display-line-lead);
  color: var(--color-text);
}

.rule,
.brand-rule {
  height: 0;
  margin: 0;
  border: none;
  border-top: var(--border-hairline) solid var(--color-brand-line);
}

/* ------------------------------------------------------------------ *
 * The property card.
 *
 * White on the paper ground, lifted by the one shadow this design has, with
 * the image scaling inside its own frame on hover. The whole card is a target
 * — the title carries the link and `::after` stretches it over the card, so
 * there is one tab stop and one accessible name rather than three.
 * ------------------------------------------------------------------ */

.property-grid,
.article-grid {
  display: grid;
  /* `auto-fit` rather than `auto-fill`, and the difference is visible
   * whenever the collection is smaller than the row. `auto-fill` keeps the
   * empty tracks it could not fill: one related article sat in a 371 px card
   * with 813 px of paper beside it, measured on the guide. `auto-fit`
   * collapses them, so one card is one card the width of the row. */
  grid-template-columns: repeat(auto-fit, minmax(min(22.5rem, 100%), 1fr));
  gap: var(--space-grid);
  margin: 0;
  padding: 0;
  list-style: none;
}

.property-card {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  background-color: var(--color-surface);
  box-shadow: var(--shadow-card);
  border: var(--border-hairline) solid var(--color-hairline);
  transition: transform var(--motion-slow) var(--motion-ease),
    box-shadow var(--motion-slow) var(--motion-ease);
}

.property-card:hover {
  transform: translateY(-6px);
  box-shadow: var(--shadow-raised);
}

/* The lift is decoration. Somebody who asked their system for less motion
 * gets the shadow and not the movement. */
@media (prefers-reduced-motion: reduce) {
  .property-card:hover {
    transform: none;
  }
}

.property-card__figure {
  position: relative;
  overflow: hidden;
  aspect-ratio: 3 / 2;
  margin: 0;
  background-color: var(--coal);
}

.property-card__figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--motion-image) var(--motion-ease);
}

.property-card:hover .property-card__figure img {
  transform: scale(1.055);
}

/* A property with no released photograph shows the mark rather than a broken
 * frame. Discretion is the default in this trade, and an empty grey box
 * states nothing while a line states a policy. */
.property-card__placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  font-size: var(--text-xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-brand-soft);
  background-color: var(--coal);
}

.property-card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: var(--space-6) var(--space-6) var(--space-8);
}

.property-card__place {
  order: -1;
  margin-bottom: var(--space-3);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-brand-ink);
}

.property-card__title {
  margin: 0;
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--text-2xl);
  font-weight: var(--font-weight-regular);
  line-height: 1.2;
  color: var(--color-heading);
}

.property-card__title a {
  color: inherit;
  text-decoration: none;
}

/* One target over the whole card. The link keeps its own focus ring because
 * the outline is drawn on the anchor and the anchor is the size of the card. */
.property-card__title a::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

.property-card__title a:focus-visible {
  outline: none;
}

.property-card:has(.property-card__title a:focus-visible) {
  outline: var(--border-focus) solid var(--color-focus);
  outline-offset: var(--border-focus-offset);
}

.property-card__facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-6);
  margin: var(--space-5) 0 0;
  padding: var(--space-5) 0 0;
  border-top: var(--border-hairline) solid var(--color-hairline);
  list-style: none;
  font-size: var(--text-sm);
  color: var(--color-muted);
}

.property-card__price {
  margin: var(--space-4) 0 0;
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--text-xl);
  color: var(--color-heading);
}

/* ------------------------------------------------------------------ *
 * The badge on a photograph — sold, reserved, new.
 * ------------------------------------------------------------------ */

.badge-photo {
  position: absolute;
  top: var(--space-4);
  left: var(--space-4);
  z-index: 2;
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-pill);
  text-transform: uppercase;
  color: var(--color-brand-soft);
  background-color: var(--black);
  border: var(--border-hairline) solid var(--color-brand-line);
}

.badge-photo--red {
  color: var(--white);
  background-color: var(--red);
  border-color: var(--red);
}

.badge-photo--ivory {
  color: var(--black);
  background-color: var(--ivory);
  border-color: var(--ivory);
}

/* ------------------------------------------------------------------ *
 * The fact bar under a property hero — value over label, the value in the
 * serif and the label in gold capitals.
 * ------------------------------------------------------------------ */

.factbar {
  background-color: var(--ink);
}

.factbar__inner {
  max-width: var(--page-max);
  margin-inline: auto;
  padding: var(--space-6) var(--space-gutter);
  /* Walk of 2026-10-16: a flexible row rather than a grid of equal columns.
   * Seven figures is the usual count for a house and all seven belong on one
   * line at desktop width, but at 7.5rem a column was narrower than
   * "Nettowohnfläche" in capitals and than "Fussbodenheizung" in the serif,
   * and the words were broken ("Nettowohnflä-che", "Kunststoff/P VC"). A
   * figure is now never narrower than its longest word and takes a line of
   * its own before it breaks one. */
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-6);
}

.factbar__inner > * {
  flex: 1 0 7.5rem;
  min-width: min-content;
}

.factbar__value {
  display: block;
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--text-2xl);
  color: var(--color-on-accent);
  /* A list of kinds (E-129) wraps between its words, never inside one. */
  overflow-wrap: normal;
  hyphens: manual;
}

.factbar__label {
  display: block;
  margin-top: var(--space-1);
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-brand);
  overflow-wrap: normal;
  hyphens: manual;
}

@media (max-width: 43.75rem) {
  .factbar__inner {
    gap: var(--space-3) var(--space-4);
  }

  /* Two figures a line, a long word taking a line of its own. */
  .factbar__inner > * {
    flex-basis: calc(50% - var(--space-4));
  }

  .factbar__value {
    font-size: var(--text-lg);
  }

  .factbar__label {
    letter-spacing: 0.06em;
  }
}

/* ------------------------------------------------------------------ *
 * The bordered panel — a service, a location, an event.
 *
 * A gold border on a near-transparent ground with a diagonal gold wash. On
 * hover the border goes full gold and the panel lifts. It is the dark
 * counterpart of the white card, and the site uses it wherever a block says
 * something about the company rather than about a property.
 * ------------------------------------------------------------------ */

.brand-panel {
  padding: var(--space-10) var(--space-8);
  border: var(--border-hairline) solid var(--color-brand-line-soft);
  background-image: var(--wash-panel);
  transition: border-color var(--motion-slow) var(--motion-ease),
    transform var(--motion-slow) var(--motion-ease);
}

.brand-panel:hover {
  border-color: var(--color-brand);
  transform: translateY(-4px);
}

@media (prefers-reduced-motion: reduce) {
  .brand-panel:hover {
    transform: none;
  }
}

.brand-panel__num {
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-style: italic;
  font-size: var(--text-lg);
  color: var(--color-brand);
}

.brand-panel h3 {
  margin: var(--space-4) 0 var(--space-3);
}

.brand-panel p {
  font-size: var(--text-base);
  margin: 0;
}

.brand-panel-grid {
  display: grid;
  margin: 0;
  padding: 0;
  list-style: none;
  grid-template-columns: repeat(auto-fit, minmax(min(18.75rem, 100%), 1fr));
  gap: var(--space-grid);
}

/* ------------------------------------------------------------------ *
 * The row list — a table that is not a table, for locations and the like.
 *
 * On hover it tints and gains left padding, which reads as the row stepping
 * forward. Below 860 px the description is dropped rather than wrapped: four
 * columns of prose on a phone is four columns of nothing.
 * ------------------------------------------------------------------ */

.row-list {
  border-top: var(--border-hairline) solid var(--color-brand-line-soft);
  margin: 0;
  padding: 0;
  list-style: none;
}

.row-list__row {
  display: grid;
  grid-template-columns: 9.375rem 1.1fr 1.6fr auto;
  align-items: baseline;
  gap: var(--space-5) var(--space-10);
  padding: var(--space-6) var(--space-1);
  border-bottom: var(--border-hairline) solid var(--color-brand-line-soft);
  transition: background-color var(--motion-base) var(--motion-ease),
    padding-left var(--motion-base) var(--motion-ease);
}

.row-list__row:hover {
  background-color: var(--color-brand-wash);
  padding-left: var(--space-4);
}

.row-list__kind {
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-pill);
  text-transform: uppercase;
  color: var(--color-brand);
}

.row-list__name {
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--text-2xl);
  line-height: 1.2;
  color: var(--color-on-accent);
}

.row-list__desc {
  font-size: var(--text-sm);
  color: var(--color-on-accent-muted);
}

.row-list__state {
  justify-self: end;
  white-space: nowrap;
  padding: var(--space-1-5) var(--space-3);
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-pill);
  text-transform: uppercase;
  color: var(--color-brand-soft);
  border: var(--border-hairline) solid var(--color-brand-line);
}

.row-list__state--active {
  color: var(--color-on-brand);
  background-color: var(--color-brand);
  border-color: var(--color-brand);
}

/* Where the four columns stand side by side, they are the list's columns and
 * not each row's own: sized per row by its `auto` state, the descriptions
 * began at three different places down the page. */
@media (min-width: 53.8125rem) {
  .row-list {
    display: grid;
    grid-template-columns: 9.375rem 1.1fr 1.6fr auto;
    column-gap: var(--space-10);
  }

  .row-list__row {
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
  }
}

@media (max-width: 53.75rem) {
  .row-list__row {
    grid-template-columns: 1fr auto;
  }

  .row-list__kind {
    grid-column: 1 / -1;
  }

  .row-list__desc {
    display: none;
  }
}

/* ------------------------------------------------------------------ *
 * The side card on a property page — sticky, white, the price in the serif.
 * ------------------------------------------------------------------ */

.detail-cols {
  display: grid;
  grid-template-columns: 1.7fr 1fr;
  gap: var(--space-section-sm);
  align-items: start;
}

.sidecard {
  position: sticky;
  top: calc(var(--header-height) + var(--space-6));
  padding: var(--space-8);
  background-color: var(--color-surface);
  border: var(--border-hairline) solid var(--color-hairline);
  box-shadow: var(--shadow-card);
}

.sidecard__ref {
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-pill);
  text-transform: uppercase;
  color: var(--color-muted);
}

.sidecard__price {
  margin: var(--space-3) 0 var(--space-6);
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--text-3xl);
  color: var(--color-heading);
}

.sidecard dl {
  margin-bottom: var(--space-6);
}

.sidecard dl > div {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-2);
  border-bottom: var(--border-hairline) solid var(--color-hairline);
  font-size: var(--text-base);
}

.sidecard dt {
  color: var(--color-muted);
}

.sidecard dd {
  margin: 0;
  text-align: right;
  color: var(--color-heading);
}

.sidecard .btn {
  width: 100%;
  justify-content: center;
}

@media (max-width: 60rem) {
  .detail-cols {
    grid-template-columns: 1fr;
  }

  .sidecard {
    position: static;
  }
}

/* The highlight list — a gold middot instead of a bullet, on hairlines. */
.checklist {
  margin: var(--space-8) 0;
  padding: 0;
  list-style: none;
}

.checklist li {
  display: flex;
  gap: var(--space-4);
  padding-block: var(--space-3);
  border-bottom: var(--border-hairline) solid var(--color-hairline);
  font-size: var(--text-md);
}

/* A gold middot, and every property of the working area's tick has to be
 * taken back for it. `custom.css` draws a filled, rounded box with a
 * checkmark centred in it; overriding only `content` and `color` left the
 * middot sitting inside that box, which is what it looked like until this
 * was measured. */
.checklist li::before {
  content: "\00B7";
  position: static;
  display: inline;
  flex: none;
  width: auto;
  height: auto;
  background-color: transparent;
  border-radius: 0;
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--text-2xl);
  line-height: 1;
  color: var(--color-brand);
}

/* ------------------------------------------------------------------ *
 * The filter row — square chips that fill black when they are on.
 * ------------------------------------------------------------------ */

/* The first section of a page without a hero starts below the fixed bar,
 * which on a phone is taller than the section's own top padding. */
.site-main > .section:first-child {
  padding-top: max(var(--space-section), calc(var(--header-height) + var(--space-6)));
}

.filter-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-section-sm);
}

.filter-row > ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.filter-row__chip {
  padding: var(--space-3) var(--space-6);
  font-family: var(--font-family-base);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-button);
  text-transform: uppercase;
  color: var(--color-text);
  background: none;
  border: var(--border-hairline) solid var(--color-border-control);
  transition: color var(--motion-base) var(--motion-ease),
    background-color var(--motion-base) var(--motion-ease),
    border-color var(--motion-base) var(--motion-ease);
}

.filter-row__chip:hover {
  color: var(--color-heading);
  border-color: var(--color-brand-edge);
}

.filter-row__chip[aria-pressed="true"],
.filter-row__chip.is-active {
  color: var(--color-on-primary);
  background-color: var(--color-primary-fill);
  border-color: var(--color-primary-fill);
}

/* The same chip as a link, on the guide's category row. */
a.filter-row__chip {
  display: inline-block;
  text-decoration: none;
}

.filter-row__spacer {
  flex: 1;
}

.filter-row__count {
  font-size: var(--text-sm);
  letter-spacing: 0.08em;
  color: var(--color-muted);
}

/* ------------------------------------------------------------------ *
 * The gallery, and the lightbox it opens.
 *
 * Every sixth tile spans two by two, which is what stops a grid of
 * photographs reading as a contact sheet. Below 700 px it collapses: a
 * two-by-two tile on a phone is one tile.
 * ------------------------------------------------------------------ */

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr));
  gap: var(--space-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.gallery a {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background-color: var(--coal);
}

.gallery img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--motion-image) var(--motion-ease);
}

.gallery a:hover img {
  transform: scale(1.05);
}

.gallery li:nth-child(6n + 1),
.gallery__lead {
  grid-column: span 2;
  grid-row: span 2;
}

@media (max-width: 43.75rem) {
  .gallery li:nth-child(6n + 1),
  .gallery__lead {
    grid-column: span 1;
    grid-row: span 1;
  }
}

/* The pattern above fills its rows only where the count comes out even: a
 * lead and two beside it, then three. Any other count left a hole, most often
 * on a flat's page, where two pictures stood as a large one and a small one
 * over an empty cell. The last row closes itself instead. */
@media (min-width: 43.8125rem) {
  /* Two pictures: side by side at the same size. */
  .gallery:has(> li:nth-child(2):last-child) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .gallery:has(> li:nth-child(2):last-child) > li {
    grid-column: auto;
    grid-row: auto;
  }

  /* One picture: across the width it has, not two thirds of it. */
  .gallery:has(> li:only-child) {
    grid-template-columns: minmax(0, 1fr);
  }

  .gallery > li:only-child {
    grid-column: auto;
    grid-row: auto;
  }

  .gallery > li:only-child a {
    aspect-ratio: 16 / 9;
  }

  /* A last row of one, or a lead with nothing beside it: the whole row. */
  .gallery > li:nth-child(6n + 4):last-child,
  .gallery > li:nth-child(6n + 1):last-child:not(:first-child) {
    grid-column: 1 / -1;
    grid-row: auto;
  }

  .gallery > li:nth-child(6n + 4):last-child a,
  .gallery > li:nth-child(6n + 1):last-child:not(:first-child) a {
    aspect-ratio: 3 / 1;
  }

  /* The lead takes the height of the two rows beside it. At its own 4:3 it
   * ended four pixels above them, which is the row gap it does not span. */
  .gallery:has(> li:nth-child(3)) > li:nth-child(6n + 1):not(:last-child) a {
    aspect-ratio: auto;
    height: 100%;
    contain: size;
  }

  /* A last row of two: the second takes the empty cell, at the height of
   * the one beside it. */
  .gallery > li:nth-child(6n + 5):last-child {
    grid-column: span 2;
  }

  /* `contain: size`, so that the picture's own proportions do not raise the
   * row: the height is the neighbour's, and the picture is cropped to it. */
  .gallery > li:nth-child(6n + 5):last-child a {
    aspect-ratio: auto;
    height: 100%;
    contain: size;
  }
}

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: none;
  align-items: center;
  justify-content: center;
  background-color: var(--color-scrim-deep);
}

.lightbox.is-open {
  display: flex;
}

.lightbox img {
  max-width: 92vw;
  max-height: 86vh;
  object-fit: contain;
}

.lightbox__btn {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--target-primary);
  height: var(--target-primary);
  color: var(--color-on-accent);
  background: none;
  border: var(--border-hairline) solid var(--color-brand-line-soft);
  transition: color var(--motion-base) var(--motion-ease),
    border-color var(--motion-base) var(--motion-ease);
}

.lightbox__btn:hover {
  color: var(--color-brand-soft);
  border-color: var(--color-brand);
}

.lightbox__close {
  top: var(--space-5);
  right: var(--space-5);
}

.lightbox__prev {
  top: 50%;
  left: var(--space-5);
  transform: translateY(-50%);
}

.lightbox__next {
  top: 50%;
  right: var(--space-5);
  transform: translateY(-50%);
}

.lightbox__count {
  position: absolute;
  bottom: var(--space-6);
  left: 50%;
  transform: translateX(-50%);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-pill);
  color: var(--color-on-accent-muted);
}

/* The bar over a list: where the reader is on the left, where they can go
 * next on the right, and a hairline under both. A path on its own was 314 px
 * of ink in a 1184 px band and read as something that had been left over
 * rather than placed. */
.meta-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2) var(--space-6);
  margin-bottom: var(--space-8);
  padding-bottom: var(--space-4);
  border-bottom: var(--border-hairline) solid var(--color-hairline);
}

.meta-bar .breadcrumb {
  margin-bottom: 0;
}

.meta-bar__aside {
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* ------------------------------------------------------------------ *
 * The long-form shell.
 *
 * Every page that is mostly running text -- the four legal documents, the
 * guide -- is laid out as a text column with a rail beside it rather than as
 * a column of 672 px in a field of 1184. The rail carries the contents of the
 * page and the two or three things a reader of that page needs next, so the
 * width is filled with something rather than held open.
 *
 * The contents themselves are built by `app.js` out of the headings that are
 * actually there. Writing them into each template by hand is a second list to
 * keep in step with the first, and the first is 646 lines long.
 * ------------------------------------------------------------------ */

.longform {
  display: grid;
  /* The same zero as on `.article` above, and for the same reason: a legal
   * document carries tables too. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-grid);
}

.longform .prose,
.longform__body {
  max-width: none;
}

.longform__aside {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

@media (min-width: 62rem) {
  .longform {
    grid-template-columns: minmax(0, 1fr) var(--aside-width);
    align-items: stretch;
  }

  /* The rail comes second in the markup, because the document is what the
   * page is about and what a screen reader should reach first. */
  .longform__aside > :first-child {
    position: sticky;
    top: calc(var(--header-height) + var(--space-5));
  }
}

/* A text column of 826 px is wider than the 672 it was, so the type grows
 * with it: the measure stays around eighty characters and the page reads
 * larger rather than merely wider. */
@media (min-width: 62rem) {
  .longform__body {
    font-size: 1.1rem;
    line-height: 1.7;
  }
}

/* ------------------------------------------------------------------ *
 * Forms on the public pages.
 *
 * The panel fills the column it is given; what holds the measure is the field
 * grid inside it (`.form-grid` in custom.css), which puts two short fields on
 * a line and leaves the long ones the full width. A single 544 px form panel
 * with 640 px of paper beside it was the second largest empty area in this
 * design, and capping the panel was what caused it.
 * ------------------------------------------------------------------ */

.form-panel {
  padding: var(--space-8);
  background-color: var(--color-surface);
  border: var(--border-hairline) solid var(--color-hairline);
  border-top: 2px solid var(--color-brand);
  box-shadow: var(--shadow-card);
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-8);
}

/* The progress rail of a stepped form — two pixels, and the bar is gold. */
.progress-rail {
  position: relative;
  height: 2px;
  margin-bottom: var(--space-10);
  background-color: var(--color-hairline);
}

.progress-rail__bar {
  position: absolute;
  inset: 0 auto 0 0;
  background-color: var(--color-brand);
  transition: width var(--motion-slow) var(--motion-ease);
}

/* ------------------------------------------------------------------ *
 * The guide.
 *
 * Built out of the blocks the public part already has — the card shape of a
 * property, the measure of a form — so that an article does not read as a
 * second product.
 * ------------------------------------------------------------------ */

.article-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: var(--space-8);
  background-color: var(--color-surface);
  border: var(--border-hairline) solid var(--color-hairline);
  border-top: 2px solid var(--color-brand);
  box-shadow: var(--shadow-card);
  transition: transform var(--motion-slow) var(--motion-ease),
    box-shadow var(--motion-slow) var(--motion-ease);
}

.article-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-raised);
}

@media (prefers-reduced-motion: reduce) {
  .article-card:hover {
    transform: none;
  }
}

.article-card__meta {
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-brand-ink);
}

.article-card__title {
  margin: var(--space-3) 0 0;
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--text-2xl);
  font-weight: var(--font-weight-regular);
  line-height: 1.2;
}

.article-card__title a {
  color: var(--color-heading);
  text-decoration: none;
}

.article-card__title a:hover {
  color: var(--color-brand-ink);
}

.article-card__excerpt {
  flex: 1;
  margin-top: var(--space-3);
  font-size: var(--text-base);
  color: var(--color-muted);
}

.article-card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
}

.article-card__foot {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: var(--border-hairline) solid var(--color-hairline);
  font-size: var(--text-xs);
  color: var(--color-muted);
}

/* The article is a shell rather than a column: the text takes the width that
 * is left over, and the rail beside it carries the contents, the author and
 * the two things a reader does next. The measure is held by the type size
 * instead — see `.article .prose` below, which is set two steps larger than
 * the page around it. A column of 672 px in a field of 1184 was the single
 * largest piece of empty paper in this design. */
.article {
  display: grid;
  /* `minmax(0, 1fr)` and not the implicit `auto` track. An implicit grid
   * track is `auto`, and an `auto` track's floor is the min-content width of
   * what stands in it -- which here is a table set `width: max-content`. On a
   * 390 px telephone the single column was 1018 px and took the page with it;
   * the cap this block replaced had been hiding it. 1.4.10 Reflow.
   * The two-column rule below stands later and in a query, so nothing about
   * the wide view changes. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-grid);
  align-items: start;
}

@media (min-width: 62rem) {
  .article {
    grid-template-columns: minmax(0, 1fr) var(--aside-width);
    align-items: stretch;
  }

  .article__head,
  .article__foot {
    grid-column: 1 / -1;
  }
}

.article__head {
  margin-bottom: var(--space-6);
}

/* 1.2 rem against the 1.0375 of the page. At 826 px the column holds about
 * eighty characters at this size, which is the measure a reading column is
 * meant to have -- the width is spent on larger type rather than on more
 * words a line. */
.article .prose {
  max-width: none;
  font-size: 1.2rem;
  line-height: 1.75;
}

.article .prose table,
.longform__body table {
  min-width: 100%;
  font-size: var(--text-base);
}

.article__byline,
.article__meta {
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  color: var(--color-muted);
}

.article__faq {
  margin-top: var(--space-16);
}

/* The questions under an article, side by side rather than one under the
 * other: eight of them in a single column was a column of closed boxes 640 px
 * long with the width of the page unused beside it. */
.faq-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(26rem, 100%), 1fr));
  gap: var(--space-4);
  align-items: start;
}

.article__foot {
  margin-top: var(--space-12);
}

.article__aside {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

/* The contents stay in view for the whole article. Everything under them
 * scrolls away as an aside should. */
@media (min-width: 62rem) {
  .article__aside .toc {
    position: sticky;
    top: calc(var(--header-height) + var(--space-5));
  }
}

.aside-card {
  padding: var(--space-6);
  background-color: var(--color-surface);
  border: var(--border-hairline) solid var(--color-hairline);
  border-top: 2px solid var(--color-brand);
}

.aside-card__name {
  margin: 0 0 var(--space-1);
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--text-xl);
  line-height: 1.2;
}

.aside-card__name a {
  color: var(--color-heading);
  text-decoration: none;
}

.aside-card__name a:hover {
  color: var(--color-brand-ink);
}

.aside-card__name {
  margin: 0 0 var(--space-1);
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--text-xl);
  line-height: 1.2;
}

.aside-card__name a {
  color: var(--color-heading);
  text-decoration: none;
}

.aside-card__name a:hover {
  color: var(--color-brand-ink);
}

.aside-card__title {
  margin: 0 0 var(--space-3);
  font-family: var(--font-family-base);
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-brand-ink);
}

.aside-card p:last-child,
.aside-card ul:last-child {
  margin-bottom: 0;
}

.aside-card__list {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-sm);
}

.aside-card__list li + li {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: var(--border-hairline) solid var(--color-hairline);
}

.aside-card__list a {
  display: block;
  padding-block: var(--space-1);
  color: var(--color-heading);
  text-decoration: none;
}

.aside-card__list a:hover {
  color: var(--color-brand-ink);
}

/* The one dark block of the rail: what the reader is meant to do next. */
.aside-card--accent {
  background-color: var(--black);
  border-color: var(--color-brand-line);
  color: var(--text-cool-soft);
}

.aside-card--accent .aside-card__title {
  color: var(--color-brand);
}

.aside-card--accent p {
  color: var(--text-cool-soft);
}

.author-box {
  margin-top: var(--space-10);
  padding: var(--space-8);
  background-color: var(--color-surface-tinted);
  border-left: 2px solid var(--color-brand);
}

.author-box__name {
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--text-xl);
  color: var(--color-heading);
}

.toc {
  padding: var(--space-6);
  background-color: var(--color-surface-tinted);
  border-left: 2px solid var(--color-brand);
}

.toc__title {
  /* A label in the interface face, like the card title beside it: as an
   * `h2` it took the serif, and "Inhalt" and "Fragen dazu" were set in two
   * families one above the other. */
  font-family: var(--font-family-base);
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-brand-ink);
}

.toc__list {
  margin: var(--space-4) 0 0;
  padding: 0;
  list-style: none;
}

.toc__list li + li {
  margin-top: var(--space-2);
}

.newsletter {
  padding: var(--space-10) var(--space-8);
  background-color: var(--black);
  border-top: 2px solid var(--color-brand);
  color: var(--text-cool-soft);
}

.newsletter h2,
.newsletter h3 {
  color: var(--color-on-accent);
}

.pagination-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-12);
  padding-top: var(--space-6);
  border-top: var(--border-hairline) solid var(--color-hairline);
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
}

/* ------------------------------------------------------------------ *
 * The sign-in pages — the first touch with the product.
 *
 * A split screen: the form on paper to the left, a photograph under a dark
 * veil to the right with the signet on it. On a phone the photograph is a
 * gold band and nothing else, because a hero above a login form is a screen
 * of scrolling before anybody can type.
 * ------------------------------------------------------------------ */

.auth {
  display: grid;
  grid-template-columns: 1fr;
  /* The band is exactly as tall as it is and the form takes the rest. Two
   * `auto` rows share the leftover height of `100svh` between them instead,
   * which measured as 229 px above the card and 107 below it at 390x844 —
   * it reads as generous spacing and is an accident. */
  grid-template-rows: auto 1fr;
  min-height: 100svh;
}

.auth__form {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--space-12) var(--space-gutter);
  background-color: var(--color-ground);
}

.auth__figure {
  display: none;
  position: relative;
  isolation: isolate;
  background-color: var(--black);
}

/* The photograph, and only the photograph. As a descendant selector this
 * also caught the signet standing on it, which then filled half the screen —
 * `width`/`height` attributes are a hint and lose against any rule. */
.auth__figure > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.auth__signet img {
  width: auto;
  height: 4.875rem;
}

.auth__figure::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--veil-page);
}

.auth__signet {
  position: absolute;
  inset: auto var(--space-12) var(--space-12) var(--space-12);
  z-index: 1;
}

.auth__claim {
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-style: italic;
  font-size: var(--text-2xl);
  color: var(--color-on-accent);
  margin-top: var(--space-5);
}

/* The band that stands in for the photograph on a phone. */
.auth__band {
  height: var(--space-1);
  background-color: var(--color-brand);
}

.auth-card {
  width: 100%;
  max-width: var(--form-max);
  margin-inline: auto;
}

.auth-card__foot {
  margin-top: var(--space-8);
  padding-top: var(--space-6);
  border-top: var(--border-hairline) solid var(--color-hairline);
  font-size: var(--text-sm);
  color: var(--color-muted);
}

.auth-card__foot a svg { margin-inline-end: var(--space-1); }

@media (min-width: 62rem) {
  .auth {
    grid-template-columns: 1fr 1fr;
    /* One row, not two. The band above is `display: none` from here, but
     * `grid-template-rows: auto 1fr` still declares a second row — and the
     * two columns both sit in the first one, which sizes itself to the form.
     * Measured at 1440x900: rows `580px 320px`, so the photograph stopped two
     * thirds of the way down and the last third was bare paper. */
    grid-template-rows: 1fr;
  }

  .auth__figure {
    display: block;
  }

  .auth__band {
    display: none;
  }

  .auth__form {
    padding-inline: var(--space-16);
  }
}

/* ------------------------------------------------------------------ *
 * Footer. Black, four columns, gold column headings, and the Swiss cross
 * drawn rather than fetched — an image for fourteen pixels of flag is a
 * request nobody needs to make.
 * ------------------------------------------------------------------ */

.site-footer {
  background-color: var(--black);
  color: var(--text-cool-soft);
}

.site-footer__inner {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--space-gutter);
}

.site-footer__top {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1.2fr;
  gap: var(--space-10);
  padding: var(--space-16) 0 var(--space-12);
}

/* A grid item is `min-width: auto`, which means it never shrinks below its
 * longest unbreakable word. At 150 per cent type on a 320 px screen,
 * "Aussergewöhnliche" is wider than the column, so the column grew and took
 * the page with it — measured at 322 px in a 320 px view on seven pages.
 *
 * `min-width: 0` lets the column shrink; `overflow-wrap` then breaks the word
 * rather than letting it stick out of the column instead. Both are needed:
 * either alone moves the problem rather than solving it. */
.site-footer__top > * {
  min-width: 0;
  overflow-wrap: break-word;
}

.site-footer h2,
.site-footer h3,
.site-footer h4 {
  margin-bottom: var(--space-5);
  font-family: var(--font-family-base);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-brand);
}

.site-footer ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-footer a {
  display: block;
  padding-block: var(--space-1);
  font-size: var(--text-base);
  color: var(--text-cool-soft);
  text-decoration: none;
}

.site-footer a:hover,
.site-footer a:focus-visible {
  color: var(--color-brand-soft);
}

.site-footer__claim {
  max-width: 18.75rem;
  margin-top: var(--space-5);
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-style: italic;
  font-size: var(--text-lg);
  color: var(--text-cool-soft);
}

.site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-4) var(--space-10);
  padding: var(--space-6) 0 var(--space-8);
  border-top: var(--border-hairline) solid var(--color-brand-line-soft);
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
}

.site-footer__legal ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
}

.site-footer__legal a {
  display: inline;
}

.swiss-note {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.swiss-note__cross {
  position: relative;
  display: inline-block;
  width: 14px;
  height: 14px;
  background-color: var(--red);
}

.swiss-note__cross::before,
.swiss-note__cross::after {
  content: "";
  position: absolute;
  background-color: var(--white);
}

.swiss-note__cross::before {
  left: 6px;
  top: 2.5px;
  width: 2px;
  height: 9px;
}

.swiss-note__cross::after {
  top: 6px;
  left: 2.5px;
  width: 9px;
  height: 2px;
}

@media (max-width: 60rem) {
  .site-footer__top {
    grid-template-columns: 1fr 1fr;
  }
}

@media (max-width: 35rem) {
  .site-footer__top {
    grid-template-columns: 1fr;
  }
}

/* ------------------------------------------------------------------ *
 * Reveal on scroll.
 *
 * `app.js` adds `is-visible` through an IntersectionObserver. The rule that
 * matters is the last one: with reduced motion the element is simply visible,
 * and it is visible whether or not the observer ever runs — an animation that
 * hides content until a script fires is a page that shows nothing when the
 * script does not.
 * ------------------------------------------------------------------ */

/* The attribute is written by `app.js` immediately before the observer is
 * created, and it is the whole safety of this arrangement: without it the
 * rule below does not exist, so a page whose script did not load, or ran and
 * threw, is a page where every block is simply visible.
 *
 * Written the other way round — `.reveal { opacity: 0 }` on its own, with a
 * script that removes it — the same failure hides half the page and nothing
 * reports it, because the markup is all there and the server answered 200.
 * The company's own site is built that way. */
:root[data-reveal="on"] .reveal {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.9s var(--motion-ease),
    transform 0.9s var(--motion-ease);
}

:root[data-reveal="on"] .reveal.is-visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  :root[data-reveal="on"] .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .hero__bg img {
    animation: none;
    transform: none;
  }

  .hero__scroll::after {
    animation: none;
  }
}

/* ------------------------------------------------------------------ *
 * Breakpoints. Every one a `max-width` here, because the public part is laid
 * out for the desk first and folded down — the opposite of the working area,
 * which is a phone first.
 * ------------------------------------------------------------------ */

@media (max-width: 60rem) {
  /* The header is solid from here down: a transparent bar over a photograph
   * nobody chose is a bar whose legibility depends on the picture. */
  .site-header {
    background-color: var(--black);
    background-image: none;
    box-shadow: 0 var(--border-hairline) 0 var(--color-brand-line);
  }

  /* Above the open drawer, which covers the whole screen from here down:
   * the handle is the way out of it again. */
  .site-nav__toggle {
    display: flex;
    position: relative;
    z-index: 121;
  }

  /* The drawer. Full-screen, near-opaque, the entries in the serif at two
   * rem — the site's own mobile menu, which is a page rather than a list. */
  .site-nav {
    position: fixed;
    inset: 0;
    z-index: 120;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background-color: var(--color-scrim-deep);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--motion-slow) var(--motion-ease);
  }

  .site-nav[data-open="true"],
  .site-nav:target {
    opacity: 1;
    pointer-events: auto;
  }

  .site-nav ul {
    flex-direction: column;
    gap: var(--space-8);
  }

  .site-nav a {
    font-family: var(--font-family-display);
    font-variant-numeric: lining-nums;
    font-size: var(--text-3xl);
    letter-spacing: 0;
    text-transform: none;
    color: var(--color-on-accent);
  }

  .site-nav a::after {
    display: none;
  }

  .site-nav a:hover,
  .site-nav a[aria-current="page"] {
    color: var(--color-brand);
  }
}

@media (max-width: 47.5rem) {
  /* A button that cannot fit its label on one line wraps rather than
   * overflowing, and centres while it does.
   *
   * `min-width: 0` belongs with it: a flex item never shrinks below its
   * content unless told to, so a tracked upper-case label was 292 px wide in
   * a 248 px row and took the page with it. */
  .hero__actions .btn,
  .form-actions .btn {
    flex: 1 1 100%;
    min-width: 0;
    justify-content: center;
    text-align: center;
  }

  /* Every button on the public part, not only the two rows above. The
   * company's own site makes the same exception at the same width, and the
   * reason is the tracking: "Zum Kontaktformular" at 0.22em is 292 px wide,
   * which fits a telephone at 100 per cent type and does not at 150.
   * Measured in the closing band, which is neither a hero nor a form. */
  .site .btn {
    max-width: 100%;
    white-space: normal;
    line-height: 1.45;
  }
}

/* ------------------------------------------------------------------ *
 * The editorial split — text beside a photograph, with the offset frame the
 * company's own "about" page draws behind the picture.
 *
 * The frame is a pseudo-element at negative inset rather than a second image,
 * and it sits *behind* the photograph on `z-index: -1`, which is why the
 * container needs no stacking context of its own.
 * ------------------------------------------------------------------ */

.split-editorial:not(:has(.framed-image)) { align-items: start; }

.split-editorial {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: var(--space-section-sm);
  align-items: center;
}

.framed-image {
  position: relative;
  margin: 0;
}

.framed-image img {
  width: 100%;
  height: auto;
  box-shadow: var(--shadow-card);
}

.framed-image::before {
  content: "";
  position: absolute;
  inset: -22px auto auto -22px;
  width: 60%;
  height: 60%;
  z-index: -1;
  border: var(--border-hairline) solid var(--color-brand-line);
}

@media (max-width: 60rem) {
  .split-editorial {
    grid-template-columns: 1fr;
  }

  /* The offset frame runs off the side of a phone and pushes the page wider
   * than the viewport. It is decoration, so it goes rather than the picture. */
  .framed-image::before {
    display: none;
  }
}

/* ------------------------------------------------------------------ *
 * The values — three statements on a gold rule, and nothing else.
 * ------------------------------------------------------------------ */

.value-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(16.25rem, 100%), 1fr));
  gap: var(--space-8);
  margin: 0;
  padding: 0;
  list-style: none;
}

.value {
  padding-top: var(--space-6);
  border-top: var(--border-hairline) solid var(--color-brand-line);
}

.value h3 {
  margin-bottom: var(--space-3);
}

.value p {
  margin: 0;
  font-size: var(--text-base);
  color: var(--color-muted);
}

.section--accent .value p,
.section--dark .value p {
  color: var(--text-cool-soft);
}

/* ------------------------------------------------------------------ *
 * The team.
 *
 * A portrait is 3:3.6, slightly desaturated at rest and full on hover, with a
 * gold frame drawing itself inward. The frame is what makes a grid of
 * headshots read as a considered page rather than as a staff directory, and
 * it costs one pseudo-element.
 * ------------------------------------------------------------------ */

.team-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(15.625rem, 100%), 1fr));
  gap: var(--space-grid);
  margin: 0;
  padding: 0;
  list-style: none;
}

.member {
  text-align: center;
}

.member__photo {
  position: relative;
  aspect-ratio: 3 / 3.6;
  overflow: hidden;
  margin-bottom: var(--space-5);
  background-color: var(--coal);
}

.member__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(0.88);
  transition: transform var(--motion-image) var(--motion-ease),
    filter var(--motion-slow) var(--motion-ease);
}

.member:hover .member__photo img {
  transform: scale(1.045);
  filter: saturate(1);
}

/* The frame that draws itself inward. `inset` is animatable in every engine
 * this project targets; the border colour carries the fade so that nothing
 * is drawn at all at rest. */
.member__photo::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  border: var(--border-hairline) solid transparent;
  transition: border-color var(--motion-slow) var(--motion-ease),
    inset var(--motion-slow) var(--motion-ease);
}

.member:hover .member__photo::after {
  inset: 10px;
  border-color: var(--color-brand-line);
}

@media (prefers-reduced-motion: reduce) {
  .member:hover .member__photo img,
  .member:hover .member__photo::after {
    transform: none;
    inset: 0;
  }
}

/* Somebody who joined before the photographer came. */
.member__initials {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  font-size: var(--text-4xl);
  color: var(--color-brand-soft);
}

.member h3 {
  font-size: var(--text-xl);
}

.member__role {
  margin: var(--space-2) 0 0;
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-brand);
}

.member__role-line {
  display: block;
  line-height: 1.8;
}

/* ------------------------------------------------------------------ *
 * The closing band — a photograph under a heavy wash with one sentence and
 * one button on it.
 * ------------------------------------------------------------------ */

.cta-band {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  text-align: center;
  background-color: var(--color-band);
}

.cta-band::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-color: var(--color-scrim);
}

.cta-band .hero__bg img {
  animation: none;
  transform: none;
}

.cta-band__inner {
  max-width: var(--page-max);
  margin-inline: auto;
  padding: var(--space-section) var(--space-gutter);
}

.cta-band h2 {
  max-width: 47.5rem;
  margin: 0 auto var(--space-5);
  color: var(--color-on-accent);
}

.cta-band p {
  max-width: 35rem;
  margin: 0 auto var(--space-10);
  color: var(--color-on-accent-muted);
}

.cta-band p.mb-0 {
  margin-bottom: 0;
}

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

.site-footer__logo {
  height: 9.375rem;
  width: auto;
}

/* The note beside an office that has not opened. Smaller and quieter than the
 * place it qualifies, because it is a qualification rather than a second
 * statement. */
.site-footer__note {
  display: block;
  margin: var(--space-1) 0 0;
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-wordmark);
  text-transform: uppercase;
  color: var(--color-brand);
  /* Not `nowrap`. "In Eröffnung" tracked at 0.18em is wider than the column
   * at 150 per cent type on a 320 px screen, and a note that may not wrap
   * takes the page sideways instead — measured at 322 px in a 320 px view.
   * A block of its own under the place, always: as an inline block it stood
   * beside "Zürich · Schweiz" and under "München · Deutschland", depending
   * on the length of the name, and the column read as uneven. */
}

.site-footer address {
  font-style: normal;
  font-size: var(--text-base);
  line-height: var(--text-md-line);
}

.site-footer li {
  padding-block: var(--space-1);
  font-size: var(--text-base);
}

/* The page behind an open lightbox does not scroll. On the body rather than
 * on `html`, because iOS keeps scrolling the root even when it is told not
 * to, and the class is removed again when the dialogue closes. */
.has-modal {
  overflow: hidden;
}

/* The arrow inside a button, which moves on hover. A span rather than a
 * pseudo-element so that a button without one is unaffected, and `aria-hidden`
 * in the markup so it is never read out — an arrow is not a word. */
.btn__arrow {
  display: inline-block;
  margin-left: var(--space-3);
  font-family: var(--font-family-display);
  font-variant-numeric: lining-nums;
  transition: transform var(--motion-base) var(--motion-ease);
}

.btn:hover .btn__arrow {
  transform: translateX(6px);
}

@media (prefers-reduced-motion: reduce) {
  .btn:hover .btn__arrow {
    transform: none;
  }
}

/* ------------------------------------------------------------------ *
 * The outlined button on a photograph.
 *
 * Public only — it exists for the second action of a hero, where a filled
 * button beside the gold one would make neither of them the main action. The
 * ivory edge at 55 per cent is the company's own value; on hover it fills and
 * inverts.
 * ------------------------------------------------------------------ */

.btn-ivory {
  color: var(--color-on-accent);
  background-color: transparent;
  border-color: var(--color-brand-line);
}

.btn-ivory:hover,
.btn-ivory:focus-visible {
  color: var(--color-on-brand);
  background-color: var(--ivory);
  border-color: var(--ivory);
}

/* ------------------------------------------------------------------ *
 * The appearance control in the footer.
 *
 * It sits on the black ground there, so its buttons take the inverse of what
 * `custom.css` gives them on a light one.
 * ------------------------------------------------------------------ */

.site-footer__tools {
  display: flex;
  justify-content: flex-end;
  padding-bottom: var(--space-6);
}

.site-footer .appearance {
  background-color: transparent;
  border-color: var(--color-brand-line-soft);
}

.site-footer .appearance__button {
  color: var(--color-on-accent-muted);
}

.site-footer .appearance__button:hover {
  color: var(--color-brand-soft);
}

.site-footer .appearance__button[aria-pressed="true"] {
  color: var(--color-on-brand);
  background-color: var(--color-brand);
}

/* ------------------------------------------------------------------ *
 * Corrections measured in the browser rather than reasoned about.
 * Every one of these was a finding of the design matrix — every public page
 * at 360, 768 and 1440 in both themes — and every one is named with what it
 * measured before.
 * ------------------------------------------------------------------ */

/* The header is black in both themes, so everything standing on it takes the
 * inverse of what `custom.css` gives it on a light ground. Measured before:
 * the sign-in link was `--color-heading` on `--black`, which is 1.00:1 in the
 * light theme — the same byte, an invisible control on twenty-six pages. */
.site-header .btn-quiet {
  color: var(--color-on-accent);
  border-color: var(--color-brand-line);
}

.site-header .btn-quiet:hover,
.site-header .btn-quiet:focus-visible {
  color: var(--color-on-brand);
  background-color: var(--ivory);
  border-color: var(--ivory);
}

/* The handle collapsed to 18 px because it is a flex child in a row that runs
 * out of space before it. WCAG 2.5.8 asks 24 by 24 of a control, and this one
 * is the only way into the navigation on a telephone. */
.site-nav__toggle {
  flex: none;
}

/* ------------------------------------------------------------------ *
 * Anything on a dark ground.
 *
 * The three dark blocks — the closing band, an accent section, the newsletter
 * — carry forms and prose that `custom.css` styles for a light ground. On the
 * guide that showed as `--color-heading` on `--black`: a consent label at
 * 1.00:1, its field label at 3.65:1 and the link to the privacy policy at
 * 3.85:1.
 *
 * One rule for all three rather than three, because the next dark block
 * would otherwise arrive with the same defect.
 * ------------------------------------------------------------------ */

/* The black main button would vanish on these; it turns light here. The
 * closing band was missing from the list, and its "Zum Kontaktformular" was
 * a black button on the black band in the light theme. */
.newsletter,
.panel--accent,
.aside-card--accent,
.cta-band,
.section--accent,
.section--dark {
  --color-primary-fill: var(--ivory);
  --color-on-primary: var(--black);
}

.newsletter,
.section--accent,
.section--dark,
.cta-band {
  --color-heading: var(--text-cool);
  --color-text: var(--text-cool);
  --color-muted: var(--text-cool-soft);
  --color-on-tint: var(--text-cool-soft);
  --color-link: var(--gold-soft);
  --color-link-hover: var(--text-cool);
  --color-danger: var(--red-lifted);
  --color-focus: var(--gold);
  --color-border-control: var(--grey-control-dark);
  --color-hairline: var(--grey-line-dark);
  /* A field on a dark ground keeps a light face: a form the reader types into
   * is the one place the page hands the pen over, and an inverted input is
   * markedly harder to read a long address in. */
  --color-surface: var(--white);
}

/* ------------------------------------------------------------------ *
 * The row list at 360 px.
 *
 * Measured: the country and the state ran to 398 px of a 360 px view. The
 * two-column fold placed a nine-word country beside a state that may not
 * wrap, and neither could give way. At this width the row becomes a stack:
 * the state moves under the name and both may wrap.
 * ------------------------------------------------------------------ */

@media (max-width: 30rem) {
  .row-list__row {
    grid-template-columns: 1fr;
    gap: var(--space-2);
  }

  .row-list__state {
    justify-self: start;
    white-space: normal;
  }
}

/* ------------------------------------------------------------------ *
 * A link that stands on its own line is a target, not a word in a sentence,
 * so WCAG 2.5.8's inline exception does not cover it. Measured at 19 and 22
 * pixels of height on three pages.
 *
 * `inline-block` with vertical padding rather than a `min-height`: a height
 * on an inline element does nothing, and the padding grows the hit area
 * without moving the text off its own baseline.
 * ------------------------------------------------------------------ */

:where(.site-main p > a:only-child),
.auth-card__foot a,
.site-footer__legal a {
  display: inline-block;
  padding-block: var(--space-1);
}

/* The way into the application stands twice in the markup and once on the
 * screen: as a button beside the navigation on a desk, as the last entry of
 * the drawer on a telephone. Neither is ever the second copy of the other. */
.site-nav__app {
  display: none;
}

@media (max-width: 60rem) {
  .site-header .btn-quiet {
    display: none;
  }

  .site-nav__app {
    display: block;
  }
}

/* The breadcrumb. Measured at 49 by 19 pixels: a row of targets rather than
 * a sentence, so WCAG 2.5.8's inline exception does not reach it. The padding
 * grows the hit area to 27 px without moving anything, because the row was
 * already taller than its text. */
.breadcrumb-item a {
  display: inline-block;
  padding-block: var(--space-1);
}

/* The locality sketch of a property page (E-02). Drawn in the document
 * rather than fetched: the technical measures this office publishes rule out
 * a third-party map service, so what a tile would have shown is a scale bar,
 * a north arrow and the released area. `landing_pages/maps.py` carries the
 * reasoning. */
.locality {
  margin: 0;
}

/* The sketch or the contact card on its own, at the width either has beside
 * the other rather than across the whole page. */
.locality-single {
  max-width: 36rem;
}

.locality__plan {
  display: block;
  width: 100%;
  max-width: 26rem;
  height: auto;
}

.locality__ground {
  fill: var(--color-surface-tinted);
  stroke: var(--gold-line-soft);
  stroke-width: 1;
}

/* A ring and not a disc. What the drawing says is "somewhere inside this",
 * and a filled shape reads as the outline of a thing. */
.locality__area {
  fill: var(--gold-wash);
  stroke: var(--gold-edge);
  stroke-width: 2;
}

.locality__pin {
  fill: var(--gold-ink);
}

.locality__north line,
.locality__scale line {
  stroke: var(--color-text);
  stroke-width: 2;
}

.locality__north polygon {
  fill: var(--color-text);
}

.locality__north text,
.locality__scale text {
  fill: var(--color-text);
  font-size: 14px;
}

.locality__caption {
  margin-block-start: var(--space-2);
  color: var(--color-muted);
  font-size: var(--text-sm);
}

.contact-person__name {
  margin-block: var(--space-2);
  font-weight: var(--font-weight-medium);
}

/* The language switch of a property page (E-02). A row of codes rather than
 * flag icons: a flag is a country and not a language, and „EN" over a Union
 * Jack is wrong for every reader outside Britain. */
.page-languages__list {
  display: flex;
  gap: var(--space-3);
  margin: 0 0 var(--space-4);
  padding: 0;
  list-style: none;
  font-size: var(--text-sm);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.page-languages__list [aria-current="true"] {
  color: var(--color-muted);
}

/* E-08: one step of an enquiry form in steps. Without the script every step
 * stands open below the last, which is why a step carries no box of its own. */
.form-step { border: 0; padding: 0; margin: 0 0 var(--space-6); min-width: 0; }
.form-step__legend { font-weight: var(--font-weight-medium); margin-bottom: var(--space-3); }
.form-step__nav { display: flex; gap: var(--space-2); justify-content: space-between;
                  margin-top: var(--space-4); }
.form-step[hidden], .form-step__nav[hidden], .form-actions[hidden] { display: none; }

/* E-141: the two sizes the office may give words of a property text. Relative
 * to the paragraph they stand in, so a larger word in a lead stays in step
 * with the lead. */
.size-large { font-size: 1.25em; line-height: 1.35; }
.size-xlarge { font-size: 1.5em; line-height: 1.3; }

/* The office's preview of a property page (2026-10-19): a band at the foot of
 * the window, where it covers neither the page's own head nor its hero, and
 * stays in sight while the page is read. */
.preview-note {
  position: fixed;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1050;
  margin: 0;
  background-color: var(--color-surface);
  color: var(--color-text);
  border-color: var(--color-heading);
  box-shadow: 0 -2px 12px var(--shadow-tint-overlay-near);
}
body:has(.preview-note) { padding-bottom: 6rem; }
