/* ==========================================================================
   Instalatér Šomek — single stylesheet
   No build step: this file is served exactly as written.
   Order: tokens, reset, base, layout, components, page blocks, utilities.
   ========================================================================== */

/* --- Design tokens ------------------------------------------------------ */

:root {
  /* Blue/white palette. Every text-on-background pair below is >= 4.5:1. */
  --blue-900: #0a3252;
  --blue-700: #0b5fa5;
  --blue-600: #1273c4;
  --blue-100: #e8f1f8;
  --blue-050: #f4f9fc;

  --ink: #17242f;
  --ink-muted: #526376;
  --line: #d7e2ec;
  --white: #ffffff;

  --ok-700: #1c6b3f;
  --ok-100: #e6f4ec;
  --warn-700: #8a5a00;
  --warn-100: #fdf3dc;
  --err-700: #a32020;
  --err-100: #fbeaea;

  /* System stack: no web font request, and full Czech diacritic coverage
     on every platform. A custom face would have to be self-hosted with the
     latin-ext subset — never loaded from Google Fonts. */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;

  --container: 68rem;
  --gap: 1.5rem;
  --radius: 10px;
  --shadow: 0 1px 2px rgba(10, 50, 82, .06), 0 6px 20px rgba(10, 50, 82, .07);

  /* Height of the fixed mobile call bar; 0 on desktop. Measured, not guessed:
     the bar is its .btn (3rem min-height, but 54px once padding and the 2px
     border are counted) plus .6rem padding top and bottom plus the 1px border,
     which comes to ~74px. It was 4.25rem — 6px short, and the only thing
     hiding that was the footer's own bottom padding. Keep this in step with
     .callbar, since it is what reserves room at the end of the page. */
  --callbar: 4.65rem;
}

/* --- Reset -------------------------------------------------------------- */

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

body, h1, h2, h3, h4, p, ul, ol, li, figure, blockquote, dl, dd {
  margin: 0;
  padding: 0;
}

ul, ol { list-style: none; }

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

input, textarea, select, button { font: inherit; color: inherit; }

/* --- Base --------------------------------------------------------------- */

html {
  font-family: var(--font);
  font-size: 100%;
  line-height: 1.6;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

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

body {
  margin: 0;
  background: var(--white);
  color: var(--ink);
  font-size: 1.0625rem;
  padding-bottom: calc(var(--callbar) + env(safe-area-inset-bottom, 0px));
}

h1, h2, h3 {
  line-height: 1.2;
  color: var(--blue-900);
  font-weight: 700;
  text-wrap: balance;
}

h1 { font-size: clamp(1.85rem, 1.3rem + 2.4vw, 2.9rem); }
h2 { font-size: clamp(1.45rem, 1.15rem + 1.3vw, 2rem); }
h3 { font-size: 1.2rem; }

/* The reset zeroes heading margins because in most blocks the spacing comes
   from a wrapper (.section__head, .service__head, a footer column). Where a
   heading stands directly in the flow instead — the columns on kontakt — these
   sibling selectors have to supply it. Inside .prose the larger rules below
   still win, since a class outranks an element selector.

   `* + h2` and not `p + h2`: what comes before a heading is not always a
   paragraph. The QR image is followed by a heading and had no gap at all
   while this said `p`, and the next block added here would have hit the same
   thing. Headings with no preceding sibling — every one inside the wrappers
   named above — are unaffected, so this stays as narrow as it reads. */
* + h2 { margin-top: 2.5rem; }
h2 + p,
h2 + .contact-list { margin-top: .75rem; }

p { text-wrap: pretty; }

a { color: var(--blue-700); }
a:hover { color: var(--blue-900); }

:focus-visible {
  outline: 3px solid var(--blue-600);
  outline-offset: 2px;
  border-radius: 3px;
}

/* --- Layout ------------------------------------------------------------- */

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

.section { padding-block: clamp(2.5rem, 1.5rem + 4vw, 4.5rem); }
.section--tint { background: var(--blue-050); }
.section__head { max-width: 42rem; margin-bottom: 2rem; }
.section__head p { color: var(--ink-muted); margin-top: .6rem; }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--blue-900);
  color: var(--white);
  padding: .75rem 1.25rem;
  border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus { left: 0; color: var(--white); }

