/*
 * locloy public site — the one stylesheet.
 *
 * Deliberately plain: black type on white, one typeface, outlines in dark
 * grey instead of colour. It does not use the application's design tokens
 * (Felix, 2026-11-12); the only thing it shares with the application is the
 * typeface and the mark. Sizes follow Felix's wireframes of 2026-11-13
 * (desktop 1440 px, content 960 px).
 *
 * Mobile first: every rule above the media queries is the phone layout, and
 * the queries change what gets bigger or wider.
 *
 *   below  720 px   phone: one column, card stack after Hochsaison
 *   from   720 px   tablet: still one column, desktop type sizes
 *   from  1024 px   desktop: 960 px, two columns, stack in the left one
 */

/* ---------------------------------------------------------------------------
 * Typeface — Atkinson Hyperlegible Next, self-hosted.
 * Never load a font (or anything else) from a third-party origin: the
 * Content-Security-Policy in vercel.json refuses it anyway.
 * Licence: SIL Open Font License 1.1, see /assets/fonts/OFL.txt
 * ------------------------------------------------------------------------- */
@font-face {
  font-family: "Atkinson Hyperlegible Next";
  src: url("/assets/fonts/AtkinsonHyperlegibleNext-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Atkinson Hyperlegible Next";
  src: url("/assets/fonts/AtkinsonHyperlegibleNext-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* ---------------------------------------------------------------------------
 * The few values the whole page is built from. Change them here, nowhere else.
 * ------------------------------------------------------------------------- */
:root {
  --ink: #000;                    /* all text stays black */
  --stroke: #333;                 /* every outline: tiles, buttons, close
                                     button, lines (Felix, 2026-11-13) */
  --paper: #fff;
  --line: 1px solid var(--stroke);
  --radius: 24px;                 /* tiles and sheet */
  --radius-small: 8px;            /* buttons and form fields */
  --gap: 16px;                    /* between tiles */
  --pad-top: 24px;                /* inside a tile */
  --pad-bottom: 24px;
  --pad-x: 24px;                  /* inside a tile, left and right */
  --edge: 20px;                   /* page margin left and right */
  --max: 960px;                   /* widest the content gets */
  --side: 328px;                  /* right-hand column on a wide screen */

  /* Card stack (see "Card stack" below). */
  --stack-top: 12px;              /* where the stack sticks, from the top of
                                     the window */
  --strip: 62px;                  /* how much of a covered card stays
                                     visible: its top edge and its title */
}

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: "Atkinson Hyperlegible Next", system-ui, sans-serif;
  font-size: 1.125rem;            /* 18px on a phone */
  line-height: 1.3;               /* the same everywhere, as in the wireframe */
  /* German compounds (Zuwendungsbestätigung) must never push a tile wider
     than the screen. No automatic hyphenation: it split short words
     ("Ver-ein") on phones; the longest compound fits the narrowest column. */
  overflow-wrap: break-word;
}

/* While an overlay is open the page underneath must not scroll. */
html:has(dialog[open]) {
  overflow: hidden;
}

h1,
h2,
p {
  margin: 0;
}

/* Paragraphs are separated by one blank line, exactly one line high, as in
   the texts (1.3em is one line at line-height 1.3). Lines inside a paragraph
   are separated by <br>. */
p + p {
  margin-top: 1.3em;
}

a {
  color: inherit;
  text-underline-offset: 0.18em;
}

/* One visible focus ring for everything that can take keyboard focus. */
:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

/* Text for screen readers only (used for the honeypot label and similar). */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The content column: centred, never wider than --max. Header, start page,
   overlay and footer all use it, so they all end at the same edges. */
.wrap {
  width: 100%;
  max-width: calc(var(--max) + 2 * var(--edge));
  margin: 0 auto;
  padding: 0 var(--edge);
}

/* ---------------------------------------------------------------------------
 * Header: the mark with the word on the left, the language switch on the right.
 * ------------------------------------------------------------------------- */
.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-top: 16px;
  padding-bottom: 16px;
}

.site-header .logo {
  display: inline-flex;
}

.site-header .logo img {
  display: block;
  height: 40px;
  width: auto;
}

.lang {
  display: flex;
  margin: 0;
  /* The links are 44px targets wider than their visible box; pull the last
     one out so the EN box ends exactly at the content edge. */
  margin-right: -4px;
  padding: 0;
  list-style: none;
}

/* Each language link is a 44px target; the visible box sits inside it. */
.lang a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  text-decoration: none;
  font-size: 1rem;
}