/* --- Header ------------------------------------------------------------- */

.site-header {
  border-bottom: 1px solid var(--line);
  background: var(--white);
}

.site-header__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .75rem 1.5rem;
  padding-block: .9rem;
}

.brand {
  font-weight: 700;
  font-size: 1.15rem;
  color: var(--blue-900);
  text-decoration: none;
  line-height: 1.25;
  margin-right: auto;
}
.brand span {
  display: block;
  font-size: .78rem;
  font-weight: 500;
  color: var(--ink-muted);
  letter-spacing: .02em;
  text-transform: uppercase;
}

/* Four links fit on one or two rows at any width, so the nav needs no
   toggle and therefore no JavaScript. */
.nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem 1.35rem;
}
.nav a {
  display: block;
  padding: .5rem 0;
  color: var(--ink);
  text-decoration: none;
  font-weight: 600;
  border-bottom: 2px solid transparent;
}
.nav a:hover { color: var(--blue-700); border-bottom-color: var(--blue-100); }
.nav a[aria-current="page"] {
  color: var(--blue-700);
  border-bottom-color: var(--blue-700);
}

.site-header .btn { display: none; }
@media (min-width: 56em) {
  .site-header .btn { display: inline-flex; }
}

/* --- Buttons ------------------------------------------------------------ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .55rem;
  min-height: 3rem;
  padding: .7rem 1.4rem;
  border: 2px solid transparent;
  border-radius: var(--radius);
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  text-align: center;
}

.btn--primary { background: var(--blue-700); color: var(--white); }
.btn--primary:hover { background: var(--blue-900); color: var(--white); }

.btn--ghost {
  background: var(--white);
  color: var(--blue-700);
  border-color: var(--line);
}
.btn--ghost:hover { border-color: var(--blue-700); color: var(--blue-900); }

.btn--lg { min-height: 3.5rem; font-size: 1.075rem; padding-inline: 1.75rem; }
.btn--block { width: 100%; }

/* A column on phones, a row from 40em up. Stacked, the buttons stretch to the
   full width of the row and so come out identical whatever their labels say;
   side by side they were sized by their text, and a pair like "Zavolat 608 542
   877" + "Napsat poptávku" wrapped onto two lines at two different widths,
   which reads as two unrelated controls rather than a choice between two.

   Equal width by stretching, not by a shared flex-basis: a basis wide enough
   for the longest label today is one the owner can outgrow with a single word,
   and it would silently go back to being unequal. 40em is where the card grids
   become multi-column — below it the page is one column, so a full-width button
   is what everything else in that column already does.

   flex-wrap stays for the row case: it is the fallback if a third button is
   ever added, or if a label grows past what one line can hold. */
.btn-row {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  gap: .85rem;
  margin-top: 1.75rem;
}
@media (min-width: 40em) {
  .btn-row { flex-direction: row; }
}

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

.hero {
  background: linear-gradient(180deg, var(--blue-050), var(--white));
  border-bottom: 1px solid var(--line);
  padding-block: clamp(2.5rem, 1.5rem + 5vw, 5rem);
}
.hero__inner { max-width: 44rem; }
/* One column on phones (illustration first would push the h1 below the
   fold, so it is ordered after the text and only sits beside it on wide
   viewports). */
.hero__grid {
  display: grid;
  gap: var(--gap);
  align-items: center;
}
@media (min-width: 56em) {
  .hero__grid { grid-template-columns: minmax(0, 1fr) 16rem; }
}
.hero__media {
  justify-self: center;
  width: 100%;
  max-width: 14rem;
  height: auto;
}
.hero__lead {
  font-size: clamp(1.05rem, 1rem + .5vw, 1.25rem);
  color: var(--ink-muted);
  margin-top: 1.1rem;
}
.hero__meta {
  margin-top: 1.75rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
  color: var(--ink-muted);
  font-size: .95rem;
}

/* --- Card grids --------------------------------------------------------- */