.lang a span {
  padding: 1px 7px;
  border: 1px solid transparent;
  border-radius: 4px;
}

.lang a[aria-current="page"] span {
  border-color: var(--stroke);
  font-weight: 500;
}

/* DE sits close to the EN box, as in the wireframe. */
.lang li:first-child a {
  justify-content: flex-end;
}

.lang a:hover span {
  border-color: var(--stroke);
}

/* ---------------------------------------------------------------------------
 * Tiles.
 * ------------------------------------------------------------------------- */
.tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;                      /* title to text, text to button */
  padding: var(--pad-top) var(--pad-x) var(--pad-bottom);
  border: var(--line);
  border-radius: var(--radius);
  /* White, not transparent: in the card stack each card covers the one
     before it. */
  background: var(--paper);
}

.tile h1 {
  font-size: 1.875rem;            /* 30px */
  font-weight: 500;
  line-height: 1.3;
}

.tile h2 {
  font-size: 1.375rem;            /* 22px */
  font-weight: 500;
  line-height: 1.3;
}

.tile--hero .body {
  font-size: 1.25rem;             /* 20px */
}

/* Hochsaison is set one step smaller than the other tiles (wireframe). */
.tile--season h2 {
  font-size: 1.25rem;             /* 20px */
}

.tile--season .body {
  font-size: 1rem;                /* 16px */
}

/* The button sits at the bottom of its tile, whatever the text length. */
.tile .button {
  margin-top: auto;
}

/* ---------------------------------------------------------------------------
 * Card stack.
 *
 * Textblocks 2 to 8 sit in one container, .stack. While the page scrolls,
 * the cards marked below stick to the top of the window one after another
 * (position: sticky), each a strip lower than the one before, so every
 * covered card keeps its top edge and its title in view. Later cards paint
 * over earlier ones because they come later in the page.
 *
 * Pure CSS, so it works without JavaScript. assets/site.js adds: a tap on a
 * covered card scrolls back to it; a card too tall for the space left under
 * the stack sticks a little higher, so its last line can always be read;
 * covered titles shrink on narrow or low screens; and once the last card has
 * reached its place the whole stack scrolls on as one piece (.is-frozen), so
 * the last card never slides over the strips above it.
 *
 * --i is the place of a card in the stack (0 is the first). The stack starts
 * with Immer in der Tasche on every screen; Hochsaison stands before it on a
 * phone and beside it on a wide screen, and is not part of it.
 * --stick-top is set by site.js only when a card needs to stick higher.
 * ------------------------------------------------------------------------- */
.stack {
  --runout: 48px;                 /* space under the last card */
  margin-top: var(--gap);
}

.stack > * + * {
  margin-top: var(--gap);
}

/* The space under the last card is part of the stack, not a margin after it:
   the cards may only stick inside their container, and this run-out is the
   room in which the last card reaches its place. site.js makes it taller on
   high screens, where the page would otherwise end before that. */
.stack::after {
  content: "";
  display: block;
  height: var(--runout);
}

.tile--pocket  { --i: 0; }
.tile--kiez    { --i: 1; }
.tile--install { --i: 2; }
.tile--cost    { --i: 3; }
.tile--privacy { --i: 4; }
.tile--clubs   { --i: 5; }

.tile--pocket,
.tile--kiez,
.tile--install,
.tile--cost,
.tile--privacy,
.tile--clubs {
  position: sticky;
  top: var(--stick-top, calc(var(--stack-top) + var(--i) * var(--strip)));
}

/* Two invisible probes site.js uses to read the height of the window as
   small and as large as it gets (with and without the iPhone's bars). */
.vh-probe {
  position: fixed;
  top: 0;
  left: 0;
  width: 0;
  height: 100vh;
  visibility: hidden;
  pointer-events: none;
}

.vh-probe--small {
  height: 100svh;
}

.vh-probe--large {
  height: 100lvh;
}

/* The end of the stack (site.js): the cards stop sticking and keep the place
   they had at that moment, so the stack scrolls on as one piece. */
.stack.is-frozen > .tile,
.stack.is-frozen .tile--season {
  position: relative;
  top: var(--freeze, 0px);
}

/* A covered card shows only what is above the next card (site.js sets
   --visible); nothing of it peeks out under a shorter card on top. */
.js .stack > .tile.is-covered {
  clip-path: inset(0 0 max(0px, calc(100% - var(--visible, 100%))) 0);
}

/* A covered card is a way back to it (site.js): its title while the next
   card is coming up, the whole strip once it is stacked. */
.stack .tile.is-covered > h2,
.stack .tile.is-stacked {
  cursor: pointer;
}

/* ---------------------------------------------------------------------------
 * Button: dark grey outline, fills dark grey on hover. Used for "Get in
 * touch" and for sending the contact form.
 * ------------------------------------------------------------------------- */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 18px;
  border: var(--line);
  border-radius: var(--radius-small);
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  font-size: 1rem;                /* 16px everywhere */
  font-weight: 500;
  line-height: 1.3;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 150ms ease-out, color 150ms ease-out;
}

.button:hover {
  background: var(--stroke);
  color: var(--paper);
}

.button[disabled] {
  cursor: progress;
  opacity: 0.6;
}

/* ---------------------------------------------------------------------------
 * Footer: a hairline across the full width above and below, links on the
 * left, the mark on the right.
 * ------------------------------------------------------------------------- */
.site-footer {
  border-top: var(--line);
  border-bottom: var(--line);
}

.site-footer .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-top: 20px;
  /* More room at the bottom on a phone, so the last links are not left
     under Safari's floating toolbar at the end of the page
     (Felix, 2026-11-13). */
  padding-bottom: 60px;
}

/* On a phone the four links do not fit in one line beside the mark: two
   columns, Impressum and Datenschutz above, Danke and Kontakt below. */
.site-footer ul {
  display: grid;
  grid-template-columns: auto auto;
  gap: 0 24px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-footer a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: 1rem;
  font-weight: 500;
  text-decoration: none;
}

.site-footer a:hover {
  text-decoration: underline;
}

.site-footer .mark {
  display: block;
  width: 34px;
  height: 34px;
  flex: none;
}

/* ---------------------------------------------------------------------------
 * The sheet: Impressum, Datenschutz, Danke and the contact form. On its own
 * page it sits under the header on white; opened from the start page it sits
 * in the same place inside a full-screen white overlay (the "Schürze"), so
 * both look the same and both are as wide as the start page.
 * ------------------------------------------------------------------------- */
.sheet-area {
  padding-bottom: 48px;
}

.sheet {
  position: relative;
  min-height: 60vh;
  padding: var(--pad-x);
  border: var(--line);
  border-radius: var(--radius);
}

.sheet h1 {
  padding-right: 56px;            /* room for the close button beside it */
  font-size: 1.875rem;
  font-weight: 500;
  line-height: 1.3;
}

.sheet h2 {
  margin-top: 32px;
  font-size: 1.25rem;
  font-weight: 500;
  line-height: 1.3;
}

.sheet h2 + p,
.sheet h2 + address {
  margin-top: 4px;
}

.sheet address {
  font-style: normal;
}

.sheet .stand {
  margin-top: 8px;
  font-size: 0.875rem;
}

.sheet .body {
  max-width: 40em;
}

/* First paragraph under the title (no inline styles: the CSP forbids them). */
.sheet .lead {
  margin-top: 24px;
}