.grid {
  display: grid;
  gap: var(--gap);
  grid-template-columns: 1fr;
}
@media (min-width: 40em) { .grid--3 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 60em) { .grid--3 { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 40em) { .grid--2 { grid-template-columns: repeat(2, 1fr); } }

.card {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.5rem;
  box-shadow: var(--shadow);
}
.card h3 { margin-bottom: .5rem; }
.card p { color: var(--ink-muted); }
.card ul { margin-top: .85rem; }

/* Checklist used inside cards and on the services page. */
.ticks li {
  position: relative;
  padding-left: 1.6rem;
  margin-bottom: .4rem;
  color: var(--ink-muted);
}
.ticks li::before {
  content: "";
  position: absolute;
  left: .1rem;
  top: .55em;
  width: .7rem;
  height: .38rem;
  border-left: 2.5px solid var(--blue-600);
  border-bottom: 2.5px solid var(--blue-600);
  transform: rotate(-45deg);
}

/* --- Service blocks (sluzby.html) --------------------------------------- */

.service {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(1.5rem, 1rem + 2vw, 2.25rem);
  margin-bottom: var(--gap);
  box-shadow: var(--shadow);
}
.service > p { color: var(--ink-muted); margin-top: .6rem; }
/* Heading + illustration on one row. The illustrations are drawings on a
   white ground, so they sit on the card with no frame of their own. */
.service__head {
  display: flex;
  align-items: center;
  gap: 1.25rem;
}
/* min-width:0 lets the heading shrink below its content width; break-word
   is the backstop that stops "Vodoinstalatérství" — one unbreakable
   18-character word — from pushing the card sideways. */
.service__head h2 {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: break-word;
}
/* Hidden below 30em: the illustration is decorative (alt=""), and at that
   width it squeezes the heading enough to force a break mid-word. */
.service__head img {
  display: none;
  flex: 0 0 auto;
  object-fit: contain;
}
@media (min-width: 30em) {
  .service__head img { display: block; width: 4.5rem; height: 4.5rem; }
}
@media (min-width: 46em) {
  .service__head img { width: 6rem; height: 6rem; }
}
.service__cols {
  display: grid;
  gap: 1.5rem;
  margin-top: 1.5rem;
}
@media (min-width: 46em) { .service__cols { grid-template-columns: 1fr 1fr; } }
.service__cols h4 {
  font-size: .82rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--blue-900);
  margin-bottom: .6rem;
}

/* --- Gallery ------------------------------------------------------------ */

/* Three columns at full width instead of four, so each photo is about a
   third larger. The min() is load-bearing: a bare minmax(20rem, 1fr)
   keeps its 20rem floor on a 320px phone and pushes the page sideways. */
.gallery {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr));
}
.gallery a {
  display: block;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--blue-050);
  text-decoration: none;
}
.gallery img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.gallery figcaption {
  padding: .7rem .9rem .85rem;
  font-size: .92rem;
  color: var(--ink-muted);
  background: var(--white);
}

/* Lightbox: created by js/main.js. Without JavaScript the gallery links
   simply open the image file, which is why they are real <a href>. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Top padding clears the close button, which is otherwise on top of the
     photo once the viewport is narrow. */
  padding: 5rem 1rem 1.5rem;
  background: rgba(10, 50, 82, .93);
}
.lightbox[hidden] { display: none; }
.lightbox > div {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-height: 100%;
  overflow-y: auto;
}
.lightbox img {
  max-width: 100%;
  max-height: 75vh;
  border-radius: var(--radius);
  background: var(--white);
}
/* dvh tracks the retracting mobile URL bar; vh above is the fallback. */
@supports (height: 100dvh) {
  .lightbox img { max-height: 70dvh; }
}
/* Still 4rem of clearance: the button is 3rem tall and sits 1rem down. */
@media (min-width: 46em) {
  .lightbox { padding: 4.5rem 1.5rem 1.5rem; }
}
.lightbox__caption {
  color: var(--white);
  text-align: center;
  margin-top: 1rem;
  font-size: .95rem;
}
/* The cross is an SVG, not a × glyph: flex centring aligns a replaced
   element exactly, whereas a glyph is centred on its own bearings and
   drifts from font to font. padding:0 keeps the box itself symmetrical. */
.lightbox__close {
  position: absolute;
  top: 1rem;
  right: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  padding: 0;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .5);
  background: transparent;
  color: var(--white);
  cursor: pointer;
}
.lightbox__close svg { display: block; }
.lightbox__close:hover { background: rgba(255, 255, 255, .15); }

/* --- Forms -------------------------------------------------------------- */