/* Round close button, top right: Felix's close_btn.svg (2026-11-13), drawn
   in the outline colour; it fills on hover like the buttons. */
.close {
  position: absolute;
  top: 16px;
  right: 16px;
  display: block;
  width: 48px;
  height: 48px;
  border-radius: 50%;             /* so the focus ring is round too */
}

.close svg {
  display: block;
  width: 48px;
  height: 48px;
  fill: none;
  stroke: var(--stroke);
  stroke-width: 1;
  stroke-linecap: round;
}

.close svg circle {
  fill: var(--paper);
  transition: fill 150ms ease-out;
}

.close:hover svg circle {
  fill: var(--stroke);
}

.close:hover svg path {
  stroke: var(--paper);
}

/* The overlay itself: full screen, white, scrolls on its own. */
dialog.apron {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: var(--paper);
  color: var(--ink);
  overflow-y: auto;
}

dialog.apron::backdrop {
  background: var(--paper);
}

/* ---------------------------------------------------------------------------
 * Contact form.
 * ------------------------------------------------------------------------- */
.form {
  display: grid;
  gap: 20px;
  max-width: 32rem;
  margin-top: 24px;
}

.field {
  display: grid;
  gap: 6px;
}

.field label {
  font-weight: 500;
}

.field .hint {
  font-weight: 400;
}

.field input,
.field select,
.field textarea {
  width: 100%;
  min-height: 48px;
  padding: 10px 12px;
  border: var(--line);
  border-radius: var(--radius-small);
  background: var(--paper);
  color: var(--ink);
  font: inherit;
}

.field textarea {
  min-height: 10rem;
  resize: vertical;
}

/* The trap for bots: off screen, out of the tab order, ignored by readers. */
.trap {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form .note {
  font-size: 1rem;
}

.form .button {
  justify-self: start;
}

.form-error {
  padding: 12px 16px;
  border: 1.5px solid var(--stroke);
  border-radius: var(--radius-small);
}

.form-error[hidden] {
  display: none;
}

/* ---------------------------------------------------------------------------
 * Narrow screens only: with JavaScript the covered titles shrink and move up
 * a little, so the strips can be lower and more of the card being read fits
 * on the screen (Felix, 2026-11-13: "if it helps"). site.js sets .js on
 * <html> and .is-stacked on a card as soon as the next one comes close.
 * Without JavaScript the strips stay high enough for the full-size title.
 * ------------------------------------------------------------------------- */
@media (max-width: 1023px) {
  html.js {
    --strip: 50px;
  }

  .js .stack .tile > h2 {
    transform-origin: 0 0;
    transition: transform 160ms ease-out;
  }

  .js .stack .tile.is-stacked > h2 {
    transform: translateY(-6px) scale(0.8);
  }
}

/* ---------------------------------------------------------------------------
 * Tablet and up: the desktop type sizes, more room inside the tiles.
 * ------------------------------------------------------------------------- */
@media (min-width: 720px) {
  :root {
    --gap: 24px;
    --pad-top: 32px;
    --pad-bottom: 32px;
    --pad-x: 40px;
    --edge: 40px;
    --stack-top: 24px;
    --strip: 76px;
  }

  body {
    font-size: 1.25rem;           /* 20px */
  }

  .site-header {
    padding-top: 40px;
    padding-bottom: 24px;
  }

  .site-header .logo img {
    height: 48px;
  }

  .tile {
    gap: 24px;
  }

  .tile h1,
  .sheet h1 {
    font-size: 2.25rem;           /* 36px */
  }

  .tile h2 {
    font-size: 1.5rem;            /* 24px */
  }

  .tile--hero .body {
    font-size: 1.5rem;            /* 24px */
    max-width: 30em;              /* 720px: the hero lines break as drawn */
  }

  .tile--season h2 {
    font-size: 1.25rem;           /* 20px */
  }

  .tile--season .body {
    font-size: 1rem;              /* 16px */
  }

  .site-footer .wrap {
    padding-top: 43px;
    padding-bottom: 43px;
  }

  .site-footer ul {
    display: flex;
    flex-wrap: wrap;
    gap: 0 40px;
  }

  .sheet {
    padding: 48px;
  }

  .close {
    top: 40px;
    right: 40px;
  }
}

@media (min-width: 720px) and (max-width: 1023px) {
  html.js {
    --strip: 64px;
  }
}

/* ---------------------------------------------------------------------------
 * Desktop: 960 px, two columns. The hero spans both; below it the stack runs
 * down the left column (608 px) and Hochsaison stands in the right one
 * (328 px), staying beside the stack while it scrolls.
 * ------------------------------------------------------------------------- */
@media (min-width: 1024px) {
  :root {
    --edge: 32px;                 /* 1024 px is the narrowest that fits 960 */
    /* Measured on the wireframe: the title starts 44 px below the top edge,
       the last line ends 41 px above the bottom edge. */
    --pad-top: 38px;
    --pad-bottom: 36px;
    --pad-x: 48px;
    --strip: 86px;                /* wireframe 2: the title in full size */
  }

  .site-header {
    padding-top: 47px;
    padding-bottom: 26px;
  }

  .stack {
    --runout: 144px;              /* doubled (Felix, 2026-11-13) */
    position: relative;           /* the season track hangs from it */
    padding-right: calc(var(--side) + var(--gap));
  }

  /* The right-hand column: as tall as the whole stack, so Hochsaison can
     stay at its top while the stack scrolls past. */
  .season-track {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: var(--side);
  }

  /* The track is out of the flow here, so Immer in der Tasche is the first
     card in it and starts level with Hochsaison. */
  .stack > .tile--pocket {
    margin-top: 0;
  }

  .tile--season {
    position: sticky;
    top: var(--stack-top);
  }

  /* Immer in der Tasche is as tall as Hochsaison beside it (site.js
     measures, --pair-height). */
  .tile--pocket {
    min-height: var(--pair-height, 0);
  }

  /* The hero sits a little higher in its frame than the other tiles. */
  .tile--hero {
    --pad-top: 34px;
    --pad-bottom: 46px;
    gap: 21px;
  }

  /* Felix's corrections of 2026-11-13, desktop only (the phone looked right):
     the language box 2 px higher with its label in the middle, also on
     hover; the label of every button in the middle; and room to the right
     of the arrow in the contact form's select. */
  .lang a span {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 27px;
    /* Measured, not guessed: with the box centred, capitals of this typeface
       sit 1.25 px high, and D and E have different side bearings. The
       padding moves each label to the exact middle of its box. */
    padding: 2.5px 7px 0;
    line-height: 1;
  }

  .lang a[hreflang="de"] span {
    padding-left: 6px;
    padding-right: 8px;
  }

  .lang a[hreflang="en"] span {
    padding-left: 6.5px;
    padding-right: 7.5px;
  }

  /* The label sat 2 px high (cap height to baseline, measured); 4 px more
     padding at the top than at the bottom puts it in the middle. */
  .button {
    padding: 4px 18px 0;
  }

  /* The browser's own arrow cannot be moved, so the select draws its own:
     the same thin stroke as the close button, 16 px from the right edge. */
  .field select {
    appearance: none;
    -webkit-appearance: none;
    padding-right: 44px;
    background-image: url("/assets/select-arrow.svg");
    background-repeat: no-repeat;
    background-position: right 16px center;
    background-size: 12px 7px;
  }
}

/* Desktop windows lower than 960 px: six strips of 86 px and the last card
   do not fit, and the last card would cover the titles under it. With
   JavaScript the covered titles shrink here too, and the strips get lower. */
@media (min-width: 1024px) and (max-height: 959px) {
  html.js {
    --strip: 60px;
  }

  .js .stack .tile > h2 {
    transform-origin: 0 0;
    transition: transform 160ms ease-out;
  }

  .js .stack .tile.is-stacked > h2 {
    transform: translateY(-16px) scale(0.8);
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
  }
}