.form { max-width: 34rem; }
/* Tighter than it looks: .field__msg below the control reserves a line of its
   own, so the gap between two fields is this plus that. */
.field { margin-bottom: .75rem; }
.field label {
  display: block;
  font-weight: 600;
  margin-bottom: .25rem;
}
/* The requirement for each field. It sits outside <label> on purpose: inside,
   it would become part of the field's accessible name and a screen reader
   would read it out on every pass through the form. It is wired up with
   aria-describedby instead. */
.field .hint {
  display: block;
  font-weight: 400;
  font-size: .88rem;
  color: var(--ink-muted);
  margin-bottom: .4rem;
}
.field input,
.field textarea {
  width: 100%;
  min-height: 3rem;
  padding: .7rem .85rem;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--white);
}
.field textarea { min-height: 8.5rem; resize: vertical; }
.field input:hover, .field textarea:hover { border-color: #b9c9d8; }
.field input:focus, .field textarea:focus {
  border-color: var(--blue-600);
  outline: 3px solid rgba(18, 115, 196, .25);
  outline-offset: 0;
}

/* Example values. Lighter than .hint so they read as "not typed yet", but
   still 4.5:1 on white — the browser default is well under that. */
.field input::placeholder,
.field textarea::placeholder {
  color: #66788c;
  opacity: 1;                 /* Firefox dims placeholders by default */
}

/* Validation state. js/main.js sets these on the .field wrapper only after
   the visitor has left the field once, so nothing turns red while it is
   still being typed into. Colour is never the only signal: a red border
   always comes with .field__msg, and the message is what gets announced. */
.field.is-ok input,
.field.is-ok textarea { border-color: var(--ok-700); }

.field.is-err input,
.field.is-err textarea {
  border-color: var(--err-700);
  background: var(--err-100);
}
.field.is-err input:focus,
.field.is-err textarea:focus {
  border-color: var(--err-700);
  outline-color: rgba(163, 32, 32, .25);
}

/* The message slot is always in the flow, empty most of the time, and holds a
   line's worth of height whether or not it says anything. That blank line is
   not spacing — it is what stops the submit button moving.

   Hiding it instead used to swallow the first click on "Odeslat poptávku":
   pressing the mouse down blurs the textarea, the message appears, everything
   below it shifts down, and the mouse comes back up somewhere that is no
   longer the button, so no click event is ever dispatched. With the line
   reserved a one-line message shifts nothing at all, and the two-line wrap a
   narrow screen can produce moves the button by less than half its own height,
   so a press aimed at its middle still lands on it.

   An empty <p> contributes nothing to the accessible name of the field that
   points at it with aria-describedby, so leaving it in place costs nothing
   there either. */
.field__msg {
  margin-top: .4rem;
  min-height: 1.35rem;
  font-size: .9rem;
  font-weight: 600;
  color: var(--err-700);
}

.field__count {
  margin-top: .4rem;
  font-size: .85rem;
  color: var(--ink-muted);
}
.field__count[hidden] { display: none; }
.field__count.is-ok { color: var(--ok-700); }
.field__count.is-err { color: var(--err-700); font-weight: 600; }

.req { color: var(--err-700); }

/* Honeypot. Hidden from sight and from assistive tech, but present in the
   DOM for bots to fill in. Never use a captcha here — see CLAUDE.md. */
.hp {
  position: absolute !important;
  left: -9999px !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form__note {
  font-size: .92rem;
  color: var(--ink-muted);
  margin-top: 1rem;
}

/* --- Send result dialog ------------------------------------------------- */

/* A real <dialog> opened with showModal(). The top layer puts it above
   everything — including the fixed .callbar — without a z-index, and the
   browser handles focus trapping, Escape and the dimmed backdrop.

   The <dialog> itself is only a transparent frame; .modal__panel carries the
   background and all the padding. That is what makes a click on the dark
   surround land on the <dialog> element, which is how js/main.js recognises
   "clicked outside" without measuring anything.

   Centring is the UA stylesheet's doing: dialog:modal is fixed with the block
   and inline insets at 0, so margin:auto centres it on both axes. */
.modal {
  margin: auto;
  padding: 0;
  border: 0;
  background: transparent;
  width: 100%;
  max-width: min(30rem, calc(100vw - 2rem));
  color: var(--ink);
}
.modal::backdrop { background: rgba(10, 50, 82, .55); }

/* The global prefers-reduced-motion rule near the top of this file cuts this
   to 0.01ms, so it needs no media query of its own. */
.modal[open] { animation: modal-in .18s ease-out; }
@keyframes modal-in {
  from { opacity: 0; transform: translateY(.75rem); }
  to   { opacity: 1; transform: none; }
}

.modal__panel {
  background: var(--white);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: clamp(1.5rem, 1rem + 2vw, 2rem);
  text-align: center;
}
/* margin-top:0 is load-bearing. The panel manages its own rhythm — the gap
   above the heading is the 1rem under .modal__icon — but the icon is an
   element sibling, so the `* + h2` rule near the top of this file would
   otherwise apply its 2.5rem here and push the title away from its icon. */
.modal__panel h2 { margin-top: 0; margin-bottom: .6rem; }
.modal__panel p { margin-bottom: .6rem; }
.modal__panel .btn { margin-top: 1rem; }

/* Countdown to the automatic close, drawn across the close button. The bar
   fills from the left as the time runs out, so it reads the way a progress
   bar does; js/main.js scales it frame by frame rather than handing it to a
   CSS animation, because the prefers-reduced-motion rule near the top of this
   file would collapse an animation to 0.01ms and the bar would jump to full
   while the dialog stayed put. A progress indicator is in any case the kind of
   movement that rule is not aimed at: it reports state rather than decorating.

   overflow:hidden clips the bar to the button's rounded corners; the label is
   positioned so that it paints over the bar rather than under it. */
.modal__close { position: relative; overflow: hidden; }
.modal__bar {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 100%;
  transform: scaleX(0);
  transform-origin: left center;
  background: rgba(255, 255, 255, .22);
  pointer-events: none;
}
.modal__close-text { position: relative; }
.modal__secs { margin-left: .4rem; font-weight: 400; opacity: .85; }

/* Green or red disc behind the glyph. It is the first thing read, so the
   colour is from the palette rather than chosen for the dialog — but it only
   repeats what the heading already says, never replaces it. */
.modal__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3.25rem;
  height: 3.25rem;
  margin: 0 auto 1rem;
  border-radius: 50%;
}
.modal__icon svg { display: block; }
.modal__icon--ok  { background: var(--ok-100);  color: var(--ok-700); }
.modal__icon--err { background: var(--err-100); color: var(--err-700); }

/* --- Contact details ---------------------------------------------------- */

.contact-list { margin-bottom: 2rem; }
.contact-list li {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem 1rem;
  padding: .85rem 0;
  border-bottom: 1px solid var(--line);
}
/* Fixed width, not a minimum. With a minimum the column is only as wide as each
   row's own label, so the two long ones ("SMS a WhatsApp", "Kalkulace na místě")
   push their value further right than every other row and the values stop
   forming a column. 11rem clears the longest label on any of the three lists
   ("Kalkulace na místě", ~154px here) with enough room left that a system font
   other than this machine's does not overflow it; a longer label wraps to two
   lines inside the column rather than widening it.
   The width is shared rather than measured per list on purpose — the two lists
   on kontakt.html sit under each other, and a column sized to each one's own
   longest label would leave them misaligned with one another. */
.contact-list dt, .contact-list .k {
  flex: 0 0 11rem;
  font-weight: 600;
  color: var(--ink-muted);
}
/* The basis is what decides when the row stops being two columns: below
   11 + 1 + 13rem of room the value no longer fits beside the label and wraps
   under it, full width. Without it the value would instead be squeezed into
   whatever the fixed label column left over — about 100px on a 320px phone,
   which breaks "PO–PÁ 7:00–19:00" across lines. */
.contact-list .v {
  flex: 1 1 13rem;
  font-size: 1.05rem;
}
/* Two of the rows hold a list of options rather than a single value: the two
   ways to write instead of calling, and the two ways to pay. Run together they
   read as one sentence and the separator ("—", "·", a comma) has to carry the
   distinction; on their own lines the line break carries it and the separators
   go away. It also pulls the two links apart, so a thumb cannot catch both. */
.contact-list .v--rows {
  display: flex;
  flex-direction: column;
  gap: .4rem;
}
/* Every link in this list is the whole value it sits in, so none of them gets
   the WCAG 2.2 2.5.8 inline exception and the 24px minimum applies. They were
   22px — and one of them is the phone number, the thing the whole page exists
   to get tapped. The padding takes them to 30px without moving the text.
   inline-block is what makes vertical padding count towards the box. */
.contact-list a {
  display: inline-block;
  padding-block: .25rem;
  font-weight: 700;
  text-decoration: none;
}
.contact-list a:hover { text-decoration: underline; }

/* QR s vizitkou. Tady je podstatná spodní hranice, ne horní: kód má 69 modulů
   plus klidovou zónu, dohromady 77. Aby ho foťák přečetl z displeje, musí na
   jeden modul zbýt zhruba 3 px — 15rem (240 px) dává ~3,1 px na modul, takže
   je to nejmenší velikost, která ještě spolehlivě funguje. Nezmenšovat.
   Bílé pozadí a klidová zóna jsou uvnitř SVG, protože bez nich se kód nenačte
   vůbec; rámeček je jen proto, aby bílý čtverec nesplýval se stránkou. */
.qr {
  width: 100%;
  max-width: 15rem;
  margin-top: 1rem;
  /* The reset makes images display:block, so auto side margins centre it under
     the left-aligned text above. */
  margin-inline: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

/* Mapa oblasti. Dokud js/map.js nenamountuje Leaflet (a bez JS napořád) drží
   .map jen náhradní odkaz s obrázkem, který si nese vlastní rámeček — proto
   dostane .map rozměry a pozadí až se třídou .map--live. */
.map--live {
  height: clamp(18rem, 12rem + 18vw, 26rem);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  /* Leaflet stacks its own layers from 200 (tiles) to 1000 (controls), and
     .leaflet-container is position: relative with no z-index of its own — so it
     forms no stacking context and every one of those numbers is compared against
     the rest of the page, including the fixed .callbar at 90. The map therefore
     paints over the call bar on mobile. These two lines make the container a
     stacking context: the layer order inside the map is unchanged, but outward
     it is a single layer at z-index 0, which the call bar sits above.
     Do not raise the call bar's z-index instead — it would have to clear 1000
     to win, and then it would also cover things that are meant to be above it. */
  position: relative;
  z-index: 0;
}
/* Na kontaktu mapa navazuje na odstavec, ne na .section__head. Byl to inline
   style; jako třída to zvládne jeden sdílený partial pro obě stránky. */
.map--spaced { margin-top: 1.5rem; }
/* Leaflet dává kontejneru vlastní šedé pozadí. Bez podkladových dlaždic je
   vidět celé, takže musí být z palety webu — a leaflet.css se proto načítá
   dřív než tento soubor. */
.map--live.leaflet-container {
  background: var(--blue-050);
  font: inherit;
}
.map--live .leaflet-control-attribution {
  font-size: .75rem;
  background: rgba(255, 255, 255, .85);
}

.map-link {
  display: block;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  text-decoration: none;
}
.map-link img { width: 100%; }
.map-link span {
  display: block;
  padding: .8rem 1rem;
  background: var(--white);
  font-weight: 600;
}

/* --- Prose (legal pages) ------------------------------------------------ */

/* break-word, not anywhere: it only kicks in for a run that genuinely cannot
   fit, and unlike `anywhere` it leaves min-content sizing alone. The legal
   pages are full of things with no break opportunity — e-mail addresses, bare
   domains, and the rows of … in the withdrawal form, which overflowed their
   paragraph into the gutter at 320px. */
.prose { max-width: 44rem; overflow-wrap: break-word; }
.prose h2 { margin-top: 2.5rem; margin-bottom: .75rem; }
.prose h3 { margin-top: 1.75rem; margin-bottom: .5rem; }
.prose p, .prose ul, .prose ol { margin-bottom: 1rem; }
.prose ul, .prose ol { padding-left: 1.35rem; }
.prose ul li { list-style: disc; margin-bottom: .4rem; }
.prose ol li { list-style: decimal; margin-bottom: .4rem; }
/* A link that is the whole list item is a standalone target, so WCAG 2.2
   2.5.8 applies and the 24px minimum is real — the list on 404.html was 22px
   tall. The inline exception covers links inside a sentence, which is why this
   is :only-child and not every .prose link. Same fix as .site-footer li a;
   scoped to .prose so it cannot reach the nav, whose padding is already
   larger. */
.prose li > a:only-child {
  display: inline-block;
  padding-block: .3rem;
}
.prose .lede {
  font-size: 1.15rem;
  color: var(--ink-muted);
  margin-bottom: 2rem;
}
.prose .updated { color: var(--ink-muted); font-size: .93rem; }
.prose table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 1.5rem;
}
.prose th, .prose td {
  text-align: left;
  padding: .65rem .75rem;
  border: 1px solid var(--line);
  vertical-align: top;
}
.prose th { background: var(--blue-050); }
.table-scroll { overflow-x: auto; }

.callout {
  background: var(--blue-050);
  border-left: 4px solid var(--blue-600);
  padding: 1.15rem 1.35rem;
  border-radius: 0 var(--radius) var(--radius) 0;
  margin-bottom: 1.5rem;
}
.callout p:last-child { margin-bottom: 0; }

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

.site-footer {
  background: var(--blue-900);
  color: #c9dcec;
  padding-block: 3rem 2.5rem;
  margin-top: 3rem;
}
.site-footer a { color: var(--white); }
.site-footer h2 {
  color: var(--white);
  font-size: 1.05rem;
  margin-bottom: .85rem;
}
.site-footer__cols {
  display: grid;
  gap: 2rem;
  grid-template-columns: 1fr;
}
@media (min-width: 46em) {
  .site-footer__cols { grid-template-columns: 1.3fr 1fr 1fr; }
}
.site-footer li { margin-bottom: .4rem; }
/* Footer links are standalone targets, not links inside a sentence, so
   WCAG 2.2 2.5.8 applies: the padding takes them from 22px to 30px. */
.site-footer li a {
  display: inline-block;
  padding-block: .25rem;
}

/* Statutory identification block — § 435 NOZ. Required on every page. */
.legal-id {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid rgba(255, 255, 255, .18);
  font-size: .9rem;
  line-height: 1.7;
}
.legal-id p { margin-bottom: .35rem; }

/* On the two pages with a slim footer (404, poptavka-odeslana) the block is the
   whole footer, so it needs no rule above it. Was an inline style on those pages;
   it is a class so that partials/legal-id.html can stay a single source. */
.legal-id--flush {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

/* Copyright and the developer credit — the last line of the page, and the only
   part of the footer that is not about the trade. Dimmer and smaller than the
   § 435 block above it, which is what the visitor is actually there to read;
   #a8c2d8 on --blue-900 is 7:1, so it stays past the 4.5:1 floor at this size.
   The two halves sit side by side on a wide screen and stack on a narrow one. */
.colophon {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem 1.5rem;
  margin-top: 1.5rem;
  padding-top: 1.1rem;
  border-top: 1px solid rgba(255, 255, 255, .12);
  font-size: .8rem;
  color: #a8c2d8;
}

/* --- Fixed call bar (mobile only) --------------------------------------- */

/* env() keeps the bar clear of the iPhone home indicator; the matching
   term in body's padding-bottom stops the bar covering the last line of
   the footer. Both fall back to 0px where the inset is unsupported. */
.callbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 90;
  display: flex;
  gap: .6rem;
  padding: .6rem .75rem;
  padding-bottom: calc(.6rem + env(safe-area-inset-bottom, 0px));
  background: var(--white);
  border-top: 1px solid var(--line);
  box-shadow: 0 -4px 16px rgba(10, 50, 82, .1);
}
.callbar .btn { flex: 1; min-height: 3.05rem; padding-inline: .75rem; }

@media (min-width: 56em) {
  .callbar { display: none; }
  body { padding-bottom: 0; }
}

/* --- Utilities ---------------------------------------------------------- */

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.muted { color: var(--ink-muted); }
.mt-0 { margin-top: 0; }
.center { text-align: center; }

/* Placeholder marker. Every occurrence is a value awaiting real data —
   see PLACEHOLDERS.md. Delete this rule once the site goes live so any
   leftover marker stops being invisible. */
.ph {
  background: var(--warn-100);
  color: var(--warn-700);
  padding: 0 .2em;
  border-radius: 3px;
  font-weight: 600;
  white-space: nowrap;
}
