/* Obesitologia — main stylesheet.
 *
 * Order: fonts, tokens, reset, links and focus, typography, page furniture,
 * components.
 *
 * Sources of truth:
 *   docs/design/README.md       — token table and type scale
 *   docs/design/DESIGN_SPEC.md  — component dimensions and states
 * Values come from there, not from the prototype and not from screenshots.
 */

/* ==========================================================================
   1. Fonts
   Self-hosted: no request leaves for a foreign server (DECISIONS.md,
   "Bez paska zgód na pierwszej wizycie"). Files in static/fonts/, licence and
   provenance in static/fonts/README.md.

   The latin / latin-ext split matters: the Polish ą ć ę ł ń ó ś ź ż live in
   latin-ext. The browser fetches the second file only when a page actually
   uses those characters.
   ========================================================================== */

/* IBM Plex Sans — variable, one file covering weights 400–600. */
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/ibm-plex-sans-var-latin.b2c9031d9fd6.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/ibm-plex-sans-var-latin-ext.f0e2925b3fa6.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Source Serif 4 — variable, optical size axis 8–60 driven by font size. */
@font-face {
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/source-serif-4-var-latin.e9192c2fe0a3.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/source-serif-4-var-latin-ext.33ba75072ebc.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Mono — static, one file per weight. */
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-400-latin.79936b18df9f.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-400-latin-ext.8450d5944de2.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-500-latin.b1c8a895f5fd.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-500-latin-ext.fa8d8838b94a.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Roboto Medium is not a typeface of the project. Google's brand guidelines ask
   for it on a custom "Sign in with Google" button, and .google-button is the one
   rule that names it, so no other screen downloads it (static/fonts/README.md). */
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/roboto-500-latin.55cfeb5304f0.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/roboto-500-latin-ext.3a24e2af79df.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ==========================================================================
   2. Tokens
   ========================================================================== */

:root {
  /* --- Surfaces and text --- */
  --bg: #F4F8FB;                /* page background */
  --panel-bg: #EDF2F7;          /* page background under /panel/ and /moje/ (panel mockup) */
  --surface: #FFFFFF;           /* cards, panels, header */
  --line: #E3EDF4;              /* card borders, list separators */
  --line-strong: #C7D8E5;       /* section borders, table rules */
  /* The border of a control, and only of a control. Success criterion
     1.4.11 asks 3:1 of anything that carries the boundary or the state of a
     user interface component, and the two tokens above give 1.19 and 1.46 on
     white — an edge nobody with low vision can find. They stay where they are:
     1.4.11 is about controls, not about the rules of a table or the edge of a
     card, and darkening those would repaint the system rather than fix it.
     Hue 206 is the hue of --line-strong; the saturation drops from 37% to 24%
     because at half the lightness the same saturation reads as a second blue
     next to --accent, and the resting border of a field must not compete with
     its focused one. Measured against every light ground in this block, the
     lowest is 3.05 on --grid; DESIGN_SPEC.md section 42.12 carries the table
     and apps/core/tests/test_a11y.py recomputes it with the suite. */
  --line-control: #6587A1;      /* control borders, control states (3:1) */
  --text: #10202C;              /* body text */
  --muted: #5A6B78;             /* secondary text, descriptions */
  --hero: #E4EFF8;              /* hero section background (with the --grid mesh) */
  --band: #E6F0F8;              /* highlighted band background */
  --hl: #EAF3FA;                /* row highlight, read-only field background */
  --grid: #DDE8F1;              /* 36x36 px hero mesh lines */
  --deep: #0B2E48;              /* footer background */
  --on-deep: #DCEAF6;           /* text on the footer */

  /* --- Accent --- */
  --accent: #0E5C93;            /* primary buttons, links, mono labels */
  --tint: #DCEAF6;              /* secondary button and badge background */
  --tint-hover: #CFE2F3;        /* secondary button under the pointer (panel mockup) */
  --on-accent: #FFFFFF;         /* text on the accent */

  /* --- Category colours ---
     Five specialist types. A pair: text colour --cN, badge background --tN.
     The same colour means the same type in the catalogue, the knowledge base
     and on the SEO pages. No new colours get added (DECISIONS.md). */
  --c1: #0E5C93;                /* doctors */
  --t1: #DDEAF7;
  --c2: #3F4F9A;                /* dietitians */
  --t2: #E5E8F6;
  --c3: #8F5D12;                /* psychodietitians */
  --t3: #FAECD2;
  --c4: #7A4E8C;                /* psychologists */
  --t4: #EEE4F3;
  --c5: #A9452F;                /* physiotherapists */
  --t5: #FBE3DD;

  /* --- States ---
     The first three are darkened. Each of them is a text colour on a light
     ground of its own making — the 12% tint of section 6 and the message
     grounds of section 14 — and at their original lightness none of those pairs
     reached 4.5:1: ok 3.52, warn 2.83, alert 4.18 on the badge. The hue is
     untouched, only the lightness moved, so this is not the new colour
     DECISIONS.md rules out. DESIGN_SPEC.md section 42 carries the measurements
     and apps/core/tests/test_a11y.py holds every pair to the threshold.

     --star is the one that stayed. It is 2.24 on white, which no darkening
     fixes without turning the brand gold brown; the rating it stands for is
     carried in text beside it either way — the number on the card, "N z 5" by
     the control. Section 42 records that too.

     Green left the palette on 24.09 (Konrad: "zmienmy zielony na niebieski").
     --ok, --band and the dietitian pair took the blue values of the panel
     mockup; --slot and --slot-bg name the free-slot pair on their own, so a
     later change to success colours does not repaint the free slots with it. */
  --ok: #0E5C93;                /* success, profile visible */
  --slot: #0E5C93;              /* free slot: text and edge */
  --slot-bg: #E6F0F8;           /* free slot: ground, the term band on a card */
  --warn: #8A5A10;              /* warning, payment needs attention */
  --alert: #B23A30;             /* validation error, cancelled appointment */
  --star: #DFA31C;              /* rating star */

  /* --- Footer scale ---
     Footer column headings use --s1. The rest are the ranges of the
     calculator scales and the article tables, never an accent. --s2, the
     healthy range, is blue since the palette lost its green (24.09). */
  --s1: #8FBEDD;
  --s2: #2E6DA4;
  --s3: #E3C558;
  --s4: #E79B45;
  --s5: #DB7247;
  --s6: #C4453A;

  /* --- Below: tokens outside the README.md table ---
     The names are ours; the values come from the "Geometria" and "Siatka
     i odstępy" sections of README.md and from DESIGN_SPEC.md. */

  --font-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-serif: "Source Serif 4", Georgia, "Times New Roman", serif;
  --font-mono: "IBM Plex Mono", ui-monospace, Consolas, monospace;

  /* --accent darkened by roughly 8% of its lightness — the hover background of
     the primary button, and the only state in DESIGN_SPEC.md section 1 given
     as a change rather than as a value. */
  --accent-dark: #0D5587;

  --radius-card: 16px;          /* cards, photos, panels (panel mockup) */
  --radius-listing: 20px;       /* the specialist card of the catalogue list (panel mockup) */
  --radius-control: 6px;        /* buttons and form fields */
  --radius-badge: 3px;          /* mono badges */
  --radius-small: 4px;          /* checkboxes, skeleton rectangles */
  --radius-pill: 20px;          /* pills: filters, categories, city tags */
  --radius-tile: 12px;          /* tiles: the calculator switch, choice tiles (panel mockup) */
  --radius-button: 12px;        /* buttons (panel mockup) */
  --radius-field: 10px;         /* text fields, selects, the card field (panel mockup) */

  --content-max: 1180px;        /* maximum width of the content column */
  --gutter: 40px;               /* horizontal section margin */
  --header-h: 72px;             /* header height, fixed across all states */

  /* The only shadow in the system — the bars stuck to the bottom of the phone
     screen (the profile's and the booking's). It was the save bar's, and keeps
     its name; the editing screens no longer have a bar. Everywhere else
     hierarchy is carried by borders and background. */
  --shadow-savebar: 0 -6px 20px rgba(11, 46, 72, 0.08);
  /* The focus halo of a field in the panel mockup. It rides on top of the
     2 px outline of section 4 rather than replacing it: 15% of --accent
     alone is not a focus indicator anybody with low vision can find. */
  --ring: 0 0 0 3px rgba(14, 92, 147, 0.15);
  /* Cards of the panel mockup: a breath of shadow at rest and a lift under
     the pointer for the ones that are links. The 1 px --line edge stays —
     on the white bands of the public pages this shadow alone vanishes. */
  --shadow-card: 0 1px 2px rgba(11, 46, 72, 0.06);
  --shadow-raised: 0 8px 24px rgba(11, 46, 72, 0.1);
}

/* --- The phone ------------------------------------------------------------ */

/* Two of the tokens above are the whole responsive step for horizontal space,
   and this is the only place either of them changes. DESIGN_SPEC.md section 15
   gives the phone a horizontal margin of 18 px and a 56 px header; the header
   is 58 here, which is what the frames draw (1f, 1h, 11a, 11d, 11e all open
   with a 58 px bar) and what .booking__topbar already carries.
   Section 41 of that document records the two pixels and why the frames win.

   Redefining --gutter rather than writing a phone padding per band is what
   makes "brak przewijania poziomego przy 360 px" a property of the sheet
   instead of a list of exceptions: every band that already paid attention to
   the token gets the narrower margin for free, and a band that hardcodes 40 px
   is now a bug this rule cannot hide.

   The breakpoint is 900 px, the one the rest of the sheet uses. The inventory
   in README.md has two design widths, 1440 and 390, and no tablet frame at
   all; the full phone treatment of 11b already lands at 900 px, so a third
   state would be a width nobody drew. Narrower thresholds are added only
   where measured content forces them — section 28 has the one precedent, at
   600 px, and says how it was measured. */
@media (max-width: 900px) {
  :root {
    --gutter: 18px;
    --header-h: 58px;
  }
}

/* ==========================================================================
   3. Reset
   Only what is necessary. No normalisation just in case.
   ========================================================================== */

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 400;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* A flex column, so the footer sits at the bottom of the window on short
     pages too (.site-main in section 6 takes the rest). */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

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

/* The <picture> wrapper around profile photos is format negotiation, not
   layout: display: contents hands its img straight to the parent flex or
   grid, so every rule written for the bare img keeps holding. */
picture {
  display: contents;
}

/* The browser's own stylesheet puts `margin: 1em 40px` on a figure, which is
   an indent nothing in this design asks for — measured, it took the panel
   screenshots 80 px narrower than their column and out of line with the
   sentence beneath them. */
figure {
  margin: 0;
}

/* Form controls inherit the typeface — otherwise the browser picks its own. */
button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

/* ==========================================================================
   4. Links and focus
   ========================================================================== */

a {
  color: var(--accent);
}

a:hover {
  color: var(--text);
}

/* A link keeps the browser's underline only inside running text, where colour
   alone would not tell it from its neighbours (WCAG 1.4.1). A link drawn as a
   button, a tile, a tab, a menu item or a list row is recognised by its shape,
   and an underline under its label reads as a broken button. The panel mockup
   of 24.09 draws them all bare.

   Two groups. The first is shaped: it never shows an underline. The second
   stands alone as an action beside the content ("Zmień", "Znajdź specjalistę
   →", a crumb): bare at rest, underlined on hover, the way .home-signals__link
   and .breadcrumbs a already behave. The knowledge base has its own grouped
   reset in section 26, and .btn carries its own in section 7.

   Left out on purpose: .account-deletion__action and .visit-row__withdraw.
   Their frames draw underlined text in --text for a quiet way out, and that
   underline is the design, not a leftover.

   The :focus-visible ring below is untouched by all of this. */
.skip-link,
.calc-switch__item,
.path-card,
.panel-nav__item,
.profile-nav__item,
.calendar__arrow,
.calendar__today,
.cal-tile,
.cal-tile__x,
.cal-tile__no,
.thread-list__tab,
.thread-row,
.thread-list__new {
  text-decoration: none;
}

.calculator__crumb,
.calc-result__aside,
.calculator__faq-cta,
.auth__back,
.auth-field__aside,
.panel-status__action,
.calendar__feed-link,
.account-row__action {
  text-decoration: none;
}

.calculator__crumb:hover,
.calc-result__aside:hover,
.calculator__faq-cta:hover,
.auth__back:hover,
.auth-field__aside:hover,
.panel-status__action:hover,
.calendar__feed-link:hover,
.account-row__action:hover {
  text-decoration: underline;
}

/* Inside the panel every link is bare, the way the mockup of 24.09 draws it:
   the menu, the tiles, the rows and the actions are all recognised by their
   shape or their place. :where() keeps this at the weight of a plain `a`, so
   every class above — the hover underlines, the three quiet exits that are
   underlined by design — still wins. A link inside running text in the panel
   carries .inline-link and gets its underline back (WCAG 1.4.1). */
:where(.page-panel .site-main) a {
  text-decoration: none;
}

.inline-link {
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* A visible focus ring is what keyboard navigation depends on. The outline
   follows DESIGN_SPEC.md section 1, "Stany". */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The ring above is --accent, and --accent on --deep is 1.98:1 — on the three
   bands standing on the navy the ring is there and nobody sees it. Those three
   are the only rules in this sheet setting `background: var(--deep)`, and
   apps/core/tests/test_a11y.py keeps a fourth from arriving without its ring.
   --on-deep is 11.44 against the same ground. On /dla-specjalistow/ the price
   shares the patient data's navy band (section 28). */
.site-footer :focus-visible,
.home-origin :focus-visible,
.offer-dark :focus-visible {
  outline-color: var(--on-deep);
}

/* ==========================================================================
   5. Typography
   Scale from README.md. Desktop values (1440 px design); the phone step-down is
   described in DESIGN_SPEC.md section 15.
   ========================================================================== */

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0;
  font-family: var(--font-serif);
  font-weight: 500;
  color: var(--text);
  text-wrap: pretty;
}

p {
  margin: 0;
  text-wrap: pretty;
}

h1,
.t-h1 {
  font-family: var(--font-serif);
  font-size: 52px;
  line-height: 1.08;
  font-weight: 500;
  letter-spacing: -0.02em;
}

h2,
.t-h2 {
  font-family: var(--font-serif);
  font-size: 42px;
  line-height: 1.12;
  font-weight: 500;
  letter-spacing: -0.015em;
}

h3,
.t-h3 {
  font-family: var(--font-serif);
  font-size: 26px;
  line-height: 1.2;
  font-weight: 500;
}

/* Card title — 21 px by default, 24 px in a highlighted card. */
.t-card-title {
  font-family: var(--font-serif);
  font-size: 21px;
  line-height: 1.25;
  font-weight: 500;
  color: var(--text);
}

.t-card-title--lg {
  font-size: 24px;
}

/* Secondary text — descriptions under a name, card meta. */
.t-secondary {
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.55;
  font-weight: 400;
  color: var(--muted);
}

/* Overline — footer column headings, section kickers, badges. */
.t-overline {
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* Design notes — technical commentary beside the content. */
.t-note {
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.65;
  font-weight: 400;
  color: var(--muted);
}

/* ==========================================================================
   6. Page furniture: skeleton, header, footer
   Anatomy: DESIGN_SPEC.md sections 7 and 8; visual reference: frame 10g.
   The phone view is a 58 px header and a single-column footer.
   ========================================================================== */

/* The body is a flex column (section 3), so the main part takes the height
   the footer does not. */
.site-main {
  flex: 1;
}

/* Skip link. It does not exist for the mouse; for the keyboard it is the first
   item on the page — without it, reaching the content means tabbing through six
   bar items on every view. */
.skip-link {
  position: absolute;
  left: -9999px;
  z-index: 10;
  padding: 10px 18px;
  font-size: 15px;
  background: var(--surface);
  border: 1px solid var(--accent);
  border-radius: var(--radius-control);
}

.skip-link:focus {
  top: 10px;
  left: var(--gutter);
}

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

/* The borders sit outside the bar height: 5 + 72 + 1 = 78 px in total.
   The bar height is fixed across all three states — the header must not jump
   on sign-in (frame 10g, note 01). */
.site-header {
  background: var(--surface);
  border-top: 5px solid var(--accent);
  border-bottom: 1px solid var(--line);
}

.site-header__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--header-h);
  padding: 0 var(--gutter);
}

.site-logo {
  display: flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
}

/* Colour on the mark, not on the link — otherwise a:hover would drag it to --text. */
.site-logo__mark {
  flex: none;
  color: var(--accent);
}

.site-logo__name {
  font-size: 18px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text);
}

.site-logo__tld {
  font-weight: 400;
  color: var(--muted);
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 30px;
  white-space: nowrap;
}

/* The four items of note 02, wrapped so the phone can hide the row without
   hiding the end of the bar with it. One flex child where there were four,
   carrying the gap the bar had — the desktop bar is unchanged. */
.site-nav__links {
  display: flex;
  align-items: center;
  gap: 30px;
}

/* The hamburger and its panel exist only below the breakpoint; section 6's
   phone block turns them on. Hidden rather than absent so the four links have
   one template and two render sites (core/partials/nav_items.html). */
.site-menu {
  display: none;
}

.site-nav__link {
  font-size: 15px;
  color: var(--text);
  text-decoration: none;
}

/* Bar items are already --text, so the global a:hover would be invisible here. */
.site-nav__link:hover {
  color: var(--accent);
}

.site-nav__link--active {
  color: var(--accent);
  border-bottom: 2px solid var(--accent);
  padding-bottom: 2px;
}

/* --- Menu under the avatar ----------------------------------------------- */

.user-menu {
  position: relative;
}

/* Pill with initials, first name and chevron — the way frame 10g shows it. */
.user-menu__toggle {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 12px 6px 8px;
  background: var(--tint);
  border: 1px solid var(--accent);
  border-radius: var(--radius-control);
  list-style: none;
  cursor: pointer;
}

/* Without this the browser draws its own triangle next to our chevron. */
.user-menu__toggle::-webkit-details-marker {
  display: none;
}

.user-menu__avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--t4);
  color: var(--c4);
  font-size: 12px;
  font-weight: 500;
}

.user-menu__name {
  font-size: 14px;
  color: var(--text);
}

/* What the control is, for anybody who cannot see that it is a menu. It is read
   after the name rather than instead of it: an aria-label would have replaced
   the visible words, and somebody driving the page by voice says what they can
   see.

   Hidden the way the star inputs of section 24 and the reason radios of section
   30 are hidden — clipped, not display: none, so it stays in the reading order.
   Not off-screen with a negative offset either: that is the skip link's trick
   and it belongs to something that comes back on focus. */
.user-menu__action {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The chevron does not rotate when opened — frame 10g shows it pointing down
   in both expanded menus, and the system has no transition animations. */
.user-menu__chevron {
  flex: none;
  color: var(--accent);
}

/* The menu hangs outside the bar layout. If it joined the flow, the header
   would change height when expanded — which is explicitly forbidden.
   The 16 px offset is (72 - 42) / 2 from the bottom of the pill to the bar edge
   plus that 1 px edge, so the menu starts exactly under the header. */
.user-menu__panel {
  position: absolute;
  top: calc(100% + 16px);
  right: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  min-width: 240px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  overflow: hidden;
}

.user-menu__head {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--line);
}

.user-menu__identity {
  font-size: 15px;
  color: var(--text);
}

/* A slot htmx can aim an out-of-band swap at. The switch on 2l changes the
   profile status without reloading the page, and an out-of-band swap can only
   replace an element that is already there — so the slot exists even while
   empty. display: contents keeps it out of the layout entirely, so the menu is
   spaced by its own children as before. */
.user-menu__slot {
  display: contents;
}

.user-menu__meta {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  color: var(--muted);
}

.user-menu__dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ok);
}

/* The other half of note 05 beside 10g. The strip is there because a hidden
   profile is the usual answer to "why is nobody writing to me", so the state
   that matters is the one the frame does not draw. --warn rather than --muted:
   section 14 gives that colour to "something needs your attention", and this is
   the same sentence in one dot. */
.user-menu__dot--hidden {
  background: var(--warn);
}

.user-menu__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 11px 16px;
  font-size: 15px;
  color: var(--text);
  text-decoration: none;
}

.user-menu__item:hover {
  color: var(--text);
  background: var(--hl);
}

/* Separator before "Wyloguj się" (DESIGN_SPEC.md section 7).

   This item is a button inside a form, because signing out is a POST. The
   declarations below the border drop what a button brings with it — its own
   font, its centred text, its border and its background — so that it renders
   as the anchor the frame draws. */
.user-menu__item--signout {
  width: 100%;
  font-family: inherit;
  text-align: left;
  color: var(--muted);
  background: none;
  border-top: 1px solid var(--line);
  border-right: 0;
  border-bottom: 0;
  border-left: 0;
  cursor: pointer;
}

.user-menu__item--signout:hover {
  color: var(--muted);
}

/* The form is scaffolding for the method, not a box: display: contents keeps it
   out of the layout, so the button stays a direct child of the panel and the
   menu has no seam where the last item begins. */
.user-menu__signout {
  display: contents;
}

/* The unread badge, in the two places the frames draw it: the menu under the
   avatar (10g) and the panel menu (2i, 8d). One component —
   components/unread_badge.html — because it is one number.

   The wrapper is always in the document, empty and hidden at zero, so that
   hx-swap-oob has an id to aim at when the count comes back. That is why the
   pill is a child rather than the element itself. */
.unread {
  display: inline-flex;
}

.unread[hidden] {
  display: none;
}

.unread__pill {
  padding: 1px 7px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--on-accent);
  background: var(--accent);
  border-radius: 10px;
}

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

.site-footer {
  background: var(--deep);
  padding-bottom: 28px;
}

/* Band of category colours: five equal segments, the first element of the
   footer, with no gap. It repeats the colour system of the type pills — closing
   the page with what the reader used to read it (DECISIONS.md "Jedna stopka
   wszędzie"). */
.site-footer__band {
  display: flex;
  height: 5px;
}

.site-footer__band > span {
  flex: 1;
}

.site-footer__band > span:nth-child(1) {
  background: var(--c1);
}

.site-footer__band > span:nth-child(2) {
  background: var(--c2);
}

.site-footer__band > span:nth-child(3) {
  background: var(--c3);
}

.site-footer__band > span:nth-child(4) {
  background: var(--c4);
}

.site-footer__band > span:nth-child(5) {
  background: var(--c5);
}

.site-footer__inner {
  padding: 56px var(--gutter) 0;
}

.site-footer__columns {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--gutter);
  max-width: var(--content-max);
  margin: 0 auto;
}

.site-footer__col {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.site-footer__col-title {
  color: var(--s1);
}

.site-footer__links {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.site-footer__link {
  font-size: 15px;
  color: var(--on-deep);
  text-decoration: none;
}

.site-footer__link:hover {
  color: var(--surface);
}

/* The separator is given in DESIGN_SPEC.md section 8 as white at 16%, and it
   stays translucent rather than becoming a token — it has to work on --deep no
   matter how dark the surface is. Two uses, both on --deep and both in this
   one notation: this edge, and the rule over each block of the dark band
   (.home-origin__block). Grep for rgba(255, 255, 255, 0.16) and you have
   them all. */
.site-footer__bottom {
  display: flex;
  justify-content: space-between;
  max-width: var(--content-max);
  margin: 44px auto 0;
  padding-top: 20px;
  font-size: 14px;
  color: var(--on-deep);
  border-top: 1px solid rgba(255, 255, 255, 0.16);
}

/* --- Phone (1f, 1g, 1h, 11a-11e): bar of 58 px, footer in one column ------ */

/* The bar keeps its anatomy and loses its middle. Its height comes from
   --header-h, which section 2 redefines here, so "stała we wszystkich stanach"
   from DESIGN_SPEC.md section 7 still holds — the phone has its own constant,
   not a variable one.

   The footer is section 8's own phone column: padding 30px 18px, bottom
   padding 24, five columns in one and the links of each wrapping into rows
   rather than standing in one (note 04 beside 1f: "linki zawijane w wiersze,
   nie pięć kolumn"). */
@media (max-width: 900px) {
  /* The panels below hang off the bar, so the bar is their containing block. */
  .site-header {
    position: relative;
  }

  .site-logo__name {
    font-size: 16px;
  }

  .site-nav {
    gap: 12px;
  }

  .site-nav__links {
    display: none;
  }

  .site-menu {
    display: block;
  }

  /* 22 x 1.5 px with 4 px between, the frames' own hamburger, inside a 44 px
     target — DESIGN_SPEC.md section 15, "Minimalny cel dotknięcia 44x44 px".
     The bars stay 22 px wide; only the area that answers a finger grows, the
     same trick .toggle uses in section 7. */
  .site-menu__toggle {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    min-width: 44px;
    min-height: 44px;
    list-style: none;
    cursor: pointer;
  }

  .site-menu__toggle::-webkit-details-marker {
    display: none;
  }

  .site-menu__bars {
    display: flex;
    flex-direction: column;
    gap: 4px;
    width: 22px;
  }

  .site-menu__bars > span {
    height: 1.5px;
    background: var(--text);
  }

  /* Full width under the bar rather than a 240 px card at the right edge: at
     390 px a hanging card would have nowhere to hang. No shadow — the only
     shadow in the project is --shadow-savebar, so the edge does the work. */
  .site-menu__panel {
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    z-index: 5;
    display: flex;
    flex-direction: column;
    padding: 6px var(--gutter) 14px;
    background: var(--surface);
    border-bottom: 1px solid var(--line);
  }

  .site-menu__panel .site-nav__link {
    display: flex;
    align-items: center;
    min-height: 44px;
    font-size: 16px;
  }

  /* An active item in the panel cannot be underlined the way it is in the bar:
     a 2 px edge under a full-width row reads as a divider. The accent alone
     carries it, which is what aria-current carries too. */
  .site-menu__panel .site-nav__link--active {
    padding-bottom: 0;
    border-bottom: 0;
  }

  /* Frames 11d and 11e: a bare 32 px circle, not the pill. The name goes —
     beside a hamburger and a logo there is no room for it, and the initials
     are what the frame draws. The target is 44 px again. */
  /* The panel is anchored to the bar, not to the pill, so the pill stops being
     its containing block. */
  .user-menu {
    position: static;
  }

  .user-menu__toggle {
    justify-content: flex-end;
    min-width: 44px;
    min-height: 44px;
    padding: 0;
    background: none;
    border: 0;
  }

  .user-menu__avatar {
    width: 32px;
    height: 32px;
    font-size: 13px;
  }

  .user-menu__name {
    display: none;
  }

  .user-menu__panel {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    min-width: 0;
    border-radius: 0;
    border-left: 0;
    border-right: 0;
    border-top: 0;
  }

  .user-menu__item {
    min-height: 44px;
    padding-left: var(--gutter);
    padding-right: var(--gutter);
  }

  /* --- Footer (section 8, phone column) --- */

  .site-footer {
    padding-bottom: 24px;
  }

  .site-footer__inner {
    padding: 30px var(--gutter) 0;
  }

  .site-footer__columns {
    grid-template-columns: minmax(0, 1fr);
    gap: 22px;
  }

  .site-footer__links {
    flex-direction: row;
    flex-wrap: wrap;
    gap: 8px 18px;
  }

  .site-footer__link {
    font-size: 14px;
  }

  .site-footer__bottom {
    flex-direction: column;
    gap: 4px;
    margin-top: 26px;
    padding-top: 16px;
    font-size: 12px;
  }
}

/* Below 360 px, a measured width (DESIGN_SPEC.md section 41.1): the signed-out
   bar holds the 153 px logo, the 103 px "Zaloguj się" button, the 12 px gap and
   the 44 px menu target inside 2 × 18 px of gutter, which is 348 px with
   nothing between the logo and the button, and at 320 px it ran 10 px past
   the screen. The mark steps aside — the name still says whose site it is —
   and the button comes 4 px closer to its label on each side, which leaves
   11 px between the two at 320 px. */
@media (max-width: 359px) {
  .site-logo__mark {
    display: none;
  }

  .site-nav .btn--tertiary {
    padding-right: 12px;
    padding-left: 12px;
  }
}

/* ==========================================================================
   7. Components
   Anatomy: DESIGN_SPEC.md sections 1, 2, 4, 5, 6, 9, 13 and 14. Markup for each
   of them sits in templates/components/; every variant and state is on display
   at /style/komponenty/.
   Two rules from "Czego nie ma w systemie" run through the whole section: the
   shadows are the save bar, the field's focus halo and the cards' own, and
   nothing here transitions or animates.
   The phone step-down (full-width primary button, 48 px fields) is section 15.
   ========================================================================== */

/* --- Button -------------------------------------------------------------- */

/* Four variants, three sizes, radius 6 px, no shadow. On hover only the
   background changes — section 1 rules out transforms.

   Padding rides on two custom properties: a size class sets the geometry, a
   variant class sets the colour, and the bordered variants correct the
   geometry, because the border has to be paid for out of the padding. */
.btn {
  --btn-py: 13px;
  --btn-px: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--btn-py) var(--btn-px);
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.2;
  border: 0;
  border-radius: var(--radius-button);
  text-decoration: none;
  cursor: pointer;
}

.btn--s {
  --btn-py: 10px;
  --btn-px: 18px;
  font-size: 15px;
}

.btn--m {
  --btn-py: 13px;
  --btn-px: 24px;
  font-size: 15px;
}

.btn--l {
  --btn-py: 16px;
  --btn-px: 30px;
  font-size: 16px;
}

/* Every variant restates its colour for :hover. A button is often an <a>, and
   the global a:hover from section 4 outranks a single class — without this the
   label would turn --text mid-hover. */
.btn--primary {
  color: var(--on-accent);
  background: var(--accent);
}

.btn--primary:hover {
  color: var(--on-accent);
  background: var(--accent-dark);
}

/* The panel mockup of 24.09 draws the secondary button filled, not edged:
   --accent on --tint (5.77:1), a shade deeper under the pointer. With no
   border it needs no geometry of its own and stands as tall as a primary. */
.btn--secondary {
  color: var(--accent);
  background: var(--tint);
}

.btn--secondary:hover {
  color: var(--accent);
  background: var(--tint-hover);
}

/* The tertiary variant appears only as "Zaloguj się" in the header (section 1),
   always at size S, so it carries that geometry itself instead of composing
   with a size class. */
.btn--tertiary {
  padding: 9px 16px;
  font-size: 14px;
  color: var(--accent);
  background: var(--tint);
  border: 1px solid var(--accent);
}

.btn--tertiary:hover {
  color: var(--accent);
  background: var(--surface);
}

/* Quiet is always the reversing action — "Anuluj", "Nie chcę oceniać",
   "Zrobię to później" — and never the main one (section 1). */
.btn--quiet {
  color: var(--muted);
  background: none;
}

.btn--quiet:hover {
  color: var(--text);
  background: var(--hl);
}

/* The confirmed deletion of a row and "Wyjdź bez zapisu" — the mockup of
   26.09. Only where the click throws something away, and only after a
   question: never the first thing somebody presses. */
.btn--danger {
  color: var(--on-accent);
  background: var(--alert);
}

.btn--danger:hover {
  color: var(--on-accent);
  background: color-mix(in srgb, var(--alert) 85%, black);
}

/* Section 1: a disabled button loses its colour, not its size. aria-disabled
   covers the ones shaped as links, which cannot carry the disabled attribute.
   While a request is running the button is disabled too, its label swapped for
   "Zapisywanie…" — and because it must not change width, the caller reserves
   the wider of the two labels with min-width. */
.btn:disabled,
.btn[aria-disabled="true"] {
  color: var(--muted);
  background: var(--line-strong);
  border-color: var(--line-strong);
  cursor: not-allowed;
}

.btn-group {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Section 1: more air when the second action is the quiet one. */
.btn-group--quiet {
  gap: 16px;
}

/* A button with a shorter word for the phone carries both (label_phone in
   components/button.html) and this hides the one the width does not want.
   Copy cannot come out of a stylesheet, so the swap has to be a swap of two
   spans; the alternative is a Polish string in a script, which
   static/js/README.md rules out, or the same button rendered twice. */
.btn__label--phone {
  display: none;
}

/* --- Form field ---------------------------------------------------------- */

/* Anatomy: section 2. The control is reached through element selectors inside
   .field rather than through a class of its own, because Django renders the
   widget and this project carries no widget-tweaks — a class would have to be
   set in Python, on every field of every form, for ever. */
.field {
  display: flex;
  flex-direction: column;
}

.field__label {
  margin-bottom: 7px;
  font-size: 15px;
  line-height: 1.4;
  color: var(--text);
}

.field input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.field select,
.field textarea {
  width: 100%;
  height: 44px;
  padding: 0 14px;
  font-family: var(--font-sans);
  font-size: 16px;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-field);
}

.field ::placeholder {
  color: var(--muted);
  opacity: 1;
}

/* A file picker is not a text box. Inside the frame of one, the browser's own
   "Wybierz plik" button sat crooked against a border it did not fill. The
   input stays native — it has to work without a script, and a hidden input
   with a drawn button cannot show which file was chosen (DESIGN_SPEC.md
   §27.4) — so only its button is dressed: the quiet button of §45.3.6 at the
   height of the fields beside it. The border is --line-control rather than the
   section's --line-strong, because a control needs 3:1 against its
   background, the rule every field on the form already follows. */
.field input[type="file"] {
  max-width: 100%;
  font-family: var(--font-sans);
  font-size: 15px;
  color: var(--muted);
}

.field input[type="file"]::file-selector-button {
  height: 44px;
  margin-right: 12px;
  padding: 0 16px;
  font-family: var(--font-sans);
  font-size: 15px;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-field);
  cursor: pointer;
}

.field input[type="file"]:hover::file-selector-button {
  border-color: var(--accent);
}

/* Both field families draw the select arrow themselves. The native control
   sets its own inner indent and arrow per browser, so "Wybierz miasto" stood a
   few pixels right of the text in the input beside it. The arrow is a file in
   static/img/ui/ and not a data URI, because a data URI of SVG carries an
   xmlns address this sheet may not contain; its stroke is the value of --muted,
   which a custom property cannot reach into an image. */
.field select,
.auth-field select {
  padding-right: 40px;
  appearance: none;
  background-image: url("../img/ui/select-arrow.558e1b225f53.svg");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px 8px;
}

/* Section 2: on focus the colour of the border changes and the thickness does
   not — a thicker border would shift the text by a pixel. The ring around the
   field comes from :focus-visible in section 4. */
.field input:focus,
.field select:focus,
.field textarea:focus {
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.field textarea {
  height: auto;
  min-height: 120px;
  padding: 12px 14px;
  line-height: 1.6;
  resize: vertical;
}

/* A disabled field keeps the full contrast of its text — --text, not --muted.
   That is the whole point of the row in section 2: the PWZ number on 2h and the
   e-mail address on 9d are disabled precisely so that they can be read and
   checked. Greying them out would defeat the reason they are shown. */
.field input:disabled,
.field select:disabled,
.field textarea:disabled,
.field input[readonly],
.field textarea[readonly] {
  color: var(--text);
  background: var(--hl);
  border-color: var(--line);
}

/* Read-only differs from disabled by the cursor alone (section 2). */
.field input:disabled,
.field select:disabled,
.field textarea:disabled {
  cursor: not-allowed;
}

.field--error input,
.field--error select,
.field--error textarea {
  border-color: var(--alert);
}

.field__hint {
  margin-top: 6px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
}

.field__error {
  margin-top: 6px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--alert);
}

/* Checkbox and radio, section 2. Drawn rather than left to the browser: the
   native control differs between Windows, macOS and Linux, and the specimen on
   /style/komponenty/ has to be the same everywhere. */
.choice {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 15px;
  line-height: 1.4;
  color: var(--muted);
  cursor: pointer;
}

/* The label darkens once the box is ticked, so a filled form reads as a list
   of answers rather than a list of questions. */
.choice:has(input:checked) {
  color: var(--text);
}

.choice input {
  flex: none;
  width: 16px;
  height: 16px;
  margin: 2px 0 0;
  appearance: none;
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-small);
  cursor: pointer;
}

.choice input[type="radio"] {
  border-radius: 50%;
}

.choice input:checked {
  background: var(--accent);
  border-color: var(--accent);
}

/* The tick is two sides of a square turned on its corner, and the radio mark a
   dot — both drawn from --on-accent, so neither needs an image file or a data
   URI holding a copy of the token. */
.choice input[type="checkbox"]:checked::before {
  content: "";
  display: block;
  width: 4px;
  height: 8px;
  margin: 0 auto;
  border: solid var(--on-accent);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.choice input[type="radio"]:checked::before {
  content: "";
  display: block;
  width: 6px;
  height: 6px;
  margin: 4px auto;
  background: var(--on-accent);
  border-radius: 50%;
}

/* The content card of section 4 stood here and is gone. It had one parent in
   the whole repository — the component gallery — and every screen that needed
   a card grew its own instead: .specialist-card, .article-card, .kb-entry,
   .plan-card, .visit-row. templates/accounts/register_choice.html says out
   loud why it refused the shared one, and section 21 of DESIGN_SPEC.md writes
   its two cards up separately rather than bending section 4 around them.
   Section 4 keeps describing the card; nothing implements it, and a base class
   nobody derives from is a promise the sheet cannot keep. */

/* --- Pill ---------------------------------------------------------------- */

/* Section 5. Seven uses, one shape: radius 20 px, one line, never wrapping.
   The count belongs to the text after a "·" — "Warszawa · 86" — and is not a
   separate element.

   Colour goes through two custom properties so that the five category
   modifiers stay one line each, and so that the restatement for :hover, which
   the global a:hover would otherwise win, is needed only once. Section 5 gives
   the pill no hover state of its own. */
.pill {
  --pill-fg: var(--text);
  --pill-bg: transparent;
  display: inline-flex;
  align-items: center;
  padding: 8px 16px;
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.2;
  color: var(--pill-fg);
  background: var(--pill-bg);
  white-space: nowrap;
  border-radius: var(--radius-pill);
  text-decoration: none;
}

.pill:hover {
  color: var(--pill-fg);
}

.pill--tag {
  padding: 5px 11px;
  font-size: 13px;
}

.pill--filter {
  gap: 8px;
  padding: 7px 13px;
  font-size: 14px;
}

.pill--plain {
  --pill-fg: var(--text);
  --pill-bg: var(--surface);
  padding: 8px 15px;
  border: 1px solid var(--line-control);
}

.pill--active {
  --pill-fg: var(--on-accent);
  --pill-bg: var(--accent);
}

/* The five category colours are a system, not decoration (DECISIONS.md): the
   same colour means the same specialist type in the catalogue, in the
   knowledge base and on the SEO pages. No sixth pair gets added here. */
.pill--c1 {
  --pill-fg: var(--c1);
  --pill-bg: var(--t1);
}

.pill--c2 {
  --pill-fg: var(--c2);
  --pill-bg: var(--t2);
}

.pill--c3 {
  --pill-fg: var(--c3);
  --pill-bg: var(--t3);
}

.pill--c4 {
  --pill-fg: var(--c4);
  --pill-bg: var(--t4);
}

.pill--c5 {
  --pill-fg: var(--c5);
  --pill-bg: var(--t5);
}

/* Only applied filters carry a cross (section 5). Category pills and city tags
   are cleared by clicking them again, so a cross there would be a second way
   of doing the same thing. */
/* currentColor rather than --muted: the cross sits on the pill's own tint, and
   --muted on the lightest of the five (--t4) is 4.47:1 — under the threshold by
   a hair on one ground out of five, which is the kind of miss no eye catches and
   no reviewer disputes. Inheriting --pill-fg puts it between 5.17 and 5.79
   everywhere and removes the pair from the table altogether. */
.pill__remove {
  font-size: 15px;
  line-height: 1;
  color: currentColor;
  text-decoration: none;
}

/* --- Mono badge ---------------------------------------------------------- */

/* Section 6: invoice numbers, statuses in tables, frame ids in the prototype.
   Not a place for a "zweryfikowany" badge — DECISIONS.md rules that one out,
   because a PWZ number is a condition of the account existing at all. */
.badge {
  display: inline-block;
  padding: 3px 7px;
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.45;
  letter-spacing: 0.08em;
  color: var(--accent);
  background: var(--tint);
  border-radius: var(--radius-badge);
}

/* Section 6 asks for the text in the colour of the status on a 12% tint of it.
   There are no tokens for those three tints and they should not become any:
   they follow from --ok, --warn and --alert, and mixing them here keeps them
   from drifting away from their source the way three pasted hex values would. */
.badge--ok {
  color: var(--ok);
  background: color-mix(in srgb, var(--ok) 12%, var(--surface));
}

.badge--warn {
  color: var(--warn);
  background: color-mix(in srgb, var(--warn) 12%, var(--surface));
}

.badge--alert {
  color: var(--alert);
  background: color-mix(in srgb, var(--alert) 12%, var(--surface));
}

/* --- Empty state --------------------------------------------------------- */

/* Section 13, pattern: frame 1d. The copy names the next step and not the
   state — "Poszerz obszar do 50 km", never "Brak wyników" — which is why this
   component has a heading and an action rather than a sentence of apology. */
.empty {
  max-width: 520px;
  margin: 0 auto;
  padding: 64px 0;
  text-align: center;
}

.empty__title {
  font-family: var(--font-serif);
  font-size: 26px;
  line-height: 1.2;
  font-weight: 500;
  color: var(--text);
}

.empty__text {
  margin-top: 12px;
  font-size: 16px;
  line-height: 1.65;
  color: var(--muted);
}

.empty__actions {
  display: flex;
  justify-content: center;
  gap: 16px;
  margin-top: 24px;
}

/* --- Card footer --------------------------------------------------------- */

/* Section 9, and DECISIONS.md "Zapis profilu jest jawny": every card of an
   editing screen ends in its own "Zapisz", under its last field, and saves only
   itself. The date of the last save stands at the far end, and gives way to
   "Niezapisane zmiany" after the first change (static/js/panel.js). */
.card-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 20px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

/* The consequence of saving, over the button on a line of its own: "Zapis
   przeliczy wolne terminy na 60 dni w przód." */
.card-foot__note {
  flex-basis: 100%;
  margin: 0;
  font-size: 14px;
  color: var(--muted);
}

.card-foot__state {
  margin: 0 0 0 auto;
  font-size: 13px;
  color: var(--muted);
}

/* The dot is --warn: a change not saved yet needs attention and is not a fault. */
.card-foot__dirty {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text);
}

.card-foot__dirty::before {
  width: 6px;
  height: 6px;
  content: "";
  background: var(--warn);
  border-radius: 50%;
}

.card-foot [hidden] {
  display: none;
}

/* --- "Niezapisane zmiany" ------------------------------------------------ */

/* The one modal besides the account deletion's ("Czego nie ma w systemie"):
   asked before a changed card is left behind. */
.leave-dialog {
  width: min(440px, calc(100vw - 32px));
  padding: 24px;
  color: var(--text);
  background: var(--surface);
  border: 0;
  border-radius: var(--radius-card);
}

.leave-dialog::backdrop {
  background: rgb(16 32 44 / 35%);
}

.leave-dialog__title {
  margin: 0 0 8px;
  font-size: 18px;
  font-weight: 500;
}

.leave-dialog__text {
  margin: 0;
  font-size: 15px;
  color: var(--muted);
}

.leave-dialog__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 20px;
}

/* --- Loading skeleton ---------------------------------------------------- */

/* Section 3: the content replaced by --line rectangles with a 4 px radius, and
   no pulsing — the list comes back from the server in a few hundred
   milliseconds, so flicker would cost more than a still skeleton does.

   These are the primitives. The specialist-card skeleton of frame 11f is
   assembled from them and becomes the hx-indicator. */
.skeleton {
  display: flex;
  flex-direction: column;
  gap: 11px;
}

/* Both are spans, so they need the display — a placeholder is a rectangle, and
   an inline one would collapse to the height of its (absent) text. */
.skeleton__line,
.skeleton__block {
  display: block;
  background: var(--line);
  border-radius: var(--radius-small);
}

.skeleton__line {
  height: 15px;
}

.skeleton__line--title {
  height: 22px;
}

.skeleton__line--short {
  width: 38%;
}

.skeleton__line--medium {
  width: 56%;
}

.skeleton__line--long {
  width: 78%;
}

.skeleton__block {
  height: 40px;
}

/* The two shapes a photo takes in the catalogue: 84×104 on the list card
   (section 3), a 74 px circle in the compact one. */
.skeleton__block--photo {
  width: 84px;
  height: 104px;
  border-radius: var(--radius-control);
}

.skeleton__block--avatar {
  width: 74px;
  height: 74px;
  border-radius: 50%;
}

/* The shapes the specialist card asks for (frame 11f). Each one stands in for
   a real element and carries its height, so the swap does not move anything:
   the price block, a type tag, the profile link in the band. */
.skeleton__line--price {
  flex: none;
  width: 74px;
  height: 36px;
}

.skeleton__line--tag {
  width: 96px;
  height: 27px;
  border-radius: 20px;
}

.skeleton__line--tag-wide {
  width: 128px;
}

.skeleton__line--link {
  width: 108px;
  margin-left: auto;
}

/* The bar above the results: two applied-filter pills and the sort control. */
.skeleton__line--pill {
  width: 118px;
  height: 30px;
  border-radius: 20px;
}

.skeleton__line--pill-wide {
  width: 150px;
}

.skeleton__line--sort {
  width: 168px;
  height: 35px;
  margin-left: auto;
  border-radius: var(--radius-control);
}

/* --- System message ------------------------------------------------------ */

/* Section 14. A strip above the content in four types, each one a colour on its
   own tint. It has no border: the tint is what separates it from --bg, and an
   outline as well would make it a card.

   Its first reader is the rejected sign-in on 2a, where the message goes above
   the form rather than under the field. Nothing here dismisses or fades: "Nie
   znika sam", and toasts are on the list of what the system does not have. */
.message {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 18px;
  font-size: 15px;
  line-height: 1.55;
  border-radius: var(--radius-card);
}

/* flex: none keeps the 16 px icon from shrinking beside a sentence that wraps,
   and the top margin drops it onto the first line rather than the middle of the
   block. */
.message__icon {
  display: flex;
  flex: none;
  margin-top: 3px;
}

/* A link after the sentence takes the strip's colour, underlined, so it reads
   as part of the message rather than a second accent. */
.message__link {
  color: inherit;
  font-weight: 500;
  text-decoration: underline;
}

/* The same words as a button, where the action is a POST: nothing of the
   button's own look, so the strip reads the same either way. */
.message__button {
  padding: 0;
  font: inherit;
  font-weight: 500;
  background: none;
  border: 0;
  cursor: pointer;
}

.message--success {
  color: var(--ok);
  background: var(--band);
}

.message--warning {
  color: var(--warn);
  background: var(--t3);
}

.message--error {
  color: var(--alert);
  background: var(--t5);
}

.message--info {
  color: var(--accent);
  background: var(--hl);
}

/* The strip above a listing (components/listing_notice.html): outer padding
   and an inner column, the way .seo-crumbs holds its trail, so the strip lines
   up with the crumbs of the type page and the pair page. */
.listing-notice {
  padding: 20px var(--gutter) 0;
}

.listing-notice__inner {
  max-width: var(--content-max);
  margin: 0 auto;
}

/* The same strip on 1c, inside the list's own column and above its heading. */
.catalog-page__notice {
  margin-bottom: 20px;
}

/* The 413 slot of a file form (components/form_refusal.html): an error message
   waiting for its sentence, drawn only once htmx has put one in — and not while
   the form is sending again, so a refusal never stands over the next attempt.
   Both file forms carry hx-disabled-elt="find button", which disables the
   button for exactly the length of a request. */
.form-refusal:has(.message__text:empty),
form:has(button:disabled) .form-refusal {
  display: none;
}

/* --- Icon ---------------------------------------------------------------- */

/* components/icon.html: the line icons of the panel mockup, 20 px on a 20 px
   box, stroked in the colour of the text beside them. */
.icon {
  flex: none;
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.icon--16 {
  width: 16px;
  height: 16px;
}

.icon--18 {
  width: 18px;
  height: 18px;
}

/* --- Panel menu ---------------------------------------------------------- */

/* The side column of every screen under /panel/ and /moje/, panel mockup of
   24.09: an icon and a label per item, the unread count at the far end of
   "Wiadomości", and the active item as a tinted pill. The specialist menu
   (components/panel_nav.html) and the patient one
   (components/patient_nav.html) differ in their items and share every rule
   below. It stays in view while a long screen scrolls. */
.panel-nav {
  position: sticky;
  top: 24px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.panel-nav__item {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 0 14px;
  font-size: 15px;
  color: var(--muted);
  border-radius: var(--radius-button);
}

.panel-nav__item:hover {
  color: var(--text);
  background: rgba(255, 255, 255, 0.75);
}

.panel-nav__item--active,
.panel-nav__item--active:hover {
  font-weight: 500;
  color: var(--accent);
  background: var(--tint);
}

/* The count of the mockup: a 20 px accent pill pushed to the end of the row. */
.unread--nav {
  margin-left: auto;
}

.unread--nav .unread__pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  font-family: var(--font-sans);
  font-weight: 600;
  line-height: 1;
  border-radius: var(--radius-pill);
}

/* --- Toggle -------------------------------------------------------------- */

/* `DESIGN_SPEC.md` section 45.3.1, the only anatomy the document gives this
   control. 40 × 23 with a 19 px handle and 2 px of padding — frames 2l and 2n
   draw 42 × 24 with 18 px, and section 26 records the difference and why the
   document wins.

   A button, not a checkbox: one click is one POST, so the switches of 2l and
   2n work with no JavaScript (templates/components/toggle.html).

   No transition. "Czego nie ma w systemie" rules out animating between htmx
   states, and this is one. */
.toggle {
  position: relative;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  width: 40px;
  height: 23px;
  padding: 2px;
  background: var(--line-control);
  border: 0;
  border-radius: 12px;
  cursor: pointer;
}

/* Section 15: "Minimalny cel dotknięcia 44×44 px." The track is 40 × 23 and
   stays that way — section 3.1 of the turn-13 document draws it — so the hit
   area is bought with an invisible overlay instead of with padding, which
   border-box would have taken out of the track rather than added around it.
   The row is 16 px of padding either side of a 23 px control, so 44 px of
   target fits inside it without reaching its neighbours. */
/* Anchored to the right edge of the 40 px track rather than centred on it: a
   centred 44 px box hangs 2 px past each side, and this control sits at the end
   of a row that ends at the edge of the screen — on 2n at 360 px those 2 px were
   the whole of the page's horizontal scrolling (measured in the browser).
   The area is still 44 x 44; it reaches 4 px further into the row instead. */
.toggle::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 0;
  width: 44px;
  height: 44px;
  transform: translateY(-50%);
}

.toggle--on {
  justify-content: flex-end;
  background: var(--accent);
}

.toggle__handle {
  width: 19px;
  height: 19px;
  background: var(--surface);
  border-radius: 50%;
}

.toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The same control as a form field, for frame 12d (DESIGN_SPEC.md §39.5).
   The button above sends the form on every click, which is right where the
   change applies at once (2l, 2n) and wrong where a save bar decides when it
   applies — a checkbox is the only thing that can hold a state without
   sending it, and it does so with no script, which is the whole condition
   docs/CONVENTIONS.md puts on not writing one.

   Drawn rather than left to the browser, exactly like .choice above: the track
   is the input itself with appearance:none, and the handle is ::before, because
   an input cannot hold the child span the button version uses.

   Dimensions are section 3.1 of the turn-13 document, the same ones .toggle
   carries — 40 × 23, handle 19 × 19, 2 px of padding. Frame 12d draws 44 × 26
   with a 20 px handle, which is that document's *phone* variant; §26 already
   settled the same disagreement for 2l and 2n, and it settles this one the same
   way. A test pins both variants to one set of numbers.

   No transition, for the reason .toggle gives. */
.toggle-check {
  position: relative;
  flex: none;
  width: 40px;
  height: 23px;
  margin: 0;
  padding: 0;
  appearance: none;
  background: var(--line-control);
  border: 0;
  border-radius: 12px;
  cursor: pointer;
}

.toggle-check::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 19px;
  height: 19px;
  background: var(--surface);
  border-radius: 50%;
}

.toggle-check:checked {
  background: var(--accent);
}

/* 40 − 19 − 2 − 2. Written as the arithmetic rather than as 17px so that
   changing the track or the handle above cannot leave the handle overhanging
   an edge. */
.toggle-check:checked::before {
  left: calc(100% - 19px - 2px);
}

/* Section 15: "Minimalny cel dotknięcia 44×44 px", bought with an overlay
   rather than with padding, for the reason .toggle::after gives. An input has
   no ::after of its own to spare, so the row provides the room instead and this
   is the pseudo-element the box itself can carry. */
.toggle-check::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}

.toggle-check:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The shape beside the two rows of 12d that nobody decides — "Niezbędne" and
   "Reklama". **Not a disabled control**: a disabled checkbox is still a control
   in the accessibility tree, so it offers an interaction and then refuses it.
   This is a picture, hidden from readers with aria-hidden, and the word beside
   it ("zawsze", "brak") is what actually carries the state. */
.toggle-static {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: flex-start;
  width: 40px;
  height: 23px;
  padding: 2px;
  background: var(--line);
  border-radius: 12px;
}

.toggle-static--on {
  justify-content: flex-end;
  background: var(--line-strong);
}

.toggle-static__handle {
  width: 19px;
  height: 19px;
  background: var(--surface);
  border-radius: 50%;
}

/* --- Phone (section 15): 48 px controls, 44 px targets, the panel rail ----- */

/* Three of the rows of the table in DESIGN_SPEC.md section 15 are components
   and live here: the field grows to 48 px, the primary button takes the full
   width with padding 15px 20px, and the panel's 200 px column becomes a
   scrolling rail of pills.

   The full width is delivered by .btn-group rather than by .btn--primary. A
   primary button also stands inside rows the phone keeps horizontal — the price
   bar of 11b, the toolbar of the calendar — and a width on the
   variant class would have to be undone in each of them. The group is the thing
   that knows it holds a form's actions, and section 1 already says what it does
   on a phone: "W pionie (mobile, karty w sidebarze) flex-direction: column".
   Screens whose frame draws one button across the width and no group say so in
   their own section.

   min-height on .btn is "pola dotykowe min. 44 px" written once. Every size
   only grows — S comes out at 38 px on the desktop and no variant is taller
   than 44 to begin with, so nothing that already fits moves. */
@media (max-width: 900px) {
  .btn {
    min-height: 44px;
  }

  .btn--m,
  .btn--l {
    --btn-py: 15px;
    --btn-px: 20px;
  }

  .btn-group {
    flex-direction: column;
    align-items: stretch;
  }

  .btn__label {
    display: none;
  }

  .btn__label--phone {
    display: inline;
  }

  .field input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
  .field select,
  .field input[type="file"]::file-selector-button {
    height: 48px;
  }

  /* A one-line choice is 21 px of text, well under the 44 the section asks
     for, and the label is the target — the whole row answers a finger. The air
     is padding rather than min-height so that a consent sentence running to
     three lines keeps its box beside the first one. */
  .choice {
    padding: 12px 0;
  }

  /* --- The panel menu as a rail of pills (11d, 11e) --- */

  /* Note 01 beside 11d asks for "przewijanego paska pigułek". The rail wraps
     into rows instead: five pills need about 430 px against the 354 px of a
     390 px screen, and a scrolling rail started the last of them, "Płatności",
     behind the edge, with nothing to bring the active one into view short of
     a script. Wrapped, every item and the active one are on the screen at
     any width, for the Tab key and after a turn of the phone (section 41.6).
     The rail is full-bleed, so it carries its own gutter and the panel grid
     gives up its own (section 18). It scrolls with the page rather than
     sticking, and the icons stay with the column: a pill is its label.

     The order is the markup's (components/panel_nav.html) and nothing here
     reorders it — `order` would move what the eye sees and not what the Tab
     key walks. */
  .panel-nav {
    position: static;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 8px;
    padding: 14px var(--gutter);
    background: var(--surface);
    border-bottom: 1px solid var(--line);
  }

  .panel-nav .icon {
    display: none;
  }

  /* Section 5, "Zakładka mobilna": 14 px, padding 8px 14px, radius 20. Section
     15 raises the padding to 10px 14px in navigational areas and asks for 44 px
     of target, which min-height guarantees whatever the line box does. */
  .panel-nav__item {
    flex: none;
    justify-content: center;
    gap: 8px;
    min-height: 44px;
    padding: 10px 14px;
    font-size: 14px;
    color: var(--text);
    white-space: nowrap;
    border: 1px solid var(--line-control);
    border-radius: var(--radius-pill);
  }

  /* The active pill is filled on the rail, where a tinted pill beside the
     edged ones would read as a disabled one. */
  .panel-nav__item--active,
  .panel-nav__item--active:hover {
    color: var(--on-accent);
    background: var(--accent);
    border-color: var(--accent);
  }
}

/* --- Announcement -------------------------------------------------------- */

/* The live region of components/announcement.html: a sentence for a reader
   who cannot see what a swap changed, and nothing at all on screen.

   Hidden the way the menu action of section 6 and the star inputs of section 24
   are hidden — clipped, so it stays in the accessibility tree. display: none
   and visibility: hidden both take an element out of that tree, which for a
   live region means it is not merely invisible but silent, and the class would
   do the opposite of its job. */
.announcement {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ==========================================================================
   8. Loading indicator
   The classes htmx puts on the page while a request is in flight.
   Convention and worked example: docs/CONVENTIONS.md, /style/fragmenty/.
   ========================================================================== */

/* htmx ships rules of its own for these two classes and appends them to the
   head at load. We turn that off in base.html with

     <meta name="htmx-config" content='{"includeIndicatorStyles":false}'>

   and write them here instead, because the shipped ones break two rules of
   this project at once:

     .htmx-indicator{opacity:0;visibility: hidden}
     .htmx-request .htmx-indicator{…;transition: opacity 200ms ease-in}

   The transition is "Animacje przejść między stanami HTMX", ruled out by name
   in "Czego nie ma w systemie" — a swap is instant. And `visibility` leaves the
   indicator holding its space in the layout at all times, while the rule for
   the skeleton is "nie powoduje przeskoku układu": it stands where the results
   will stand, so it has to take the space and then give it back.

   Hence `display`. Nothing here transitions or animates, the same as the
   skeleton it is usually made of. */
.htmx-indicator {
  display: none;
}

/* Both selectors are needed, and they are not the same case. htmx marks the
   element that fired the request with .htmx-request, so an indicator nested
   inside it matches the first. When hx-indicator points somewhere else — the
   usual arrangement, and the one on /style/fragmenty/ — the class lands on the
   indicator itself and only the second matches. */
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  display: block;
}

/* The two calendars (sections 22 and 23) switch late. A week arrow answers in
   well under 400 ms, and for that long the week that is on the screen stays on
   the screen: the person sees the dates change and nothing else. Hiding the
   week and showing grey for the few dozen milliseconds of a fast answer read
   as the whole calendar reloading. Only a request still waiting at 400 ms gets
   the skeleton, which then takes the region's place exactly as the immediate
   switch does.

   static/js/wait.js marks that moment: it adds .is-slow to a skeleton with
   data-wait once .htmx-request has stood on it for that many milliseconds,
   and takes it off with .htmx-request. Sections 22 and 23 switch `display` on
   the two classes together, in one rule for the skeleton and one for the
   region, so the two are never shown together and never gone together.
   Nothing moves and nothing fades — the value jumps once — so the ban on
   "Animacje przejść między stanami HTMX" holds. The script says why CSS alone
   cannot wait in every browser. */

/* ==========================================================================
   9. Specialist list — frame 1c
   Page layout, the filter rail (DESIGN_SPEC.md section 2, "Szyna filtrów"),
   the specialist card in its list variant (section 3), sort control and
   pagination (section 17). The checkbox and radio rows reuse .choice from
   section 7; the tag and applied-filter pills reuse .pill.
   ========================================================================== */

.catalog-page {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 26px var(--gutter) 64px;
}

/* Wraps between the crumbs and not inside them: unwrapped, a specialist's
   full name squeezed into a narrow column broke over two lines with the next
   crumb floating at the far end of the first. */
.breadcrumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
  font-size: 14px;
  color: var(--muted);
}

.breadcrumbs a {
  color: var(--muted);
  text-decoration: none;
}

.breadcrumbs a:hover {
  color: var(--text);
  text-decoration: underline;
}

/* Heading and count on one baseline — "Specjaliści w Krakowie   100 osób".
   The trail over it stands only under a type or a city
   (catalog/partials/list_heading.html), and only then is there a gap to keep. */
.catalog-page__head {
  display: flex;
  align-items: baseline;
  gap: 14px;
}

.breadcrumbs + .catalog-page__head {
  margin-top: 10px;
}

.catalog-page__title {
  font-size: 42px;
  line-height: 1.12;
  letter-spacing: -0.015em;
}

.catalog-page__count {
  font-size: 15px;
  color: var(--muted);
}

.catalog-page__layout {
  display: flex;
  align-items: flex-start;
  gap: 40px;
  margin-top: 24px;
}

/* --- Filter rail ---------------------------------------------------------- */

/* Section 2: width 250 px, flex: none; groups separated by a 1 px --line top
   border with 22 px on either side of it; the row counts pushed right with
   margin-left: auto. */
.filter-rail {
  flex: none;
  width: 250px;
}

/* The name of the form, for a screen reader only: clipped rather than
   display: none, which would take it out of the accessibility tree and leave
   aria-labelledby on the form pointing at nothing. */
.filter-rail__title {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* With the name out of sight, the first group opens the rail level with the
   top of the results column. */
.filter-rail > .filter-group:first-of-type {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

/* "Wyczyść" only once there is something to clear: a checked box, a radio
   other than the default "Wszyscy", a city chosen, or a day in the date
   field. Read off the form itself, because an htmx request swaps the results
   and leaves the form as the visitor set it. A date field is never :checked,
   so its value attribute stands in: the page renders it for a chosen day and
   static/js/filters.js keeps it in step with what is typed or picked. */
.filter-rail__clear {
  display: none;
  font-size: 14px;
  color: var(--muted);
  text-decoration: none;
}

.filter-rail:has(
    input[type="checkbox"]:checked,
    input[type="radio"]:checked:not([value=""]),
    option:checked:not([value=""]),
    input[type="date"][value]
  )
  .filter-rail__clear {
  display: inline-flex;
  margin-top: 22px;
}

.filter-rail__clear:hover {
  color: var(--text);
  text-decoration: underline;
}

.filter-group {
  display: flex;
  flex-direction: column;
  gap: 9px;
  margin-top: 22px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
}

.filter-group__name {
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
}

/* A rail row is a .choice with the count on the far right. */
.filter-row {
  align-items: center;
}

.filter-row__count {
  margin-left: auto;
  font-size: 15px;
  color: var(--muted);
}

/* The date field of the "Dostępność" group, under the seven-day box. Its
   label reads like the box's own — the two are two answers to one question —
   and the input is a .field control, so it is the 44 px box every form on
   the site draws. The two shortcuts beside it are quiet links in the size of
   "Wyczyść", in a row of their own under the field. */
.filter-day__label {
  font-size: 15px;
  line-height: 1.4;
  color: var(--muted);
}

.filter-day__shortcuts {
  display: flex;
  gap: 16px;
  margin-top: 9px;
}

.filter-day__shortcut {
  font-size: 14px;
  color: var(--muted);
  text-decoration: none;
}

.filter-day__shortcut:hover {
  color: var(--text);
  text-decoration: underline;
}

.filter-rail__submit {
  margin-top: 22px;
}

/* The pill row of frame 1g ships on every width and shows on none but the
   phone — the arrangement section 6 uses for .site-menu and section 10 for
   .profile-bar. Here it also keeps six disclosures out of the reading order of
   a rail whose groups are all open anyway. */
.filter-pills {
  display: none;
}

/* --- Results column -------------------------------------------------------- */

/* The column holds two things that never show at once: the results and the
   skeleton that stands in for them while a request is in flight. */
.catalog-results-area {
  flex: 1;
  min-width: 0;
}

.catalog-results {
  min-width: 0;
}

/* --- Catalogue map ---------------------------------------------------------- */

/* The map over the results: a card of the same family as the specialist card
   below it (--surface, --radius-listing, --shadow-card), as wide as the
   column. templates/catalog/partials/map.html and static/js/map.js; Leaflet's
   own sheet (static/css/leaflet.css) is linked on this page only. The section
   is `hidden` until the script has pins to show, and the attribute has to win
   over the display below. */
.catalog-map[hidden] {
  display: none;
}

.catalog-map {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 20px;
}

/* Only the phone collapses the map, so only the phone draws the toggle. */
.catalog-map__toggle {
  display: none;
  align-self: flex-start;
}

.catalog-map__frame {
  position: relative;
  height: 320px;
  background: var(--surface);
  border-radius: var(--radius-listing);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.catalog-map__canvas {
  width: 100%;
  height: 100%;
  font-family: var(--font-sans);
  background: var(--surface);
}

/* The licence credit (map.html). Always in the corner, at every width and
   without any interaction, over Leaflet's own panes and controls. */
.catalog-map__credit {
  position: absolute;
  right: 0;
  bottom: 0;
  z-index: 1000;
  margin: 0;
  padding: 2px 8px;
  font-size: 12px;
  line-height: 1.4;
  background: rgba(255, 255, 255, 0.85);
  border-top-left-radius: var(--radius-small);
}

.catalog-map__credit a {
  color: var(--muted);
}

/* A pin: a drop in the colour of the profile's type, drawn here rather than
   loaded as an image, so it costs no request. The point sits at the bottom
   corner, which iconAnchor in map.js puts on the office. */
.catalog-map__pin {
  width: 22px;
  height: 22px;
  background: var(--pin, var(--c1));
  border: 2px solid var(--surface);
  border-radius: 50% 50% 50% 0;
  box-shadow: var(--shadow-card);
  transform: rotate(-45deg);
  transform-origin: center;
}

.catalog-map__pin--c1 { --pin: var(--c1); }
.catalog-map__pin--c2 { --pin: var(--c2); }
.catalog-map__pin--c3 { --pin: var(--c3); }
.catalog-map__pin--c4 { --pin: var(--c4); }
.catalog-map__pin--c5 { --pin: var(--c5); }

/* The pins of the card under the pointer or holding the focus. Leaflet sets
   its own transform on the marker, so the pin grows through an outline. */
.catalog-map__pin.is-active,
.catalog-map__pin:focus-visible {
  outline: 3px solid var(--pin, var(--c1));
  outline-offset: 2px;
}

.catalog-map__popup-name {
  font-weight: 600;
}

.catalog-map__popup-address {
  margin: 4px 0 0;
  color: var(--muted);
}

/* --- Loading state, frame 11f ---------------------------------------------- */

/* htmx puts .htmx-request on whatever hx-indicator names — here the skeleton
   itself, which section 8 then switches on through `display`. The results are
   the other half of the same swap: without this rule the skeleton would append
   itself under a full list and double the height of the column.

   The sibling combinator is why results_skeleton.html comes first in the
   markup. It is also why this is one rule and not a class the view has to set
   on the results as well. */
.results-skeleton.htmx-request ~ .catalog-results {
  display: none;
}

.results-skeleton__bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-bottom: 4px;
  margin-bottom: 14px;
}

/* Applied-filter pills left, sort control right, one row (section 17). */
.results-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding-bottom: 4px;
  margin-bottom: 14px;
}

.results-bar__filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.results-sort {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 9px;
  margin-left: auto;
}

/* The way to the ranking rules; underlined at rest, because it has to be found
   by somebody who did not come looking for it. Where the row runs out of room
   it wraps under the select instead of being cut. */
.results-sort__terms {
  font-size: 14px;
  color: var(--muted);
}

.results-sort__terms:hover {
  color: var(--text);
}

.results-sort__label {
  font-size: 14px;
  color: var(--text);
}

/* Section 17 draws its own 10×6 arrow in --muted; embedding one here would
   mean a data URI holding a pasted copy of the token, so the native arrow
   stays — the same trade the badge tints refused for hex values. */
.results-sort__select {
  padding: 8px 13px;
  font-size: 14px;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-field);
}

/* --- Empty list, frame 1d --------------------------------------------------- */

/* Note 02 on the frame: "Bez ramki i bez ikony. Zdanie w 20 px, powód w drugim
   wierszu." So this is a block in the flow of the results column, not the
   centred card of section 13 — that one is for screens where the emptiness is
   all there is (2i, 2m, 8d), and it keeps components/empty_state.html.

   Section 13's rule still holds and is the reason the block exists at all: an
   empty state names the next step, never the state. Here the next step is three
   specialists who would actually see this patient. */
.catalog-empty {
  display: flex;
  flex-direction: column;
  gap: 36px;
}

.catalog-empty__lead {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.catalog-empty__title {
  font-size: 20px;
  line-height: 1.5;
  color: var(--text);
}

.catalog-empty__note {
  max-width: 560px;
  font-size: 16px;
  line-height: 1.6;
  color: var(--muted);
}

.catalog-empty__note a {
  color: var(--accent);
}

.catalog-empty__online {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.catalog-empty__online-title {
  color: var(--accent);
}

.catalog-empty__widen {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 20px;
  padding-top: 4px;
  font-size: 16px;
}

.catalog-empty__widen a {
  color: var(--accent);
  text-decoration: none;
}

.catalog-empty__widen a:hover {
  color: var(--text);
}

.catalog-empty__sep {
  color: var(--muted);
}

/* --- Specialist card, list variant ----------------------------------------- */

/* Section 3: two parts in one container — the data block and the availability
   band. Padding sits on the parts, never on the container. */
.specialist-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* The card of PODGLAD_katalog in the panel mockup of 24.09: no edge, a 20 px
   corner, a breath of shadow that deepens under the pointer. It stands on the
   --bg of the list, where the shadow is enough to lift it (unlike the white
   bands the cards of section 4 keep their edge for). */
.specialist-card {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-radius: var(--radius-listing);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.specialist-card:hover {
  box-shadow: var(--shadow-raised);
}

.specialist-card__body {
  display: flex;
  gap: 20px;
  padding: 22px 24px;
}

.specialist-card__photo {
  flex: none;
  width: 84px;
  height: 104px;
  border-radius: var(--radius-card);
  object-fit: cover;
}

/* Never a silhouette (section 3, "Brak zdjęcia"): initials on --tint, the
   same geometry as the photo. */
.specialist-card__photo--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 22px;
  color: var(--accent);
  background: var(--tint);
}

.specialist-card__content {
  display: flex;
  flex: 1;
  min-width: 0;
  flex-direction: column;
  gap: 9px;
}

.specialist-card__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
}

.specialist-card__name {
  font-family: var(--font-serif);
  font-size: 21px;
  font-weight: 500;
  line-height: 1.25;
  overflow-wrap: anywhere;
}

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

.specialist-card__name a:hover {
  color: var(--accent);
}

/* Price in --c1, right-aligned, "pierwsza wizyta" beneath — and no "od":
   the price always names the first visit (section 3). */
.specialist-card__price {
  display: flex;
  flex: none;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  text-align: right;
}

.specialist-card__amount {
  font-size: 19px;
  font-weight: 500;
  line-height: 1.2;
  color: var(--c1);
}

.specialist-card__price-note {
  font-size: 13px;
  color: var(--muted);
}

/* "Cena niepodana": a profile without a priced first service keeps the row,
   in the caption's colour rather than the amount's. */
.specialist-card__price--none {
  font-size: 14px;
  color: var(--muted);
}

/* One star, not five (section 3): the number of reviews is the link, because
   it says more than the average. */
.specialist-card__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  font-size: 14px;
  color: var(--muted);
}

.specialist-card__star {
  font-size: 16px;
  color: var(--star);
}

.specialist-card__rating {
  font-size: 15px;
  color: var(--text);
}

.specialist-card__reviews {
  color: var(--muted);
  text-decoration: underline;
}

.specialist-card__reviews:hover {
  color: var(--text);
}

.specialist-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* The band: the "Najbliższe terminy" chips on the left, the profile link on the
   right (DESIGN_SPEC.md section 3, "Pas dostępności"). Without chips the link
   stands alone, pushed right. */
.specialist-card__band {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 14px 24px;
  background: var(--slot-bg);
}

.specialist-card__terms {
  display: flex;
  align-items: center;
  flex: 1;
  min-width: 0;
  gap: 10px;
}

/* The chips run in one row that scrolls sideways: a finger or a wheel moves
   it, and Tab brings each chip into view on its own. The 4 px padding keeps
   a focus ring from being clipped by the overflow; the negative margin puts
   the first chip back where the row began. `contain: inline-size` keeps the
   row's full length out of the width every ancestor asks for — without it
   the flex items up to the results column grow to fit all the chips and the
   phone layout scrolls sideways as a whole. */
.specialist-card__track {
  display: flex;
  flex: 1;
  align-items: center;
  gap: 10px;
  min-width: 0;
  contain: inline-size;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  padding: 4px;
  margin: -4px;
}

.specialist-card__track > a {
  flex: none;
  white-space: nowrap;
  scroll-snap-align: start;
}

.specialist-card__terms-label {
  flex: none;
  white-space: nowrap;
  font-size: 13px;
  color: var(--muted);
  padding-right: 2px;
}

/* Section 3: 14 px --ok on --surface, 1 px --ok, radius 6, padding 7px 12px.
   A term is a link — the main entrance from the list, not a button. */
/* A white chip on the term band, as the mockup draws it: 36 px, weight 500,
   the free-slot blue. The edge is transparent at rest and --slot under the
   pointer, so the hover does not move the text by a pixel. */
.specialist-card__term {
  display: inline-flex;
  align-items: center;
  height: 36px;
  padding: 0 14px;
  font-size: 14px;
  font-weight: 500;
  color: var(--slot);
  background: var(--surface);
  border: 1px solid transparent;
  border-radius: var(--radius-field);
  box-shadow: var(--shadow-card);
  text-decoration: none;
}

.specialist-card__term:hover {
  color: var(--slot);
  border-color: var(--slot);
}

/* The end of the track: one more batch of terms. A text link like the
   profile link, so the chips stay the only boxes in the band. */
.specialist-card__more {
  font-size: 14px;
  color: var(--accent);
  text-decoration: none;
  padding: 7px 4px;
}

.specialist-card__more:hover {
  color: var(--text);
}

.specialist-card__profile-link {
  flex: none;
  margin-left: auto;
  font-size: 14px;
  font-weight: 500;
  color: var(--accent);
  text-decoration: none;
}

.specialist-card__profile-link:hover {
  color: var(--text);
}

/* --- Pagination ------------------------------------------------------------ */

/* Section 17: centred, 38 px items, the active page on --accent with no
   border, "Dalej →" wider and set apart. */
.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding-top: 22px;
}

.pagination__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 38px;
  height: 38px;
  font-size: 14px;
  color: var(--text);
  text-decoration: none;
  border: 1px solid var(--line-control);
  border-radius: var(--radius-control);
}

.pagination__item--active {
  color: var(--on-accent);
  background: var(--accent);
  border: 0;
}

.pagination__ellipsis {
  padding: 0 6px;
  color: var(--muted);
}

.pagination__next {
  padding: 0 18px;
  margin-left: 10px;
}

/* --- Phone (1g): the rail as a row of pills, the card in full width -------- */

/* The 250 px rail has nowhere to stand, so the layout stacks and the rail
   becomes what frame 1g draws: a row of pills over the results, each opening
   its group. A band of wrapped rows stood here before, on the argument that
   "each of those pills is a popover, and this project has no popover and no
   modal". That reading is too broad. "Czego nie ma w systemie" bans modals;
   what it says about opening a list is said twice elsewhere in DESIGN_SPEC.md
   (sections 19 and 45): the project expands in place, in the flow. That is
   what this does — the panel pushes the results down and lays nothing over
   them — so the pills are not the thing that was refused.

   The mechanism is the one .site-menu uses: a <details> with no `open` in the
   markup, opening on click, Enter and space with no script. What is new is
   that it carries its <summary> alone and the group it opens stands further
   down, inside the form the row sits beside, and comes back through :has() on
   the layout they share. That buys the two things a wrapper around the
   controls could not: the desktop keeps its open 250 px column with one
   `display: none`, and the inputs exist once, so no second copy submits its
   values alongside the first.

   Note 01 is satisfied in all three of its parts now, stickiness included. The
   measurement that once ruled it out — "four lines of filters pinned to the
   top would take a third of a 390 px screen" — was about the wrapped band; one
   row of pills is 44 px and its panel is opened on purpose.

   Note 02 beside the frame: "Karta zawsze pełnej szerokości, przycisk
   w osobnym wierszu: 44 px wysokości celu dotykowego." */
@media (max-width: 900px) {
  .catalog-page {
    padding-bottom: 48px;
  }

  .catalog-page__title {
    font-size: 28px;
  }

  .catalog-page__head {
    flex-wrap: wrap;
    gap: 4px 14px;
  }

  /* No gap between the three: with every group shut the rail draws nothing, and
     a gap on either side of an empty box is 48 px of air under a band the frame
     puts straight over the first card. The results carry their own distance
     instead, which is the one that has to hold in both states. */
  .catalog-page__layout {
    flex-direction: column;
    gap: 0;
    margin-top: 20px;
  }

  .filter-rail {
    width: auto;
  }

  .catalog-results-area {
    margin-top: 20px;
  }

  /* Closed behind its toggle until asked for: the script and the tiles cost
     nothing until then. Open, it takes most of the screen over the cards. */
  .catalog-map__toggle {
    display: inline-flex;
  }

  .catalog-map__frame {
    display: none;
    height: 60vh;
  }

  .catalog-map.is-open .catalog-map__frame {
    display: block;
  }

  /* The row itself: the frame's own band — 12 px over 18 px on --surface with a
     hairline under it — carried edge to edge by the negative margin the panel
     rails of section 25 use, and stuck to the top of the screen (note 01). It
     scrolls sideways rather than wrapping, so the fifth and sixth pills sit
     past the edge exactly as the fifth pill of 11d does. */
  .filter-pills {
    position: sticky;
    top: 0;
    z-index: 3;
    /* The column above it is align-items: flex-start, so without this the row
       is as wide as its six pills, the negative margin adds to that width
       instead of cancelling the gutter, and the page scrolls sideways —
       measured at 390 px before the line went in. Stretch against a negative
       margin is what makes a full-bleed band exactly the width of the screen. */
    align-self: stretch;
    display: flex;
    gap: 8px;
    margin: 0 calc(var(--gutter) * -1);
    padding: 12px var(--gutter);
    overflow-x: auto;
    background: var(--surface);
    border-bottom: 1px solid var(--line);
  }

  .filter-pill-toggle {
    flex: none;
  }

  /* 13 px, 6 px of radius and 9/12 of padding are the frame's; the height is
     not — 35 px is under the 44 px a touch target has to reach, the same two
     pixels section 41 already added to the pills of 11d and 11e. The edge is
     --line-control and not the frame's --line-strong, because a pill is
     a control and section 42 asks controls to draw their edge with the control
     token. */
  .filter-pill {
    display: flex;
    align-items: center;
    gap: 7px;
    min-height: 44px;
    padding: 9px 12px;
    font-size: 13px;
    color: var(--muted);
    white-space: nowrap;
    background: transparent;
    border: 1px solid var(--line-control);
    border-radius: var(--radius-control);
    list-style: none;
    cursor: pointer;
  }

  .filter-pill::-webkit-details-marker {
    display: none;
  }

  /* "Dietetycy" and "Kraków" on the frame: filled with --tint and written in
     --text, which is how the frame tells a group holding a value from one that
     does not. */
  .filter-pill--chosen {
    color: var(--text);
    background: var(--tint);
  }

  /* The 9 x 6 chevron of the frame, drawn rather than fetched — two 1.4 px
     strokes, the same trick .site-menu__bars uses for the hamburger. */
  .filter-pill__chevron {
    flex: none;
    width: 7px;
    height: 7px;
    margin-top: -3px;
    border-right: 1.4px solid currentcolor;
    border-bottom: 1.4px solid currentcolor;
    transform: rotate(45deg);
  }

  /* The groups. Every one of them is closed until its own pill is open, and the
     panel is a sibling rather than a child so that it can open the full width
     of the screen under a row that scrolls sideways. Seven rules and not one
     selector, because each pill opens exactly one group and a rule that said so
     generally would have to reach for an attribute nobody would maintain. */
  .filter-group {
    display: none;
    margin-top: 16px;
    padding-top: 16px;
  }

  .catalog-page__layout:has(#pigulka-typ[open]) #grupa-typ,
  .catalog-page__layout:has(#pigulka-miasto[open]) #grupa-miasto,
  .catalog-page__layout:has(#pigulka-forma[open]) #grupa-forma,
  .catalog-page__layout:has(#pigulka-nfz[open]) #grupa-nfz,
  .catalog-page__layout:has(#pigulka-dostepnosc[open]) #grupa-dostepnosc,
  .catalog-page__layout:has(#pigulka-cena[open]) #grupa-cena,
  .catalog-page__layout:has(#pigulka-ocena[open]) #grupa-ocena {
    display: flex;
  }

  /* With every group shut, the rail would be "Wyczyść" over a "Filtruj"
     button with nothing between them — a form asking to be submitted empty.
     Both come back with the first open group, which is also when the submit is
     needed: it is what applies a filter with no JavaScript running. */
  .filter-rail__submit {
    display: none;
  }

  /* Even with a value to clear: the selector carries the :has() of the rule
     that shows the link, so it outranks that rule instead of losing to it. */
  .catalog-page__layout:not(:has(.filter-pill-toggle[open]))
    .filter-rail:has(input:checked, option:checked, input[type="date"][value])
    .filter-rail__clear {
    display: none;
  }

  .catalog-page__layout:has(.filter-pill-toggle[open]) .filter-rail__submit {
    display: block;
  }

  /* On the phone every group is a panel of its own, and the first one keeps
     the rule over it like the rest. */
  .filter-rail > .filter-group:first-of-type {
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--line);
  }

  /* The name stays in the markup and out of the picture: the pill above says
     which group this is, and aria-labelledby still reads the name off it. */
  .filter-group__name {
    display: none;
  }

  .filter-row__count {
    margin-left: auto;
    font-size: 14px;
  }

  .filter-rail__submit .btn {
    width: 100%;
  }

  /* The two shortcuts under the date field are links a thumb has to hit,
     like "Wyczyść" below. */
  .filter-day__shortcut {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  /* Measured at 390 px: 23 px of link and 38 px of select, both of them a
     control somebody has to hit with a thumb. */
  .filter-rail__clear {
    align-items: center;
    min-height: 44px;
    margin-top: 16px;
  }

  .results-sort__select {
    min-height: 44px;
  }

  /* 23 px of text at 390 px; the row under the select gives it a thumb's height. */
  .results-sort__terms {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  /* Section 15: the photo of the list card goes to 64 x 80. */
  .specialist-card__photo {
    width: 64px;
    height: 80px;
  }

  .specialist-card__photo--placeholder {
    font-size: 18px;
  }

  .specialist-card__body {
    gap: 14px;
    padding: 18px var(--gutter);
  }

  .specialist-card__top {
    flex-direction: column;
    gap: 8px;
  }

  /* The band breaks in two: the terms take the whole first row, still
     scrolling sideways, and the profile link takes the second one on its own,
     at the height a finger needs. Without the auto margin gone it would sit
     half a row to the right. */
  .specialist-card__band {
    flex-wrap: wrap;
    gap: 10px 14px;
    padding: 12px var(--gutter);
  }

  .specialist-card__terms {
    flex-basis: 100%;
  }

  .specialist-card__profile-link {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: 44px;
    margin-left: 0;
  }

  .results-bar {
    flex-wrap: wrap;
    gap: 8px 14px;
  }

  /* The column is as wide as the page and no wider, whatever the bar asks
     for, and the sort gives way inside it rather than pushing the page
     sideways. */
  .catalog-results-area {
    align-self: stretch;
  }

  .results-sort {
    flex: 0 1 auto;
    min-width: 0;
    margin-left: 0;
  }

  .results-sort__select {
    flex: 0 1 auto;
    min-width: 0;
  }

  /* Section 17 gives the desktop 38 px; a touch target is 44. */
  .pagination {
    flex-wrap: wrap;
    justify-content: flex-start;
  }

  .pagination__item {
    min-width: 44px;
    height: 44px;
  }
}

/* The map toggle at the right end of the sort row instead of on a row of its
   own, on a phone wide enough for both at their natural width: 119 px of
   toggle, 8 px of gap and the 224 px of "Sortuj: Najbliższy termin" need 351 px
   of column. Narrower, the two keep a row each. Closed, the section drops out
   of the flow and the sort row keeps the toggle's width free; open, the
   toggle goes back over the map it closes. The applied-filter pills move under
   the sort row, so that row stays the top one the toggle is pinned to. */
@media (min-width: 387px) and (max-width: 900px) {
  .catalog-results-area {
    position: relative;
  }

  .catalog-map:not(.is-open) {
    margin-bottom: 0;
  }

  .catalog-map:not(.is-open) .catalog-map__toggle {
    position: absolute;
    top: 0;
    right: 0;
  }

  .catalog-results-area:has(.catalog-map:not([hidden]):not(.is-open)) .results-sort {
    margin-right: 127px;
  }

  .catalog-results-area:has(.catalog-map:not([hidden]):not(.is-open)) .results-bar__filters {
    flex-basis: 100%;
    order: 1;
  }
}

/* ==========================================================================
   10. Specialist profile — frame 1e
   The header band, the content column and the sticky rail beside it. Pills,
   buttons and the overline label come from section 7; what is here is the
   geometry of this one screen, plus the 120 px round photo added to
   DESIGN_SPEC.md section 3 ("Brak zdjęcia").
   ========================================================================== */

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

/* Note 01 beside the frame: everything needed to decide sits above the fold,
   on --surface, separated from the body by one line. */
.profile-head {
  padding: 40px var(--gutter) 44px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.profile-head__inner {
  display: flex;
  align-items: flex-start;
  gap: 32px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.profile-head__photo {
  flex: none;
  width: 120px;
  height: 120px;
  object-fit: cover;
  border-radius: 50%;
}

/* Same geometry as the photo, initials on --tint — never a silhouette
   (DESIGN_SPEC.md section 3). The mono size is this variant's own: 22 px is
   set there for the 84×104 card photo and would swim inside a 120 px circle. */
.profile-head__photo--placeholder {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 34px;
  color: var(--accent);
  background: var(--tint);
}

.profile-head__identity {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding-top: 6px;
}

/* 38 px, not the 52 px of the type scale: the name shares the band with the
   photo and the price, and the frame sets it here. A surname longer than the
   column breaks inside itself rather than pushing the page sideways. */
.profile-head__name {
  font-size: 38px;
  line-height: 1.1;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}

.profile-head__types {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.profile-head__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px;
  padding-top: 2px;
}

.profile-head__rating {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 17px;
  color: var(--text);
}

.profile-head__star {
  font-size: 18px;
  color: var(--star);
}

/* Text, not a link. The list card's "38 opinii" is a link because it leads
   here; here it is already where it leads, and the reviews themselves stand
   further down the page. */
.profile-head__reviews {
  font-size: 15px;
  color: var(--muted);
}

.profile-head__sep {
  font-size: 15px;
  color: var(--muted);
}

/* The card's price treatment (DESIGN_SPEC.md section 3): amount in --c1,
   caption under it, no "od". */
.profile-head__price {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
}

.profile-head__amount {
  font-size: 19px;
  font-weight: 500;
  color: var(--c1);
}

.profile-head__price-note {
  font-size: 13px;
  color: var(--muted);
}

.profile-head__price--none {
  font-size: 14px;
  color: var(--muted);
}

/* --- Body ------------------------------------------------------------------ */

.profile-page__body {
  padding: 48px var(--gutter) 64px;
}

/* Three children, not two: the rail stands between the offices and the
   reviews in the markup, where frame 1h puts it on the phone. The desktop
   drawing is unchanged and stays that way by placing the three rather than by
   reordering them — content in the left column, the rail across both rows of
   the right one so it can still be sticky the whole way down, the reviews
   under the content.

   The 44 px between the content and the reviews is a margin and not a row gap,
   and the difference shows on a profile with no reviews: the rail spans both
   rows whether or not the second one holds anything, so a row gap would leave
   44 px of air under every profile nobody has reviewed yet.

   `auto 1fr` is the other half of that, and it is not decoration. Left to
   themselves both rows would be `auto`, and a grid item spanning two intrinsic
   rows hands its surplus height to **both** of them — so a tall rail beside a
   short bio would push the reviews down and open a gap the 44 px above says
   nothing about (measured: 400 px of rail over 120 px of content turns 44 into
   72). A flexible second row takes the whole surplus instead, which puts the
   air under the reviews. */
.profile-page__layout {
  display: grid;
  grid-template-columns: 1fr 360px;
  grid-template-rows: auto 1fr;
  gap: 0 72px;
  align-items: start;
  max-width: var(--content-max);
  margin: 0 auto;
}

/* Note 02: sections are told apart by their labels and by 44 px of air, not by
   borders — "bez kart w kartach". */
.profile-page__content {
  grid-column: 1;
  grid-row: 1;
  display: flex;
  flex-direction: column;
  gap: 44px;
  min-width: 0;
  max-width: 720px;
}

/* The reviews wear both classes, so this rule has to stand after the one above
   or `grid-row: 1` wins on source order and the two land on top of each other.
   Measured in the browser at 1440 px, where the modifier was written first. */
.profile-page__content--reviews {
  grid-column: 1;
  grid-row: 2;
  margin-top: 44px;
}

.profile-section {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.profile-section__label {
  color: var(--accent);
}

.profile-section__prose p {
  font-size: 17px;
  line-height: 1.72;
  color: var(--text);
}

/* The bio arrives through |linebreaks, so its paragraphs are spaced here and
   the last one does not push the section apart. */
.profile-section__prose p + p {
  margin-top: 14px;
}

.profile-scope {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* "Usługi i ceny": the name on the left and the price on the right of one
   line, one line per service, divided like the rows of a list. */
.profile-services {
  display: flex;
  flex-direction: column;
  margin: 0;
}

.profile-services__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  padding: 12px 0;
  border-top: 1px solid var(--line);
}

.profile-services__row:last-child {
  border-bottom: 1px solid var(--line);
}

.profile-services__name {
  font-size: 16px;
  color: var(--text);
}

.profile-services__price {
  flex: none;
  margin: 0;
  font-size: 16px;
  font-weight: 500;
  color: var(--c1);
}

.profile-services__price--none {
  font-weight: 400;
  color: var(--muted);
}

/* --- Where they see patients ----------------------------------------------- */

.profile-places {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

.profile-place {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.profile-place__name {
  font-size: 17px;
  font-weight: 500;
  color: var(--text);
}

.profile-place__lines {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--muted);
}

/* --- Sticky rail ------------------------------------------------------------ */

/* Note 03: the actions stay visible down the whole page, which is what saves
   the content column from repeating them. */
.profile-aside {
  grid-column: 2;
  grid-row: 1 / span 2;
  position: sticky;
  top: 24px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 24px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.profile-aside__booking {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.profile-aside__label {
  color: var(--accent);
}

/* The calendar half of the rail: three term rows, day left and hour right,
   the hour in mono accent — the frame's own arrangement. */
.profile-aside__terms {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.profile-aside__term {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 15px;
  color: var(--text);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-field);
  padding: 13px 16px;
  text-decoration: none;
}

.profile-aside__term:hover {
  border-color: var(--accent);
}

.profile-aside__term-time {
  font-family: var(--font-mono);
  color: var(--accent);
}

.profile-aside__calendar-link {
  font-size: 14px;
  color: var(--accent);
  text-decoration: none;
}

/* DESIGN_SPEC.md section 1, "Grupa przycisków": stacked in a sidebar card,
   gap 10, and each button takes the width of the rail. */
.profile-aside__actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

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

.profile-aside__hint {
  font-size: 13px;
  color: var(--muted);
  text-align: center;
}

.profile-aside__report {
  padding-top: 4px;
  font-size: 13px;
  color: var(--muted);
  text-align: center;
  text-decoration: none;
}

.profile-aside__report:hover {
  color: var(--text);
  text-decoration: underline;
}

/* The phone's action bar of frame 1h. It ships on every width and shows on
   none but the phone, where the block below gives it its geometry — the same
   arrangement as .booking-bar in section 23. */
.profile-bar {
  display: none;
}

/* --- Phone (1h): one column, the rail before the reviews, actions in a bar -- */

/* Three notes beside frame 1h decide the whole of this block.
   Note 02: "Nagłówek bez przycisków: na telefonie zastępuje je pasek dolny,
   więc nie ma dublowania" — .profile-aside__actions goes and .profile-bar
   stands in.
   Note 03: "Kolejność sekcji jak na desktopie; terminy przed recenzjami, bo
   prowadzą do zadania" — the rail carries the free terms and the frame draws it
   between "Gdzie przyjmuje" and "Recenzje". `order: -1` would get it there for
   the eye alone, while the Tab key kept walking the markup, so the page would
   have two sequences. The markup moved instead, the way the
   pill rail of 11d settles the same split, and this block has no `order` left.
   Note 01 and note 04 read as two bars, one stuck to the bottom of the screen
   and one static after the content. They are the same element: a sticky bar
   whose containing block is .profile-page__body floats over the page for as
   long as the body is on screen and comes to rest at its end, which is exactly
   the pair of drawings.

   The bar is the only phone element on this screen with markup of its own
   (specialist_detail.html). It duplicates two buttons, the arrangement 11b
   uses as well: one representation per width, each hidden at the other,
   rather than one set of buttons moved by CSS from a rail into a bar. */
@media (max-width: 900px) {
  /* The band carries the phone's padding and the inner box none: both had a
     gutter of their own, which doubled the side margin to 36 px. */
  .profile-head {
    padding: 26px var(--gutter) 28px;
  }

  /* Photo and name share the first row; the types, the reviews with the
     pills and the price take a full-width row each, in the order of the
     markup — the card's "price under the content" of section 15. The identity
     box steps out of the way with `display: contents` so that its three
     children are rows of this grid. It is a plain div with no role, so the
     accessibility tree does not change. */
  .profile-head__inner {
    display: grid;
    grid-template-columns: 72px minmax(0, 1fr);
    align-items: center;
    gap: 16px;
  }

  .profile-head__identity {
    display: contents;
  }

  .profile-head__types,
  .profile-head__meta,
  .profile-head__price {
    grid-column: 1 / -1;
  }

  .profile-head__photo {
    width: 72px;
    height: 72px;
  }

  .profile-head__photo--placeholder {
    font-size: 22px;
  }

  .profile-head__name {
    font-size: 26px;
  }

  .profile-head__meta {
    flex-wrap: wrap;
    gap: 8px 16px;
  }

  /* The dot parts the reviews from the pills on one desktop line. Wrapped,
     it was left alone at the start or end of a row. */
  .profile-head__sep {
    display: none;
  }

  /* Amount and service on one line, from the left edge like the rows above. */
  .profile-head__price {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 8px;
  }

  .profile-page__body {
    padding: 28px var(--gutter) 40px;
  }

  /* One column, and the three children take it in the order they are written
     in — which is frame 1h's order, so nothing here has to move them. The
     placements of the desktop are undone rather than overridden one by one:
     `grid-column: 2` would otherwise open a second column that this width does
     not have. */
  .profile-page__layout {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
    gap: 32px;
  }

  .profile-page__content,
  .profile-page__content--reviews,
  .profile-aside {
    grid-column: 1;
    grid-row: auto;
  }

  /* One column, so the distance between the sections is the gap above and the
     margin would be added to it. */
  .profile-page__content--reviews {
    margin-top: 0;
  }

  .profile-page__content {
    gap: 32px;
    max-width: none;
  }

  .profile-section__prose p {
    font-size: 16px;
  }

  .profile-places {
    grid-template-columns: minmax(0, 1fr);
  }

  .profile-aside {
    position: static;
    padding: 18px var(--gutter);
    margin: 0 calc(var(--gutter) * -1);
    border-left: 0;
    border-right: 0;
    border-radius: 0;
  }

  .profile-aside__actions {
    display: none;
  }

  /* The bar. Two buttons at flex: 1 the way the frame draws them, over the
     footer and never in the content, with --shadow-savebar, the one exception
     "Czego nie ma w systemie" grants. */
  .profile-bar {
    position: sticky;
    bottom: 0;
    z-index: 4;
    display: flex;
    gap: 8px;
    margin: 28px calc(var(--gutter) * -1) 0;
    padding: 14px var(--gutter);
    background: var(--surface);
    border-top: 1px solid var(--line-strong);
    box-shadow: var(--shadow-savebar);
  }

  .profile-bar .btn {
    flex: 1;
    min-width: 0;
    --btn-px: 8px;
  }
}

/* ==========================================================================
   11. Home page — frame 6a
   Seven bands under the header, each one full-bleed with a --content-max inner
   block, in the background rhythm of note 10 beside the frame.

   Dimensions are the frame's own inline values, written up as DESIGN_SPEC.md
   section 18 — until then the specification said nothing about this screen
   except the compact card.

   Section kickers keep the 0.12em of .t-overline; the frame sets 0.14em on
   four of the five, which is 0.02em on an 11 px word. The token wins, so the
   page has one overline and not two.
   ========================================================================== */

/* --- Band 1: hero ----------------------------------------------------------- */

/* The 36×36 mesh. The frame paints this band flat, but the hero is specified
   as "hero z siatką 36×36", and the token table in README.md describes --hero
   as "tło sekcji hero (z siatką --grid)" and --grid as "linie siatki 36x36 px
   w hero". Neither token had a reader until now. The two linear-gradients draw
   lines, not a ramp — "Gradienty. Żaden." is about the decoration, and frame
   10a already uses exactly this declaration. */
.home-hero {
  padding: 72px var(--gutter) 76px;
  background-color: var(--hero);
  background-image: linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 36px 36px, 36px 36px;
  border-bottom: 1px solid var(--line-strong);
}

.home-hero__inner {
  display: grid;
  grid-template-columns: 1fr 420px;
  align-items: start;
  gap: 72px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.home-hero__lead {
  display: flex;
  flex-direction: column;
  gap: 30px;
  padding-top: 8px;
}

.home-hero__intro {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.home-hero__kicker {
  color: var(--accent);
}

/* 54 px, not the 52 px of the scale in README.md. The frame sets it for this
   one line and DESIGN_SPEC.md section 18 records it, so nobody moves the scale
   to match. */
.home-hero__title {
  max-width: 600px;
  font-size: 54px;
  line-height: 1.1;
  letter-spacing: -0.015em;
}

.home-hero__subtitle {
  max-width: 520px;
  font-size: 18px;
  line-height: 1.6;
  color: var(--muted);
}

/* --- Band 1: the search box ------------------------------------------------- */

/* One box holding two selects and the button, so the border belongs to the
   form and not to the controls inside it. */
.home-search {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 620px;
  padding: 10px;
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-card);
}

.home-search__field {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 3px;
  padding: 0 14px;
}

/* "Kogo szukasz" holds five type names, "Gdzie" thirty-four city names — the
   frame gives the first the wider share. */
.home-search__field:first-of-type {
  flex: 1.1;
}

.home-search__label {
  font-size: 10px;
  color: var(--accent);
}

/* The native arrow stays, for the reason .results-sort__select gives in
   section 9: drawing the 10×6 arrow of section 17 here would mean a data URI
   carrying a pasted copy of --muted. */
.home-search__select {
  min-height: 44px;
  padding: 0;
  font-family: inherit;
  font-size: 16px;
  color: var(--text);
  background: none;
  border: none;
}

.home-search__divider {
  flex: none;
  width: 1px;
  height: 38px;
  background: var(--line);
}

.home-search .btn {
  flex: none;
}

/* Two columns need 1065 px, a measured width: the search row cannot be
   narrower than its two selects, which are as wide as their longest option
   (493 px in Chromium), then the 72 px gap, the 420 px card and 2 × 40 px of
   gutter. Narrower, the card ran past the right edge of the screen, so from
   here down to the phone the card stands under the search, as wide as it. */
@media (max-width: 1079px) {
  .home-hero__inner {
    grid-template-columns: minmax(0, 1fr);
    gap: 40px;
  }

  .home-pitch {
    max-width: 620px;
  }
}

/* --- Band 1: the "Najczęściej" row ------------------------------------------ */

/* Two links, not the frame's four. "Po operacji bariatrycznej" and
   "Insulinooporność" are phrases out of the scope field, and there is no filter
   for a phrase — the same gap that keeps the "Zakres pomocy" phrases on 1e from
   being links. */
.home-hero__shortcuts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.home-hero__shortcuts-label {
  padding-right: 4px;
  font-size: 14px;
  color: var(--muted);
}

.home-hero__shortcut {
  padding: 7px 14px;
  font-size: 14px;
  color: var(--text);
  text-decoration: none;
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-pill);
}

.home-hero__shortcut:hover {
  background: var(--tint);
  border-color: var(--accent);
}

/* --- Band 1: the card for specialists --------------------------------------- */

/* The one card on the page with an accent border. It is the conversion card,
   not a specialist card: note 01 beside the frame says "karta specjalisty po
   prawej" and is describing the version this band was inherited from. */
.home-pitch {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 28px;
  background: var(--surface);
  border: 1px solid var(--accent);
  border-top: 5px solid var(--accent);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.home-pitch__intro {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.home-pitch__kicker {
  color: var(--accent);
}

.home-pitch__title {
  font-size: 27px;
  line-height: 1.2;
}

.home-pitch__list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 18px 0;
  margin: 0;
  list-style: none;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.home-pitch__item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
}

.home-pitch__check {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  color: var(--accent);
  background: var(--tint);
  border-radius: 50%;
}

.home-pitch__foot {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.home-pitch__price {
  font-size: 15px;
  line-height: 1.55;
  color: var(--text);
}

.home-pitch__foot .btn {
  text-align: center;
}

.home-pitch__login {
  font-size: 14px;
  color: var(--muted);
  text-align: center;
}

/* --- Band 2: "Kto może Ci pomóc" -------------------------------------------- */

.home-types {
  padding: 60px var(--gutter) 66px;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}

.home-types__inner {
  display: flex;
  flex-direction: column;
  gap: 26px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.home-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding-bottom: 12px;
  border-bottom: 2px solid var(--accent);
}

.home-section-head__title {
  font-size: 28px;
}

.home-section-head__link {
  font-size: 14px;
  color: var(--accent);
  text-decoration: none;
}

.home-section-head__link:hover {
  text-decoration: underline;
}

.home-types__grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 16px;
}

/* Note 02 beside the frame: the category colour survives in the top rail alone.
   The name is plain --text and the count is --accent, which is why no --cN
   appears anywhere else in this tile. */
.home-type {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 196px;
  padding: 22px 20px 20px;
  text-decoration: none;
  background: var(--surface);
  border: 1px solid var(--line);
  border-top: 5px solid var(--line-strong);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.home-type:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow-raised);
}

.home-type--c1,
.home-type--c1:hover {
  border-top-color: var(--c1);
}

.home-type--c2,
.home-type--c2:hover {
  border-top-color: var(--c2);
}

.home-type--c3,
.home-type--c3:hover {
  border-top-color: var(--c3);
}

.home-type--c4,
.home-type--c4:hover {
  border-top-color: var(--c4);
}

.home-type--c5,
.home-type--c5:hover {
  border-top-color: var(--c5);
}

.home-type__name {
  font-family: var(--font-serif);
  font-size: 21px;
  line-height: 1.2;
  font-weight: 500;
  color: var(--text);
}

.home-type__desc {
  flex: 1;
  font-size: 14px;
  line-height: 1.55;
  color: var(--muted);
}

.home-type__count {
  align-self: flex-start;
  font-size: 13px;
  color: var(--accent);
}

/* --- Band 3: "Nasze podejście" ---------------------------------------------- */

/* Note 03: "jedno mocne przerwanie w środku strony". One dark band per page, so
   nothing else here goes navy. */
.home-origin {
  padding: 86px var(--gutter) 78px;
  background: var(--deep);
}

.home-origin__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: start;
  gap: 80px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.home-origin__intro {
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.home-origin__kicker {
  color: var(--s1);
}

.home-origin__title {
  font-size: 44px;
  line-height: 1.14;
  letter-spacing: -0.015em;
  color: var(--on-deep);
}

.home-origin__para {
  font-size: 17px;
  line-height: 1.7;
  color: var(--on-deep);
  opacity: 0.92;
}

.home-origin__blocks {
  display: flex;
  flex-direction: column;
  gap: 26px;
  padding-top: 6px;
}

/* Each block opens with the white-at-16% rule the footer's bottom edge uses —
   one value, one notation; the comment above .site-footer__bottom names the uses. */
.home-origin__block {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 22px;
  border-top: 1px solid rgba(255, 255, 255, 0.16);
}

.home-origin__heading {
  font-family: var(--font-serif);
  font-size: 22px;
  line-height: 1.25;
  font-weight: 500;
  color: var(--on-deep);
}

.home-origin__text {
  font-size: 16px;
  line-height: 1.7;
  color: var(--on-deep);
  opacity: 0.82;
}

/* --- Band 4: "Trzy etapy" --------------------------------------------------- */

.home-stages {
  padding: 80px var(--gutter);
  background: var(--bg);
}

.home-stages__inner {
  display: flex;
  flex-direction: column;
  gap: 44px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.home-stages__intro {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 640px;
}

.home-stages__kicker {
  color: var(--accent);
}

.home-stages__title {
  font-size: 36px;
  line-height: 1.15;
}

.home-stages__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  padding: 0;
  margin: 0;
  list-style: none;
}

.home-stage {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* Note 04: "numer w kroju maszynowym, pasek koloru nad zdjęciem". */
.home-stage__rail {
  height: 6px;
  background: var(--line);
}

.home-stage--c1 .home-stage__rail {
  background: var(--c1);
}

.home-stage--c3 .home-stage__rail {
  background: var(--c3);
}

.home-stage--c5 .home-stage__rail {
  background: var(--c5);
}

.home-stage__head {
  display: flex;
  align-items: baseline;
  gap: 14px;
}

.home-stage__number {
  font-family: var(--font-mono);
  font-size: 44px;
  line-height: 1;
  font-weight: 400;
}

.home-stage--c1 .home-stage__number {
  color: var(--c1);
}

.home-stage--c3 .home-stage__number {
  color: var(--c3);
}

.home-stage--c5 .home-stage__number {
  color: var(--c5);
}

.home-stage__name {
  font-family: var(--font-serif);
  font-size: 23px;
  line-height: 1.2;
  font-weight: 500;
  color: var(--text);
}

/* A photograph between the name and the paragraph, as the frame draws it.
   width and height in the markup reserve the box; aspect-ratio keeps the crop
   at 16/10 whatever the column. */
.home-stage__figure {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: var(--radius-card);
}

.home-stage__body {
  font-size: 16px;
  line-height: 1.65;
  color: var(--muted);
}

/* --- Band 5: "Sygnały" ------------------------------------------------------ */

/* Note 06: "Sygnały w wierszach ze znacznikami koloru, na wypełnieniu." */
.home-signals {
  padding: 76px var(--gutter);
  background: var(--band);
}

/* The title stands over the rows and the rows fill the width in two columns.
   Beside one narrow column the left half of the band stood empty. */
.home-signals__inner {
  display: flex;
  flex-direction: column;
  gap: 32px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.home-signals__lead {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.home-signals__kicker {
  color: var(--accent);
}

.home-signals__title {
  font-size: 34px;
  line-height: 1.18;
}

.home-signals__link {
  padding-top: 4px;
  font-size: 16px;
  color: var(--accent);
  text-decoration: none;
}

.home-signals__link:hover {
  text-decoration: underline;
}

.home-signals__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 56px;
  padding: 0;
  margin: 0;
  list-style: none;
}

.home-signal {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 16px 0;
  font-size: 16px;
  line-height: 1.55;
  color: var(--text);
  border-top: 1px solid var(--line-strong);
}

/* The rule stands above each row, so no row ends on a rule with nothing under
   it, whichever column is shorter. The first row of the two columns opens
   the list without one. */
.home-signal:nth-child(-n + 2) {
  border-top: none;
}

.home-signal__text {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.home-signal__title {
  font-weight: 600;
}

.home-signal__note {
  font-size: 14px;
  line-height: 1.55;
  color: var(--muted);
}

.home-signal__note a {
  color: var(--accent);
}

.home-signal__marker {
  flex: none;
  align-self: stretch;
  width: 4px;
  border-radius: 2px;
}

.home-signal--c1 .home-signal__marker {
  background: var(--c1);
}

.home-signal--c2 .home-signal__marker {
  background: var(--c2);
}

.home-signal--c3 .home-signal__marker {
  background: var(--c3);
}

.home-signal--c4 .home-signal__marker {
  background: var(--c4);
}

.home-signal--c5 .home-signal__marker {
  background: var(--c5);
}

/* --- Band 6: the calculators ------------------------------------------------ */

.home-calculators {
  padding: 80px var(--gutter);
  background: var(--surface);
}

.home-calculators__inner {
  display: flex;
  flex-direction: column;
  gap: 30px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.home-calculators__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
}

.home-calculators__title-block {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.home-calculators__rule {
  width: 56px;
  height: 4px;
  background: var(--accent);
}

.home-calculators__title {
  font-size: 32px;
}

.home-calculators__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
}

.home-calculator {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 26px 24px 24px;
  text-decoration: none;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.home-calculator:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow-raised);
}

.home-calculator__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.home-calculator__eyebrow {
  color: var(--muted);
}

.home-calculator__index {
  font-family: var(--font-mono);
  font-size: 11px;
}

.home-calculator__index--c1 {
  color: var(--c1);
}

.home-calculator__index--c3 {
  color: var(--c3);
}

.home-calculator__index--c4 {
  color: var(--c4);
}

.home-calculator__name {
  font-family: var(--font-serif);
  font-size: 26px;
  font-weight: 500;
  color: var(--text);
}

/* Segments, not a gradient: "Gradienty. Żaden." (DESIGN_SPEC.md, "Czego nie ma
   w systemie"). BMI, WHtR and BMR are read in bands, so the picture is the
   honest one as well as the permitted one. The widths differ per calculator and
   come from the frame, which is why they sit on the card modifier — an inline
   style would be the other way to say it and CLAUDE.md rules those out. */
.home-calculator__scale {
  display: flex;
  height: 8px;
  overflow: hidden;
  border-radius: var(--radius-small);
}

.home-calculator__step {
  flex: 5;
}

.home-calculator--bmi .home-calculator__step:nth-child(1) {
  flex: 15;
}

.home-calculator--bmi .home-calculator__step:nth-child(2) {
  flex: 6.5;
}

.home-calculator--whtr .home-calculator__step:nth-child(1) {
  flex: 4;
}

.home-calculator--whtr .home-calculator__step:nth-child(6) {
  flex: 6;
}

.home-calculator--bmr .home-calculator__step:nth-child(1) {
  flex: 8;
}

.home-calculator__step--s1 {
  background: var(--s1);
}

.home-calculator__step--s2 {
  background: var(--s2);
}

.home-calculator__step--s3 {
  background: var(--s3);
}

.home-calculator__step--s4 {
  background: var(--s4);
}

.home-calculator__step--s5 {
  background: var(--s5);
}

.home-calculator__step--s6 {
  background: var(--s6);
}

.home-calculator__body {
  flex: 1;
  font-size: 15px;
  line-height: 1.55;
  color: var(--muted);
}

.home-calculator__cta {
  align-self: flex-start;
  padding: 8px 16px;
  font-size: 14px;
  color: var(--on-accent);
  background: var(--accent);
  border-radius: var(--radius-pill);
}

.home-calculator:hover .home-calculator__cta {
  background: var(--accent-dark);
}

/* --- Band 7: FAQ ------------------------------------------------------------ */

/* Note 08: the title and the disclaimer, then the questions. They stand over
   the list rather than in a column beside it. */
.home-faq {
  padding: 80px var(--gutter);
  background: var(--bg);
}

.home-faq__inner {
  display: flex;
  flex-direction: column;
  gap: 32px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.home-faq__lead {
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-width: 720px;
}

.home-faq__kicker {
  color: var(--accent);
}

.home-faq__title {
  font-size: 34px;
  line-height: 1.18;
  letter-spacing: -0.01em;
}

.home-faq__note {
  font-size: 16px;
  line-height: 1.65;
  color: var(--muted);
}

/* --- The FAQ list of 6a and 9b (templates/components/faq_list.html) ------- */

/* One column on every page and at every width, held to the left edge of its
   title: in two columns an open answer left an empty cell beside it
   (FAQ_WSPOLNY_UKLAD_V01). An entry's narrower column takes it whole. */
.faq-list {
  display: flex;
  flex-direction: column;
  max-width: 800px;
}

.faq-list__item {
  border-top: 1px solid var(--line);
}

/* A folded row holds two lines of the question at least (2 x 22px x 1.3 and
   the padding); static/js/faq.js raises every row of the list to the tallest
   question, so longer ones stand at one height too. */
.faq-list__summary {
  display: grid;
  grid-template-columns: 8px minmax(0, 1fr) 14px;
  align-items: center;
  gap: 18px;
  min-height: max(var(--faq-row, 0px), 98px);
  padding: 20px 0;
  list-style: none;
  cursor: pointer;
}

.faq-list__summary::-webkit-details-marker {
  display: none;
}

.faq-list__bullet {
  width: 8px;
  height: 8px;
  background: var(--accent);
  border-radius: 50%;
}

.faq-list__question {
  font-size: 22px;
  line-height: 1.3;
}

.faq-list__summary:hover .faq-list__question,
.faq-list__summary:focus-visible .faq-list__question {
  color: var(--accent);
}

/* 18 px --muted. The span is aria-hidden, so the generated character stays out
   of the reading order — the state of the row is announced by the <details>. */
.faq-list__sign {
  font-size: 18px;
  line-height: 1;
  color: var(--muted);
  text-align: center;
}

.faq-list__sign::before {
  content: "+";
}

.faq-list__item[open] .faq-list__sign::before {
  content: "–";
}

/* --text and not --muted: the answer is the content of this list, not a note
   under a heading. Indented to the question, past the bullet. */
.faq-list__answer {
  padding: 0 32px 24px 26px;
  font-size: 17px;
  line-height: 1.72;
  color: var(--text);
}

/* An answer of an entry may run to two paragraphs or a list. */
.faq-list__answer > * + * {
  margin-top: 12px;
}

.faq-list__answer a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* The eighth band, "Z bazy wiedzy", is in section 26. It is drawn with
   the card of 9a, and one card cannot stand in two sections. The link out of
   the dark band above — .home-origin__link — is there for the same reason: it
   waited for the entry it points at. */

/* --- Phone (1f): seven bands in one column ---------------------------------- */

/* Every band of this page is a two- or three-track grid, and on a phone every
   one of them is one track. The type comes from frame 1f rather than from a
   scale invented here: 30 px on the H1, 24 px on a band title, 19 px on a FAQ
   question, 17 px on the lead and 16 px on running text — the frame's own inline
   values, read the way section 18 of DESIGN_SPEC.md read the desktop ones.

   One thing the frame draws differently from what ships, and one that is
   easy to get wrong:

   - "Kafle 2x2 zamiast karuzeli: cztery grupy widoczne naraz" (note 02). The
     catalogue has five types, not four, so two columns come out as three rows
     with the fifth alone. The note is about the arrangement — two columns and
     no carousel — and that is what it gets; the count was never ours to pick.
   - The FAQ, note 03: the first question expanded and the rest collapsed.
     A <details> wrapped around the answer would be shut on the desktop too —
     `open` is an attribute, not a declaration. The answer stands outside the
     <details> instead: the element carries its <summary> alone and the
     paragraph beside it obeys `[open] ~ …`, so the phone gets the accordion
     and the desktop hides the whole toggle. No script, and the answer is in
     the tree once. */
@media (max-width: 900px) {
  /* The bands stand 72 to 86 px apart on the desktop, which on a phone is a
     screen of air between two screens of content. Frame 1f draws the hero at
     36/32 and the rest at about 40; the horizontal half is already the token. */
  .home-hero {
    padding: 36px var(--gutter) 32px;
  }

  .home-types,
  .home-origin,
  .home-stages,
  .home-signals,
  .home-calculators,
  .home-faq {
    padding: 40px var(--gutter);
  }

  .home-hero__inner {
    grid-template-columns: minmax(0, 1fr);
    gap: 28px;
  }

  .home-hero__title {
    max-width: none;
    font-size: 30px;
  }

  .home-hero__subtitle {
    max-width: none;
    font-size: 17px;
  }

  /* The box becomes a column, so the 1 px divider between the two selects
     turns from a rule standing up into one lying down. */
  .home-search {
    flex-direction: column;
    align-items: stretch;
    max-width: none;
  }

  /* The field is 44 px, but the select inside it was 22 and the select is what
     answers a touch — the padding around it opens nothing. */
  .home-search__field {
    justify-content: center;
  }

  .home-search__select,
  .home-search__field .place-field__box {
    min-height: 44px;
  }

  .home-search__divider {
    width: auto;
    height: 1px;
  }

  .home-search .btn {
    width: 100%;
  }

  .home-pitch__foot {
    flex-wrap: wrap;
  }

  .home-section-head {
    flex-wrap: wrap;
    gap: 6px 14px;
  }

  .home-section-head__title {
    font-size: 24px;
  }

  .home-types__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .home-origin__inner {
    grid-template-columns: minmax(0, 1fr);
    gap: 28px;
  }

  .home-origin__title {
    font-size: 24px;
  }

  .home-stages__grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .home-stages__title {
    font-size: 24px;
  }

  .home-stage__number {
    font-size: 32px;
  }

  .home-signals__inner {
    gap: 20px;
  }

  .home-signals__list {
    grid-template-columns: minmax(0, 1fr);
  }

  .home-signal:nth-child(2) {
    border-top: 1px solid var(--line-strong);
  }

  .home-signals__title {
    font-size: 24px;
  }

  .home-calculators__grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .home-calculators__title {
    font-size: 24px;
  }

  .home-faq__inner {
    gap: 24px;
  }

  .home-faq__title {
    font-size: 24px;
  }

  /* Two lines of 19px at least (2 x 19px x 1.3 and the padding). */
  .faq-list__summary {
    gap: 12px;
    min-height: max(var(--faq-row, 0px), 82px);
    padding: 16px 0;
  }

  .faq-list__question {
    font-size: 19px;
  }

  .faq-list__answer {
    padding: 0 26px 16px 20px;
    font-size: 16px;
  }
}

/* ==========================================================================
   12. Kalkulatory — frames 1i, 1j, 1k
   Four bands: the intro on --surface, the working area on the page ground, the
   "why" band back on --surface, the questions last. Note 01 beside 1j: "Ta
   sama siatka co w 1i… Trzy kalkulatory różnią się danymi, nie układem."

   Dimensions are the frames' own inline values, written up as DESIGN_SPEC.md
   section 19 — as with section 18 and the home page, the specification said
   nothing about these three screens until then.

   Two exceptions to "bez stylów inline" live here, and both are answers rather
   than styling: the marker offset on the scale and the two flex weights of the
   split bar on 1k. Neither can sit in a stylesheet, because neither is known
   until something has been calculated. The script sets them as custom
   properties and the rules below read them.
   ========================================================================== */

/* An element the calculators hide — the result before it exists, the marker
   before it has a segment, the waist chip without a waist — is hidden. Several
   of them carry a display of their own further down, which would otherwise
   outrank the attribute. */
.calculator [hidden] {
  display: none;
}

/* --- Band 1: intro ---------------------------------------------------------- */

.calculator__intro {
  padding: 44px var(--gutter) 40px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.calculator__intro-inner {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.calculator__crumbs {
  display: flex;
  gap: 8px;
  font-size: 13px;
  color: var(--muted);
}

.calculator__crumb {
  color: var(--muted);
}

/* The separator is drawn rather than written, so it never lands in the reading
   order between two crumb names. */
.calculator__crumb:not(:first-child)::before {
  content: "/";
  margin-right: 8px;
  color: var(--muted);
}

.calculator__crumb--current {
  color: var(--text);
}

/* 42 px, not the 52 px of the scale in README.md. The frames set it on all
   three screens and DESIGN_SPEC.md section 19 records it, the same way section
   18 records the 54 px of 6a. */
.calculator__title {
  font-family: var(--font-serif);
  font-size: 42px;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--text);
}

/* The frames set 620 px on 1i and 640 px on the other two, which is the length
   of the sentence rather than a decision. One value for the three. */
.calculator__lead {
  max-width: 640px;
  font-size: 17px;
  line-height: 1.6;
  color: var(--muted);
  text-wrap: pretty;
}

/* --- Band 1: the switch ----------------------------------------------------- */

/* Not the segmented pill of DESIGN_SPEC.md section 5 — that one is drawn on
   11b and stays there. Section 19 carries the anatomy of this tile. */
.calc-switch {
  display: flex;
  gap: 10px;
  padding-top: 10px;
}

/* The corner and the grounds follow the panel mockup of 24.09: a tile of
   12 px, a pale ground under the pointer, and the chosen one on --tint with
   its name in --accent. The border stays --line-control, because a tile is a
   control and 1.4.11 asks 3:1 of its edge (section 2, --line-control). */
.calc-switch__item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 11px 18px;
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-tile);
}

.calc-switch__item:hover {
  background: var(--hl);
  border-color: var(--accent);
}

.calc-switch__item--on,
.calc-switch__item--on:hover {
  background: var(--tint);
  border-color: var(--accent);
}

.calc-switch__item--on .calc-switch__name {
  font-weight: 500;
  color: var(--accent);
}

.calc-switch__name {
  font-size: 15px;
  color: var(--text);
}

.calc-switch__note {
  font-size: 13px;
  color: var(--muted);
}

/* --- Band 2: the working area ----------------------------------------------- */

.calculator__work {
  padding: 48px var(--gutter) 56px;
}

/* Note 01 beside 1i sets the proportion: "Wynik zajmuje więcej miejsca niż
   formularz. Formularz wypełnia się raz, wynik czyta się przez minutę." */
.calculator__grid {
  display: grid;
  grid-template-columns: 400px 1fr;
  align-items: start;
  gap: 64px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.calculator__output {
  display: flex;
  flex-direction: column;
  gap: 32px;
}

/* --- Band 2: the form ------------------------------------------------------- */

.calc-form {
  display: flex;
  flex-direction: column;
  gap: 22px;
  padding: 28px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.calc-form__kicker {
  color: var(--accent);
}

/* The one button in the card fills it. Its padding is the M of DESIGN_SPEC.md
   section 1 rather than the frame's 15 px, so the site keeps one button and
   not a sixth shape. */
.calc-form .btn {
  width: 100%;
}

/* 1k puts age and height in one row (the frame's 1fr 1fr). */
.calc-form__pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}

/* The same wrapper on 1i and 1j, for a row the phone makes and the desktop
   does not (note 01 beside 11a). One column here with the gap of the card
   around it, so nothing on either desktop frame moves; section 12's phone
   block is where it becomes a row. */
.calc-form__pair--phone {
  grid-template-columns: minmax(0, 1fr);
  gap: 22px;
}

/* Frame 1j: the note that WHtR needs neither sex nor age (note 03). */
.calc-form__aside {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 16px;
  font-size: 14px;
  line-height: 1.55;
  color: var(--text);
  background: var(--tint);
  border-radius: var(--radius-control);
}

.calc-form__aside-mark {
  display: flex;
  flex: none;
  padding-top: 3px;
  color: var(--accent);
}

/* --- Band 2: a number with its unit ----------------------------------------- */

.calc-field {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.calc-field__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

.calc-field__label {
  font-size: 14px;
  color: var(--text);
}

.calc-field__aside {
  font-size: 13px;
  color: var(--muted);
}

/* 52 px and a 19 px value, against the 44 px and 16 px of section 2. On these
   three screens the field is the object of the page rather than one row of a
   long form, and it carries a unit the section 2 field has nowhere to put.
   Section 19 records it as a variant so section 2 keeps saying what it says. */
.calc-field__control {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 52px;
  padding: 0 16px;
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-field);
}

.calc-field__control:focus-within {
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.calc-field__control input {
  width: 100%;
  min-width: 0;
  padding: 0;
  font-family: var(--font-sans);
  font-size: 19px;
  color: var(--text);
  background: transparent;
  border: 0;
}

/* The stepper of type="number" would sit exactly where the unit goes. The
   arrows are the only thing removed; min, max and step keep working and the
   browser keeps checking them. */
.calc-field__control input::-webkit-outer-spin-button,
.calc-field__control input::-webkit-inner-spin-button {
  margin: 0;
  -webkit-appearance: none;
  appearance: none;
}

.calc-field__control input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}

/* The ring belongs to the box, not to the borderless input inside it. */
.calc-field__control input:focus-visible {
  outline: none;
}

.calc-field__control:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.calc-field__unit {
  flex: none;
  font-size: 15px;
  color: var(--muted);
}

.calc-field__hint {
  font-size: 13px;
  line-height: 1.55;
  color: var(--muted);
}

/* --- Band 2: the two halves of "Płeć" --------------------------------------- */

.calc-choices {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 0;
  border: 0;
}

.calc-choices__row {
  display: flex;
  gap: 8px;
}

.calc-choice {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 12px 0;
  font-size: 15px;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-tile);
  cursor: pointer;
}

/* The chosen half is drawn from the input, not from a class the server put
   there: the page is never rendered a second time, so the state has to follow
   the click. */
.calc-choice:has(input:checked) {
  color: var(--text);
  border-color: var(--accent);
}

.calc-choice input,
.calc-level input {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.calc-choice__dot,
.calc-level__dot {
  flex: none;
  width: 14px;
  height: 14px;
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: 50%;
}

.calc-choice:has(input:checked) .calc-choice__dot,
.calc-level:has(input:checked) .calc-level__dot {
  border: 4px solid var(--accent);
}

.calc-choice:has(input:focus-visible),
.calc-level:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* --- Band 2: the activity list of 1k ---------------------------------------- */

/* Note 03: a list with its multipliers, not a select. Choosing means comparing
   the descriptions, so they are all visible at once. */
.calc-levels {
  display: flex;
  flex-direction: column;
  padding: 0;
  border: 0;
}

.calc-levels .calc-field__label {
  padding-bottom: 4px;
}

.calc-level {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 13px 0;
  border-top: 1px solid var(--line);
  cursor: pointer;
}

.calc-level:last-of-type {
  border-bottom: 1px solid var(--line);
}

/* The dot leads, the text takes the middle, the multiplier stays right. */
.calc-level__dot {
  order: -1;
}

.calc-level__text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-right: auto;
  margin-left: 11px;
}

.calc-level__name {
  font-size: 15px;
  color: var(--muted);
}

.calc-level:has(input:checked) .calc-level__name {
  color: var(--text);
}

.calc-level__note {
  font-size: 13px;
  color: var(--muted);
}

.calc-level__factor {
  flex: none;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--muted);
}

/* --- Band 2: the result card ------------------------------------------------ */

.calc-result {
  display: flex;
  flex-direction: column;
  gap: 26px;
  padding: 30px 32px;
  background: var(--surface);
  border: 1px solid var(--accent);
  border-top: 5px solid var(--accent);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.calc-result__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 32px;
}

.calc-result__figure {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.calc-result__kicker {
  color: var(--accent);
}

.calc-result__line {
  display: flex;
  align-items: baseline;
  gap: 16px;
}

.calc-result__line--tight {
  gap: 10px;
}

.calc-result__value {
  font-family: var(--font-serif);
  font-size: 64px;
  font-weight: 500;
  line-height: 1;
  color: var(--text);
}

.calc-result__band {
  font-family: var(--font-serif);
  font-size: 26px;
  font-weight: 500;
}

/* The qualifier takes the colour of the segment the value fell into. WHtR and
   the energy line keep the accent, as the frames draw them. */
.calc-result__band[data-colour="accent"] {
  color: var(--accent);
}

.calc-result__band[data-colour="s1"] {
  color: var(--s1);
}

.calc-result__band[data-colour="s2"] {
  color: var(--s2);
}

.calc-result__band[data-colour="s3"] {
  color: var(--s3);
}

.calc-result__band[data-colour="s4"] {
  color: var(--s4);
}

.calc-result__band[data-colour="s5"] {
  color: var(--s5);
}

.calc-result__band[data-colour="s6"] {
  color: var(--s6);
}

/* The waist chip of 1i (note 03). --s5 above the threshold, as the frame draws
   it; --s2 below it, staying inside the same scale. */
.calc-result__chip {
  flex: none;
  padding: 6px 13px;
  font-size: 13px;
  color: var(--on-accent);
  border-radius: var(--radius-pill);
}

.calc-result__chip[data-colour="s2"] {
  background: var(--s2);
}

.calc-result__chip[data-colour="s5"] {
  background: var(--s5);
}

/* 1j: the division itself, demoted beside the number (note 02). */
.calc-result__ratio {
  flex: none;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.6;
  color: var(--muted);
  text-align: right;
}

/* 1k: the multiplication and the name of the formula, on two lines. */
.calc-result__formula {
  display: flex;
  flex: none;
  flex-direction: column;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.6;
  color: var(--muted);
  text-align: right;
}

.calc-result__reading {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
}

/* 1j, note 02: the sentence that carries the meaning stands above the reading,
   because the ratio on its own says nothing. */
.calc-result__highlight {
  padding: 16px 18px;
  font-size: 16px;
  line-height: 1.55;
  color: var(--text);
  background: var(--tint);
  border-radius: var(--radius-control);
}

/* Note 04 beside 1i: the sentence about who makes a diagnosis lives here, in
   the text of the result, and not in a footer nobody reads. */
.calc-result__text {
  font-size: 17px;
  line-height: 1.65;
  color: var(--text);
  text-wrap: pretty;
}

/* Note 05: the calculator ends by walking into the catalogue. */
.calc-result__actions {
  display: flex;
  align-items: center;
  gap: 20px;
  padding-top: 4px;
}

.calc-result__aside {
  font-size: 15px;
}

/* --- Band 2: the scale ------------------------------------------------------

   The knowledge-base tile beside an entry (section 26) draws the same bar,
   without ticks and without a marker. */

.calc-scale {
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.calc-scale__bar {
  display: flex;
  height: 14px;
  margin-top: 16px;
}

/* Segments, never a gradient: DESIGN_SPEC.md, "Czego nie ma w systemie", and
   note 02 beside 1i, which asks for a scale rather than a traffic light. */
.calc-scale__segment {
  position: relative;
  height: 100%;
  border-right: 2px solid var(--surface);
}

.calc-scale__segment:first-child {
  border-radius: var(--radius-small) 0 0 var(--radius-small);
}

.calc-scale__segment:last-child {
  border-right: 0;
  border-radius: 0 var(--radius-small) var(--radius-small) 0;
}

.calc-scale__segment--s1 {
  background: var(--s1);
}

.calc-scale__segment--s2 {
  background: var(--s2);
}

.calc-scale__segment--s3 {
  background: var(--s3);
}

.calc-scale__segment--s4 {
  background: var(--s4);
}

.calc-scale__segment--s5 {
  background: var(--s5);
}

.calc-scale__segment--s6 {
  background: var(--s6);
}

/* The segment weights of the two scales. Not the weights of section 18 — those
   size the tile on the home page, which draws the same classification at a
   different width. 1k has no scale at all: it has no thresholds. */
.calc-scale--bmi .calc-scale__segment:nth-child(1),
.calc-scale--bmi .calc-scale__tick:nth-child(1) {
  flex: 15;
}

.calc-scale--bmi .calc-scale__segment:nth-child(2),
.calc-scale--bmi .calc-scale__tick:nth-child(2) {
  flex: 6.5;
}

.calc-scale--bmi .calc-scale__segment:nth-child(n + 3),
.calc-scale--bmi .calc-scale__tick:nth-child(n + 3) {
  flex: 5;
}

.calc-scale--whtr .calc-scale__segment:nth-child(1),
.calc-scale--whtr .calc-scale__tick:nth-child(1) {
  flex: 8;
}

.calc-scale--whtr .calc-scale__segment:nth-child(2),
.calc-scale--whtr .calc-scale__tick:nth-child(2),
.calc-scale--whtr .calc-scale__segment:nth-child(3),
.calc-scale--whtr .calc-scale__tick:nth-child(3) {
  flex: 10;
}

.calc-scale--whtr .calc-scale__segment:nth-child(4),
.calc-scale--whtr .calc-scale__tick:nth-child(4) {
  flex: 12;
}

/* The answer, as a position. --marker is the offset inside its own segment,
   which is where the frame puts it; a percentage of the whole bar would drift
   by the two-pixel gaps. Centred on the value, so a result sitting exactly on
   a threshold straddles the line rather than leaning off it. */
.calc-scale__marker {
  position: absolute;
  top: -11px;
  bottom: -11px;
  left: var(--marker, 0%);
  width: 3px;
  background: var(--text);
  transform: translateX(-50%);
}

.calc-scale__marker::after {
  position: absolute;
  top: -8px;
  left: 50%;
  width: 12px;
  height: 12px;
  content: "";
  background: var(--text);
  border-radius: 50%;
  transform: translateX(-50%);
}

.calc-scale__ticks {
  display: flex;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
}

.calc-scale__tick[data-current] {
  color: var(--text);
}

/* --- Band 2: the split bar of 1k -------------------------------------------- */

.calc-split {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.calc-split__bar {
  display: flex;
  height: 36px;
  overflow: hidden;
  border-radius: var(--radius-small);
}

/* Note 02: the two parts stand in the real proportion, so the picture shows
   that movement is a quarter of the budget and not half. The weights are the
   two figures themselves and arrive as custom properties. */
.calc-split__part {
  display: flex;
  align-items: center;
  padding-left: 14px;
  font-size: 14px;
  white-space: nowrap;
}

.calc-split__part--basal {
  flex: var(--basal, 3);
  color: var(--on-accent);
  background: var(--accent);
}

.calc-split__part--movement {
  flex: var(--movement, 1);
  color: var(--text);
  background: var(--tint);
}

.calc-split__note {
  font-size: 14px;
  line-height: 1.55;
  color: var(--muted);
}

/* --- Band 2: the table beside the result ------------------------------------ */

.calc-table {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.calc-table__kicker {
  color: var(--accent);
}

.calc-table__rows {
  display: flex;
  flex-direction: column;
}

.calc-table__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 13px 0;
  border-top: 1px solid var(--line);
}

.calc-table__row:last-child {
  border-bottom: 1px solid var(--line);
}

.calc-table__label {
  font-size: 15px;
  color: var(--muted);
}

.calc-table__value {
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--muted);
}

/* The row the value fell into. The bar takes the colour of its own segment, of
   which there are six and all are known in advance — so it is an attribute
   selector rather than another number passed in from outside. */
.calc-table__row[data-current] {
  padding-right: 12px;
  padding-left: 12px;
  background: var(--hl);
  border-left: 4px solid var(--line-strong);
}

.calc-table__row[data-current] .calc-table__label,
.calc-table__row[data-current] .calc-table__value {
  color: var(--text);
}

.calc-table__row[data-current][data-band="s1"] {
  border-left-color: var(--s1);
}

.calc-table__row[data-current][data-band="s2"] {
  border-left-color: var(--s2);
}

.calc-table__row[data-current][data-band="s3"] {
  border-left-color: var(--s3);
}

.calc-table__row[data-current][data-band="s4"] {
  border-left-color: var(--s4);
}

.calc-table__row[data-current][data-band="s5"] {
  border-left-color: var(--s5);
}

.calc-table__row[data-current][data-band="s6"] {
  border-left-color: var(--s6);
}

/* 1k marks the chosen level with the tint alone — the frame draws no bar
   there, because the row is a choice rather than a threshold. */
.calc-table--levels .calc-table__row[data-current] {
  background: var(--tint);
  border-left: 0;
}

/* --- Band 3: what the calculator does not show ------------------------------ */

.calculator__why {
  padding: 52px var(--gutter) 60px;
  background: var(--surface);
  border-top: 1px solid var(--line);
}

.calculator__why-inner {
  display: flex;
  flex-direction: column;
  gap: 26px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.calculator__why-title {
  font-family: var(--font-serif);
  font-size: 28px;
  font-weight: 500;
  color: var(--text);
}

.calculator__why-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 40px;
}

.calculator__why-item {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.calculator__why-name {
  font-size: 18px;
  font-weight: 500;
  color: var(--text);
}

.calculator__why-text {
  font-size: 16px;
  line-height: 1.6;
  color: var(--muted);
  text-wrap: pretty;
}

.calculator__caveat {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 20px 22px;
  background: var(--tint);
  border-radius: var(--radius-card);
}

.calculator__caveat-text {
  font-size: 15px;
  line-height: 1.5;
  color: var(--muted);
}

/* --- Band 4: the questions -------------------------------------------------- */

.calculator__faq {
  padding: 64px var(--gutter) 72px;
  border-top: 1px solid var(--line);
}

.calculator__faq-inner {
  display: grid;
  grid-template-columns: 280px 1fr;
  align-items: start;
  gap: 80px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.calculator__faq-aside {
  position: sticky;
  top: 24px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.calculator__faq-kicker {
  color: var(--accent);
}

.calculator__faq-cta {
  padding-top: 2px;
  font-size: 15px;
}

.calculator__faq-list {
  display: flex;
  flex-direction: column;
  max-width: 760px;
}

.calculator__faq-item {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 22px 0;
  border-top: 1px solid var(--line);
}

.calculator__faq-question {
  font-family: var(--font-serif);
  font-size: 21px;
  font-weight: 500;
  color: var(--text);
}

.calculator__faq-answer {
  font-size: 17px;
  line-height: 1.7;
  color: var(--text);
  text-wrap: pretty;
}

.calculator__faq-footnote {
  margin-top: 18px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--muted);
}

/* --- Phone (11a): one column, the pair, the result on another ground -------- */

/* Note 02 beside 11a is why .calc-result changes colour here and only here:
   "Wynik na innym tle, od razu pod formularzem. Na telefonie nie ma dwóch
   kolumn, więc rozdziela je kolor." On the desktop the 64 px gap between the
   two tracks does that work; stacked, two --surface cards would read as one.

   The four bands lose the vertical air of a 1440 px page, and the title steps
   down to the 30 px the frame draws. The card's own 28 px padding goes to 18 so
   that a pair of number fields still has room between the edges. */
@media (max-width: 900px) {
  .calculator__intro {
    padding: 32px var(--gutter) 28px;
  }

  .calculator__title {
    font-size: 30px;
  }

  .calc-switch {
    flex-wrap: wrap;
  }

  .calculator__work {
    padding: 28px var(--gutter) 36px;
  }

  .calculator__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 24px;
  }

  .calc-form {
    padding: 18px;
  }

  /* Note 01: height and weight in one row, the waist under them. */
  .calc-form__pair--phone {
    grid-template-columns: 1fr 1fr;
    gap: 14px;
  }

  .calc-result {
    background: var(--band);
  }

  /* The waist chip, the division and the formula do not fit beside a 64 px
     figure in a phone column, so they go under it, aligned with it. */
  .calc-result__head {
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 12px 32px;
  }

  .calc-result__ratio,
  .calc-result__formula {
    text-align: left;
  }

  /* The basal part keeps its real proportion, so its label is the one that
     breaks onto a second line; the movement part is the narrow one and keeps
     its label whole. */
  .calc-split__part--basal {
    line-height: 1.2;
    white-space: normal;
  }

  .calculator__why {
    padding: 32px var(--gutter);
  }

  .calculator__why-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  /* The link out of the strip goes under the sentence: beside it, at its full
     width, it would push the page wider than the phone. */
  .calculator__caveat {
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
  }

  .calculator__faq {
    padding: 32px var(--gutter);
  }

  .calculator__faq-inner {
    grid-template-columns: minmax(0, 1fr);
    gap: 24px;
  }

  .calculator__faq-answer {
    font-size: 16px;
  }
}

/* ==========================================================================
   13. Error pages — frames 2g and 11g
   One band between the header and the footer, in two widths: 1180 px for the
   404, which lines up with every other page, and 620 px for the 500, where the
   paragraph has to stay readable. Note 01 beside both frames: "jedno zdanie
   i wyjścia. Bez wielkiej cyfry, bez ilustracji, bez żartu."

   Dimensions are the frames' own, written up as DESIGN_SPEC.md section 20 —
   the specification said nothing about these two screens until then, as with
   section 18 and section 19 before them.

   Four values come from the specification rather than the frame, and section 20
   carries the reasons: button size M, a 10 px gap between the two buttons, the
   0.12em of .t-overline, and no colour anywhere but the label of 11g (note 05:
   "Awaria nie musi krzyczeć").

   The footer does not float up the window on either page — body carries
   min-height and .site-main the flex grow, both in section 6, where the comment
   names the 404 as the reason.
   ========================================================================== */

.error-page {
  padding: 120px var(--gutter) 140px;
}

.error-page__inner {
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 1180px;
  margin: 0 auto;
}

/* Frame 11g. The narrower column is not a preference: the paragraph is the only
   long line on either page and 1180 px would run it past a comfortable measure. */
.error-page__inner--narrow {
  gap: 22px;
  max-width: 620px;
}

.error-page__kicker {
  color: var(--muted);
}

/* Note 05 beside 11g: the alert colour reaches the label and stops there. */
.error-page__kicker--alert {
  color: var(--alert);
}

/* 38 px, not the 52 px of the scale in README.md. The exception holds for these
   two screens only and is recorded in DESIGN_SPEC.md section 20, so that nobody
   corrects it here or moves the scale because of it. */
.error-page__title {
  font-size: 38px;
  line-height: 1.16;
  letter-spacing: -0.01em;
}

.error-page__lead {
  font-size: 17px;
  line-height: 1.7;
  color: var(--muted);
}

/* Frame 2g. The ways out are a stack of plain links, not buttons: three buttons
   in a row would rank them, and the frame gives them equal weight. */
.error-page__exits {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 6px;
}

.error-page__exit {
  font-size: 18px;
  color: var(--accent);
}

/* DESIGN_SPEC.md section 1, "Grupa przycisków": 10 px between two buttons, and
   16 px only when the second one is quiet. This second one is secondary. */
.error-page__actions {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-top: 6px;
}

/* Frame 11g, the card between the buttons and the fine print. It waited for
   "Kontakt →" to have an address to point at, and the contact page of
   section 32 is that address. The frame's own values: --surface inside 1 px of
   --line, a sentence in --muted on the left and the link on the right, which is
   why the link is flex: none — the sentence wraps and the link never does. */
.error-page__contact {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  margin-top: 10px;
  padding: 18px 20px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.error-page__contact-text {
  font-size: 15px;
  line-height: 1.55;
  color: var(--muted);
}

.error-page__contact-link {
  flex: none;
  font-size: 15px;
  color: var(--accent);
}

/* Note 04 beside 11g: the event id and the time in fine print. */
.error-page__event {
  padding-top: 4px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
}

/* --- Phone: the band loses the air a 1440 px page could afford ------------- */

/* No phone frame was drawn for either screen — the inventory in README.md puts
   11g under "Widoki publiczne", at 1440 px, and there is no 390 px twin. The
   band is the only thing that has to change: 120/140 of vertical padding is
   most of a phone screen before the sentence starts. The 38 px heading of
   section 20 steps down the way the other headings do on a phone. */
@media (max-width: 900px) {
  .error-page {
    padding: 56px var(--gutter) 72px;
  }

  .error-page__title {
    font-size: 28px;
  }

  .error-page__actions {
    flex-direction: column;
    align-items: flex-start;
  }

  /* The sentence needs about 300 px on its own and the design width of section
     15 is 390, so the link takes its own line under it rather than squeezing
     the sentence into a column of words. */
  .error-page__contact {
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
  }
}

/* ==========================================================================
   14. Konto — frames 2a and 2b
   One centred column between the header and the footer, in two widths: 400 px
   for the sign-in card and 820 px for the fork. Note 02 beside 2a rules out
   everything else on that screen — "bez ilustracji i bez drugiej szpalty".

   Dimensions are written up as DESIGN_SPEC.md section 21, together with the
   four places the frames disagree with the specification. Three of them go to
   the document, as in section 20: the label is 15 px and not 14, the control
   text 16 px and not 15, and the button keeps its M padding. The fourth keeps
   the frame: 38 px for the heading of 2b, the same exception section 20 made
   for the error pages.

   The 46 px control is not an exception at all — section 2 allows 46 to 48 px
   for single-column forms, and this is one.
   ========================================================================== */

.auth {
  padding: 96px var(--gutter) 120px;
  display: flex;
  justify-content: center;
}

/* 2b stands taller on the page than 2a: its column is twice as wide, so the
   same padding would leave it floating. */
.auth--wide {
  padding: 88px var(--gutter) 104px;
}

.auth__column {
  display: flex;
  flex-direction: column;
  gap: 22px;
  width: 400px;
}

.auth__column--wide {
  gap: 32px;
  width: 820px;
}

.auth__intro {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.auth__column--wide .auth__intro {
  gap: 10px;
}

.auth__kicker {
  color: var(--accent);
}

/* 34 px and 38 px, neither of them on the scale in README.md. The exception is
   the one section 20 already made for 2g and 11g and section 18 for the hero:
   a one-off page heading takes the frame's value. It is recorded in section 21
   so that nobody corrects it here or moves the scale because of it. */
.auth__title {
  font-size: 34px;
  line-height: 1.16;
  letter-spacing: -0.01em;
}

.auth__title--wide {
  font-size: 38px;
}

.auth__lead {
  font-size: 15px;
  line-height: 1.55;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.auth__form {
  display: flex;
  flex-direction: column;
  gap: 22px;
}

/* The one button in the column fills it, like the calculator card. */
.auth__form .btn {
  width: 100%;
}

/* The line under the card is separated by a rule, not by a gap — the frame
   draws the border and then pushes the text 14 px below it. */
.auth__switch {
  display: flex;
  justify-content: center;
  gap: 7px;
  padding-top: 14px;
  font-size: 15px;
  color: var(--muted);
  border-top: 1px solid var(--line);
}

/* 2b has no rule above the line: the two cards already close the block. */
.auth__switch--plain {
  padding-top: 0;
  border-top: 0;
}

/* --- Field of the sign-in card ------------------------------------------- */

.auth-field {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

/* The label row carries "Nie pamiętam hasła" opposite the label, which is the
   whole reason this is not components/field.html. Baseline alignment, because
   the two sit at different sizes. */
.auth-field__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

/* The line height of .field__label, so a label stands as far above its box
   here as it does on every other form. Inherited, it was the 1.65 of body text. */
.auth-field__label {
  font-size: 15px;
  line-height: 1.4;
  color: var(--text);
}

.auth-field__aside {
  font-size: 13px;
  color: var(--accent);
}

/* Two phone-only additions, both hidden here.

   The reveal is drawn by frame 11c and by no desktop frame — note 02 gives the
   reason ("literówka na klawiaturze ekranowej jest częstsza niż podglądanie
   przez ramię"), which is a statement about a phone. It is a different element
   from the reveal of 9d, which lives in the same row and shows on every width:
   there the password is being chosen, here it is being recalled.

   The short version of the link beside it belongs to note 03. */
.auth-field__aside--reveal {
  display: none;
}

/* Except while the password is actually showing. Hiding the control at a width
   the field can be carried across — a tablet turned from portrait to landscape,
   a window dragged wider — would leave the password on screen with no way to
   put it back. The attribute is the button's own state, kept by
   static/js/password.js, so the rule needs nothing else to know about it. */
.auth-field__aside--reveal[aria-pressed="true"] {
  display: inline-flex;
  align-items: center;
  /* The target has to survive the crossing too. This rule exists for a device
     with a finger — a tablet turned on its side — and section 15 asks for 44 px
     there; without it the only way back to a hidden password is one line of
     13 px text. */
  min-height: 44px;
  padding: 0;
  margin: -11px 0 -11px auto;
  font-family: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

.auth-field__aside-phone {
  display: none;
}

/* The select is here because a select is a control of this card too. Left to
   the browser it comes out at 24 px next to the 46 px of every input beside
   it — measured on 2d, a defect on both widths, and the section 21 variant
   already says what the height is. */
/* Full width and no minimum, so the box takes the width of its track and
   never the intrinsic width of twenty characters, which pushed a two-column
   pair past its column. */
.auth-field input,
.auth-field select {
  width: 100%;
  min-width: 0;
  height: 46px;
  padding: 0 14px;
  font-family: var(--font-sans);
  font-size: 16px;
  color: var(--text);
  background-color: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-field);
}

/* Room for the arrow drawn beside .field select, which this block's padding
   would otherwise take back. */
.auth-field select {
  padding-right: 40px;
}

.auth-field input::placeholder {
  color: var(--muted);
}

/* Section 2: the focus ring is the border going to --accent, and the thickness
   does not change — a border that thickens on focus moves everything below it. */
.auth-field input:focus,
.auth-field select:focus {
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.auth-field--error input,
.auth-field--error select {
  border-color: var(--alert);
}

.auth-field__hint {
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
}

/* An empty field still says so under itself. What moves above the form is the
   rejected sign-in, which is about the pair and not about either box. */
.auth-field__error {
  font-size: 14px;
  line-height: 1.5;
  color: var(--alert);
}

/* --- The fork, frame 2b --------------------------------------------------- */

.path-choice {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

/* Note 01: both cards carry the same weight. Same padding, same minimum height,
   same type — the accent border below is the only difference between them. */
.path-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 210px;
  padding: 28px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

/* "ścieżka specjalisty dostaje tylko obwódkę w akcencie, bo tam jest
   zobowiązanie finansowe" — the 5 px top edge is the same mark the header
   carries, and it is the whole of the difference. */
.path-card--committed {
  border-color: var(--accent);
  border-top: 5px solid var(--accent);
}

/* Clickable in full, so it follows the card of section 4: the border firms up
   and the card lifts (--shadow-raised, panel mockup). No scaling, no motion. */
.path-card:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow-raised);
}

.path-card__kicker {
  color: var(--accent);
}

.path-card__title {
  font-family: var(--font-serif);
  font-size: 26px;
  line-height: 1.2;
  font-weight: 500;
  color: var(--text);
}

/* flex: 1 pushes "Dalej →" to the bottom edge, so the two cards line their
   last line up however long the sentences above them run. */
.path-card__text {
  flex: 1;
  font-size: 15px;
  line-height: 1.6;
  color: var(--muted);
}

.path-card__go {
  font-size: 15px;
  color: var(--accent);
}

/* --- Phone (11c): 48 px fields, the reveal, the shortened link ------------- */

/* Note 01 beside 11c: "Ten sam formularz co na desktopie, pola 48 px. Nic nie
   ubywa i nic nie dochodzi." The two fixed column widths are the reason this
   block cannot be skipped — 400 px and 820 px are wider than a 360 px screen,
   which is exactly the horizontal scrolling nothing here may cause. They
   become maxima; the column takes what there is.

   Note 02 turns the reveal on, note 03 shortens the link beside it. Both are in
   the markup on every width (accounts/partials/auth_field.html) and both are
   shown here, because "tylko na telefonie" is a rule about width and the words
   cannot come out of a stylesheet. */
@media (max-width: 900px) {
  .auth {
    padding: 40px var(--gutter) 56px;
  }

  .auth--wide {
    padding: 36px var(--gutter) 48px;
  }

  .auth__column,
  .auth__column--wide {
    width: 100%;
  }

  .auth__column {
    max-width: 400px;
  }

  .auth__column--wide {
    max-width: 820px;
    gap: 24px;
  }

  .auth__title {
    font-size: 26px;
  }

  .auth__title--wide {
    font-size: 28px;
  }

  /* Three items on the row now — label, "Pokaż", "Nie pamiętam" — so it is
     allowed to break rather than squeeze the label. */
  .auth-field__head {
    flex-wrap: wrap;
  }

  /* A button dressed as the link beside it: the reset drops what a button
     brings with it, the auto margin puts it next to that link rather than in
     the middle of the row, and 44 px is the area a finger has to find (the row
     wraps for it, above). */
  /* The negative block margin keeps the 44 px target without letting it
     heighten the row: at full height it stood the label of "Hasło" twice as far
     from its box as the label of "E-mail" above. */
  .auth-field__aside--reveal {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0;
    margin: -11px 0 -11px auto;
    font-family: inherit;
    background: none;
    border: 0;
    cursor: pointer;
  }

  .auth-field__aside--reveal[hidden] {
    display: none;
  }

  .auth-field__aside-wide {
    display: none;
  }

  .auth-field__aside-phone {
    display: inline;
  }

  .auth-field input,
  .auth-field select {
    height: 48px;
  }

  /* Two cards of 160 px would be two headings wrapped to three lines each. */
  .path-choice {
    grid-template-columns: minmax(0, 1fr);
  }

  .path-card {
    padding: 20px;
  }
}

/* ==========================================================================
   15. Konto — rejestracja pacjenta i potwierdzenie adresu (2c, 9e)
   2c extends the block of section 14: the same centred column, one width wider
   (440 px against 400) and one band shorter, with a way back to the fork above
   the heading. 9e is not a card and gets a block of its own — a heading, a list
   of states and a panel, in a 680 px column.

   Written up as DESIGN_SPEC.md section 22 together with the places the frames
   disagree with the specification. Three of them repeat the resolutions of
   section 21: the consent box is 16×16 with a 15 px label (section 2) and not
   18×18 with 14, and the button keeps its M padding. The fourth is not a
   dimension — the frame sends "Załóż konto" to 2m, and it goes to 9e, the
   screen whose own title is "Po rejestracji".
   ========================================================================== */

.auth--form {
  padding: 80px var(--gutter) 104px;
}

.auth__column--form {
  width: 440px;
}

/* "← Zmień typ konta". The fork is one click back and the arrow is part of the
   copy, not an icon — nothing else on the screen needs one. */
.auth__back {
  font-size: 14px;
  color: var(--muted);
}

/* --- Confirm the address, frame 9e ---------------------------------------- */

.confirm {
  padding: 72px var(--gutter) 96px;
}

.confirm__column {
  display: flex;
  flex-direction: column;
  gap: 28px;
  max-width: 680px;
  margin: 0 auto;
}

/* The landing page of the link carries one sentence and one button, so it keeps
   the column of 2a rather than the 680 px of 9e — a lone button under a heading
   twice as wide reads as a page that failed to load the rest of itself. */
.confirm__column--narrow {
  max-width: 460px;
}

.confirm__intro {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* The one pill in the project that is not a filter or a tag: a mono overline on
   the accent tint, 20 px radius like every other pill in section 5. align-self
   keeps it the width of its own text inside a column stretch. */
.confirm__pill {
  align-self: flex-start;
  padding: 6px 14px;
  color: var(--accent);
  background: var(--tint);
  border-radius: var(--radius-pill);
}

/* 38 px, the value of the frame. The same exception sections 18 to 21 make for
   a one-off page heading — the scale in README.md offers nothing nearer.
   The heading can carry the address the link went to, and an address without
   a hyphen has nowhere to break: `anywhere` breaks only a string wider than
   the column, so a word that fits is left whole. */
.confirm__title {
  font-size: 38px;
  line-height: 1.15;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}

.confirm__lead {
  font-size: 17px;
  line-height: 1.7;
  color: var(--muted);
}

/* Note 02 beside the frame: "Trzy kroki jako lista stanów, nie pasek postępu."
   One card, rows divided by a rule, and the state is on the row. */
.steps {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.step {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 20px 24px;
  border-bottom: 1px solid var(--line);
}

.step:last-child {
  border-bottom: 0;
}

/* The open step stands on the accent tint, which is the whole of "otwarty na
   tle wypełnienia" from note 02. */
.step--open {
  background: var(--tint);
}

.step__mark {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  font-family: var(--font-mono);
  font-size: 12px;
  border-radius: 50%;
}

.step--done .step__mark {
  color: var(--on-accent);
  background: var(--ok);
}

.step--open .step__mark {
  color: var(--accent);
  border: 2px solid var(--accent);
}

.step__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 3px;
}

.step__title {
  font-size: 16px;
  color: var(--text);
}

.step__note {
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
}

/* Resending sends mail and writes a date, so it is a POST and the frame's link
   is a button — the same move the sign-out item makes. display: contents
   keeps the form out of the layout, so the button stays inside the sentence it
   ends. */
.step__resend {
  display: contents;
}

.step__resend button {
  padding: 0;
  font-family: inherit;
  font-size: 14px;
  color: var(--accent);
  background: none;
  border: 0;
  cursor: pointer;
}

.step__resend button:hover {
  text-decoration: underline;
}

.confirm__actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Note 05: the commonest reason a message does not arrive is a typo, not a spam
   filter, so the panel offers the account rather than an apology. */
.confirm__help {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 20px 22px;
  font-size: 15px;
  line-height: 1.5;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.confirm__help a {
  flex: none;
  color: var(--accent);
}

/* --- Phone: the two bands of this section lose their desktop air ----------- */

@media (max-width: 900px) {
  .auth--form {
    padding: 36px var(--gutter) 48px;
  }

  /* The third fixed column width of the account screens — 440 px, wider than
     a 360 px screen. A maximum, like the other two. */
  .auth__column--form {
    width: 100%;
    max-width: 440px;
  }

  .confirm {
    padding: 36px var(--gutter) 48px;
  }

  /* The phone H1 of section 41.7. At 38 px an address takes four lines of a
     320 px screen. */
  .confirm__title {
    font-size: 30px;
  }

  .confirm__help {
    flex-wrap: wrap;
  }
}

/* ==========================================================================
   16. Konto — rejestracja specjalisty (2d)
   The one sign-up with two columns. Note 01 beside the frame puts the price in
   a rail next to the form — "czyta się go w chwili decyzji, nie dzień
   wcześniej" — and note 04 makes that rail sticky, so this screen does not
   extend the centred block of sections 14 and 15. What it does reuse is
   everything inside a field: .auth-field, .field and .choice are untouched.
   Anatomy and the frame-versus-document differences: DESIGN_SPEC.md section 23.
   ========================================================================== */

.signup-specialist {
  padding: 56px var(--gutter) 88px;
}

/* minmax(0, 1fr): a bare 1fr has the minimum of its content, and at 901–963 px
   the form column refused to shrink and pushed the rail off the screen. */
.signup-specialist__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: 80px;
  align-items: start;
  max-width: var(--content-max);
  margin: 0 auto;
}

.signup-specialist__column {
  display: flex;
  flex-direction: column;
  gap: 24px;
  max-width: 620px;
}

.signup-specialist__form {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Not .auth__form: that class stretches its button across the column (section
   14), and the frame gives this one the width of its text. */
.signup-specialist__form .btn {
  align-self: flex-start;
}

/* A grid rather than two fields in a row, so a message under one of them does
   not push its neighbour out of line. The pair splits when a box would drop
   under 200 px: between 901 and about 1000 px the rail leaves the form a narrow
   column, and "Online i w gabinecie" was cut off in half of it. */
.signup-specialist__pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
  align-items: start;
}

.signup-specialist__consents {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-top: 4px;
}

/* --------------------------------------------------------------------------
   The "Kim jesteś dla pacjenta" group
   -------------------------------------------------------------------------- */

.type-choice {
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.type-choice__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
}

.type-choice__label {
  font-size: 15px;
  color: var(--text);
}

/* Equal columns rather than a wrapping row. Five pills of different widths
   broke four and one on a desktop and two, two and one on a phone, each row
   ragged against the fields under it; a grid lines them up with the column.
   160 px is the widest pill, "Psychodietetyk" with its box, so the column
   count follows the width of the form: three on 2d and 2h, two on a phone. */
.type-choice__options {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 8px;
}

/* The whole pill is the click target, which is why the label wraps its own box
   here and stands beside it in consent_field.html — there is no link inside
   these two to fight over. */
.type-choice__option {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 11px 16px;
  font-size: 15px;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-tile);
  cursor: pointer;
}

/* The real checkbox stays in the accessibility tree and on the keyboard path;
   what is drawn is .type-choice__mark beside it. Not display:none, which would
   take it out of both. */
.type-choice__option input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.type-choice__mark {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  color: transparent;
  border: 1px solid var(--line-control);
  border-radius: var(--radius-small);
}

.type-choice__option:has(input:checked) {
  color: var(--text);
  border-color: var(--accent);
}

.type-choice__option:has(input:checked) .type-choice__mark {
  color: var(--on-accent);
  background: var(--accent);
  border-color: var(--accent);
}

/* The box is invisible, so the ring has to sit on the pill or the keyboard
   loses its place entirely. */
.type-choice__option:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.type-choice--error .type-choice__option {
  border-color: var(--alert);
}

/* --------------------------------------------------------------------------
   The subscription rail
   -------------------------------------------------------------------------- */

.pricing-rail {
  position: sticky;
  top: 24px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 26px;
  background: var(--surface);
  border: 1px solid var(--accent);
  border-top: 5px solid var(--accent);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.pricing-rail__intro {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.pricing-rail__kicker {
  color: var(--accent);
}

.pricing-rail__prices {
  display: flex;
  align-items: stretch;
  gap: 18px;
  padding: 16px 18px;
  background: var(--tint);
  border-radius: 6px;
}

.pricing-rail__price {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.pricing-rail__price:first-child {
  padding-right: 18px;
  border-right: 1px solid var(--line-strong);
}

.pricing-rail__amount {
  font-family: var(--font-serif);
  font-size: 26px;
  font-weight: 500;
  color: var(--text);
}

.pricing-rail__period {
  font-size: 12px;
  color: var(--muted);
}

.pricing-rail__list {
  display: flex;
  flex-direction: column;
  gap: 9px;
  margin: 0;
  padding: 14px 0 0;
  list-style: none;
  border-top: 1px solid var(--line);
}

.pricing-rail__list li {
  display: flex;
  gap: 10px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text);
}

.pricing-rail__list li::before {
  content: "";
  flex: none;
  width: 5px;
  height: 5px;
  margin-top: 8px;
  background: var(--accent);
  border-radius: 50%;
}

.pricing-rail__note {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 16px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--muted);
  border-top: 1px solid var(--line);
}

/* --------------------------------------------------------------------------
   The third row of the state list on 9e, and the way out beside it
   -------------------------------------------------------------------------- */

/* Note 02 beside 9e: "Krok pierwszy odhaczony, drugi otwarty na tle
   wypełnienia, trzeci szary." The third variant of the mark, and the only one
   with nothing on this screen able to close it. */
.step--waiting .step__mark {
  color: var(--muted);
  border: 1px solid var(--line-strong);
}

.step--waiting .step__title {
  color: var(--muted);
}

/* Note 04: "«Zrobię to później» jest linkiem, nie przyciskiem. Konto już
   istnieje; nie blokujemy wyjścia." */
.confirm__later {
  align-self: center;
  font-size: 15px;
  color: var(--muted);
}

/* --- Phone: the one two-column sign-up becomes one column ----------------- */

/* Note 01 beside 2d puts the price rail beside the form. In one column it goes
   above it, which is where the frame's own sentence about the free year wants
   to be read anyway — before the form, not after it. The paired fields split:
   at 390 px two controls in a row leave 140 px each, and these are a PWZ number
   and a city. */
@media (max-width: 900px) {
  .signup-specialist {
    padding: 32px var(--gutter) 48px;
  }

  .signup-specialist__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 28px;
  }

  .signup-specialist__pair {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ==========================================================================
   17. Konto — reset hasła (9c, 9d)
   ==========================================================================

   The shortest of the account sections, because note 01 beside 9c settles most
   of it in one sentence: "Ta sama kolumna 400 px co w logowaniu (2a). Reset
   hasła nie jest osobnym światem." Both screens are the card of section 14 —
   .auth, .auth__column, .auth__intro, .auth__title, .auth__lead, .auth__form,
   .auth-field, .auth__switch — and nothing here restates any of it.

   Three things are new: the line under the button on 9d, the sent panel on 9c,
   and the strength bar. §24 records the dimensions.
   -------------------------------------------------------------------------- */

/* Note 04 beside 9d: "Wylogowanie z innych urządzeń zapowiedziane przed
   kliknięciem, nie po. To jedyny efekt uboczny tego ekranu." Centred under the
   button, in the 14 px of a hint rather than the 15 px of the lead — it belongs
   to the button above it and not to the heading at the top of the column. */
.auth__aside {
  text-align: center;
  font-size: 14px;
  line-height: 1.55;
  color: var(--muted);
}

/* --- The sent panel on 9c ------------------------------------------------- */

/* Note 02: "Potwierdzenie pojawia się pod przyciskiem, na tym samym ekranie."
   A card of its own rather than the system message of §14: that component is
   a strip with an icon answering the form, and this is a block of three lines
   answering the mailbox. §16 keeps the two apart. */
.reset-sent {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 18px 20px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

/* .t-overline carries the mono 11 px and the letter spacing; this adds the
   colour and the room for the dot. */
.reset-sent__status {
  display: flex;
  align-items: center;
  gap: 9px;
  color: var(--ok);
}

.reset-sent__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ok);
}

.reset-sent__body {
  font-size: 15px;
  line-height: 1.55;
  color: var(--text);
}

.reset-sent__again {
  font-size: 14px;
  line-height: 1.55;
  color: var(--muted);
}

/* --- What the script uncovers --------------------------------------------- */

/* Both halves of the enhancement ship with the `hidden` attribute and are
   uncovered by static/js/password.js. The attribute alone does not hold: the
   bar below carries a display of its own, and an author rule outranks the
   browser's own [hidden] — so with no JavaScript the meter would render as an
   empty grey track, which is exactly what §24 promises it does not do.

   Section 12 met this first and answers it the same way, in a comment worth
   repeating: "Several of them carry a display of their own further down, which
   would otherwise outrank the attribute." Scoped to the field so that it
   covers the reveal as well, and so it cannot reach anything else. */
.auth-field [hidden] {
  display: none;
}

/* --- The reveal on 9d ------------------------------------------------------ */

/* .auth-field__aside was written in section 14 for the link opposite the label
   on 2a, so it carries a size and a colour and nothing else. On 9d the same
   slot holds "Pokaż", which changes the field rather than going anywhere and is
   therefore a button — and a button with only those two declarations keeps the
   browser's own chrome: a grey raised box in the system font, where the frame
   draws 13 px of accent text.

   The same reset section 15 makes for "Wyślij ponownie" on 9e, recorded in
   DESIGN_SPEC.md §22. Qualified by the element, so the link on 2a — which needs
   none of this — is left alone. */
button.auth-field__aside {
  padding: 0;
  font-family: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

/* --- The strength bar on 9d ----------------------------------------------- */

/* Between the control and the hint, which is the order the frame draws and the
   order that reads: the bar reacts, and the sentence under it explains to what. */
.strength {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-top: 4px;
}

/* Two colours and no more. Note 02: "Wskaźnik siły hasła jako pasek na dwóch
   kolorach, bez etykiet »słabe / silne« przy każdym stopniu." So the track and
   the fill, and the level is carried by how much of the track is covered — not
   by the fill changing colour, which would be the traffic light note 02 beside
   1i refused for the calculators, and not by a gradient, which "Czego nie ma
   w systemie" refuses everywhere. */
.strength__track {
  flex: 1;
  display: flex;
  height: 5px;
  border-radius: 3px;
  overflow: hidden;
  background: var(--line);
}

/* Width is set by static/js/password.js, in whole percents that come from
   apps/accounts/strength.py. No transition: DESIGN_SPEC.md, "Czego nie ma
   w systemie" — the project animates no change of state, and a bar catching up
   with the typing a moment later is the lag that rule exists to avoid. */
.strength__fill {
  width: 0;
  background: var(--ok);
}

/* "Jedno słowo obok paska wystarczy." */
.strength__word {
  font-size: 13px;
  color: var(--muted);
}

/* --- Phone: the reveal of 9d is already in the row; only the row wraps ----- */

/* 9d has no phone frame. The button sits in this row on every width and needs
   nothing here except permission for the row to break, which the longer of its
   two words ("Ukryj") can ask for on a 360 px screen. The 46 px control of
   section 21 grows to 48 in section 14's block, which this screen shares. */
@media (max-width: 900px) {
  .strength {
    flex-wrap: wrap;
  }
}

/* ==========================================================================
   18. Panel specjalisty — profil (2h)
   ==========================================================================

   The first screen under /panel/. The grid is the frame's — 1180 px split
   200 px / 1fr with a 64 px gap. The editor column is capped at 660 px, which
   is the frame's too; the profile screen drops the cap for its list of
   sections. Every card ends in its own footer (section 9).

   .panel-nav is styled here rather than in section 7, even though the template
   is a component: it has one reader today, and moving it up a section when the
   second one arrives is a smaller change than guessing now which half of it is
   general.
   -------------------------------------------------------------------------- */

/* The panel and the patient area keep the card corner and the flat cards
   they had, until their own redesign takes the whole of the mockup at once;
   a panel half in the old language and half in the new would be neither.
   Custom properties inherit, so these two lines hold every card inside. */
.panel {
  display: block;
  padding: 40px 0 0;
  --radius-card: 8px;
  --shadow-card: none;
  --shadow-raised: none;
}

.panel__grid {
  display: grid;
  grid-template-columns: var(--panel-menu) minmax(0, 1fr);
  gap: var(--panel-gap);
  align-items: start;
  max-width: var(--panel-max);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.panel__column {
  display: flex;
  flex-direction: column;
  gap: 26px;
  max-width: 660px;
  padding-bottom: 88px;
}

/* The panel menu moved to section 7 once a second screen read it — which is
   exactly what the note at the head of this section said would happen, and the
   second reader turned out to need a menu of its own
   (components/patient_nav.html) sharing every rule. */

/* --- Visibility strip ---------------------------------------------------- */

/* Note 05: "Pasek statusu na górze zamiast paska postępu. Liczy się jedno: czy
   profil jest widoczny." */
.panel-status {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 18px 22px;
  background: var(--tint);
  border-radius: var(--radius-card);
}

.panel-status__state {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 16px;
  color: var(--text);
}

.panel-status__dot {
  flex: none;
  width: 9px;
  height: 9px;
  background: var(--accent);
  border-radius: 50%;
}

/* Hidden, unconfirmed and awaiting verification are not errors — nobody did
   anything wrong — so the dot goes quiet rather than red. --alert on this strip
   would say "broken" about a profile that is merely unfinished. */
.panel-status:not(.panel-status--public) {
  background: var(--hl);
}

.panel-status:not(.panel-status--public) .panel-status__dot {
  background: var(--muted);
}

/* A failed payment with another attempt ahead is the one state that is a
   warning rather than an unfinished step: the profile is still public and
   something needs doing. --warn is the token README.md names for "płatność
   wymaga uwagi"; --alert stays out for the same reason as above. */
.panel-status--retrying .panel-status__dot {
  background: var(--warn);
}

/* The way out of the state, and after it on an inactive profile the preview
   of its page. */
.panel-status__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 8px 20px;
}

.panel-status__action {
  flex: none;
  font-size: 15px;
  color: var(--accent);
}

/* --- Heading and open section -------------------------------------------- */

.panel__heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  padding-bottom: 14px;
  border-bottom: 2px solid var(--accent);
}

.panel__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 32px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text);
}

.panel__note {
  font-size: 14px;
  color: var(--muted);
}

.panel-section__fields {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Two fields side by side, as a grid rather than a flex row: an error message
   under one of them must not push its neighbour out of line. The same reason
   2d gives in section 23. */
.panel-section__pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

/* A sentence at the top of a section ("Pierwsza usługa z listy jest na karcie
   w katalogu.") and a caption over a group of fields ("Adres praktyki"). */
.panel-section__note {
  margin: 0;
  font-size: 14px;
  color: var(--muted);
}

.panel-section__caption {
  margin: 4px 0 -8px;
  font-size: 15px;
  font-weight: 500;
  color: var(--text);
}

/* --- Sections: the list on the left, the form on the right --------------- */

/* The panel mockup of 24.09: the list stands still in a fixed order and the
   chosen section is highlighted, instead of the old arrangement in which the
   open section jumped to the top and the rest reordered under it. The screen
   is wider than the other panel screens by the width of that list, so it
   drops the 660 px cap of .panel__column. */
.panel__column--wide {
  max-width: none;
}

.profile-editor {
  display: grid;
  grid-template-columns: 248px minmax(0, 1fr);
  gap: 24px;
  align-items: start;
}

.profile-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
}

.profile-nav__item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 12px;
  color: var(--text);
  border-radius: 6px;
}

.profile-nav__item:hover {
  color: var(--text);
  background: var(--hl);
}

.profile-nav__item--current,
.profile-nav__item--current:hover {
  color: var(--accent);
  background: var(--tint);
}

.profile-nav__title {
  font-size: 15px;
}

/* The state beside the title: "uzupełnione", "3 usługi" — or "puste", which
   is a state and not a fault ("brak cennika nie jest błędem"), so it is set
   apart by italics and not by --alert. */
.profile-nav__state {
  flex: none;
  font-size: 12px;
  color: var(--muted);
}

.profile-nav__state--empty {
  font-style: italic;
}

.profile-editor__card {
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-width: 0;
  padding: 24px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
}

/* "Cennik" has two cards: the list, and the notice before booking under it. */
.profile-editor__cards {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

.profile-editor__form {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.profile-editor__title {
  margin: 0;
  font-size: 19px;
  font-weight: 500;
  color: var(--text);
}

/* --- Photo --------------------------------------------------------------- */

.panel-photo {
  display: flex;
  align-items: center;
  gap: 20px;
}

.panel-photo__image {
  flex: none;
  width: 76px;
  height: 76px;
  object-fit: cover;
  border-radius: 50%;
}

/* The systemic placeholder of section 3: initials in mono on --tint, never a
   silhouette. 22 px is the card's size, and this circle sits between the card's
   84×104 and the profile header's 120 px, so it takes the smaller of the two. */
.panel-photo__image--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 22px;
  color: var(--accent);
  background: var(--tint);
}

/* flex: 1, so the photo field runs to the edge of the column like the fields
   under it instead of stopping at the width of its own content. */
.panel-photo__controls {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

/* "Wybierz zdjęcie": the file input's label drawn as a button. The input is
   clipped, not display: none, so it stays focusable and announced; its focus
   ring is drawn on the button, which is the thing a sighted user sees. */
.panel-photo__pick {
  position: relative;
  align-self: flex-start;
}

.panel-photo__pick input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.panel-photo__pick label {
  cursor: pointer;
}

.panel-photo__pick input[type="file"]:focus-visible + label {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* --- Locked field -------------------------------------------------------- */

/* Note 04: "Pola zablokowane (numer PWZ) na tle wypełnienia, nie wyszarzone."
   The colours are already in section 7, on .field input:disabled — what is left
   here is the width, because this box stands alone rather than in a pair. */
.field--locked input {
  width: 100%;
}

/* --- Lists: "Cennik" and "Miejsca przyjęć" ------------------------------- */

/* The mockup of 26.09. A row is read — name, price or address, the arrows,
   "Edytuj" and "Usuń" — between two hairlines; an open row (its form, or the
   question before deleting it) becomes a tinted box in the same place. */
.panel-list {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line);
}

.panel-list__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto auto;
  align-items: center;
  gap: 8px 16px;
  min-height: 60px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}

.panel-list__row--open {
  display: block;
  padding: 12px 0;
}

.panel-list__main {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  min-width: 0;
}

.panel-list__name {
  font-size: 15px;
  font-weight: 500;
  overflow-wrap: anywhere;
}

.panel-list__sub {
  font-size: 14px;
  color: var(--muted);
}

.panel-list__price {
  font-size: 15px;
  white-space: nowrap;
  text-align: right;
}

.panel-list__price--none {
  color: var(--muted);
}

/* Two arrows in one column, so a row without one of them (the first has no
   "up", the last no "down") keeps the others in line. 44 px is the smallest
   target a finger is given (section 1). */
.panel-list__move {
  display: grid;
  grid-template-columns: 44px 44px;
}

.panel-list__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  color: var(--muted);
  background: none;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}

.panel-list__arrow:hover {
  color: var(--accent);
  background: var(--hl);
}

.panel-list__arrow--down {
  grid-column: 2;
}

.panel-list__actions {
  display: flex;
  gap: 4px;
}

.panel-list__action {
  padding: 10px 12px;
  font-size: 15px;
  font-weight: 500;
  color: var(--accent);
  text-decoration: none;
  border-radius: 6px;
}

.panel-list__action:hover {
  color: var(--accent);
  background: var(--hl);
}

.panel-list__action--danger,
.panel-list__action--danger:hover {
  color: var(--alert);
}

.panel-list__confirm,
.panel-item {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 20px;
  background: var(--band);
  border-radius: var(--radius-card);
}

.panel-list__question {
  margin: 0;
  font-size: 15px;
}

.panel-list__consequence {
  margin: -10px 0 0;
  font-size: 14px;
  color: var(--muted);
}

.panel-list__buttons,
.panel-item__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.panel-list__none {
  margin: 0;
  font-size: 14px;
  color: var(--muted);
}

.panel-list__new {
  margin-top: 4px;
}

.panel-list__add {
  align-self: flex-start;
}

/* A service's name and its price: the price is short, so it gets a short track. */
.panel-item__pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 160px;
  gap: 16px;
}

/* N1 — the address the geocoder did not find. --warn, as the mockup draws
   it: it asks for a look, it does not report a fault. */
.panel-item__note {
  margin: 0;
  font-size: 14px;
  color: var(--warn);
}

/* --- Phone (11d, 11e): the column becomes a rail, the shell full width ----- */

/* The 200 px menu column has nowhere to stand at 390 px, so the grid gives up
   its second track and the menu — already turned into a rail by section 7 —
   spans the width above the content. The grid also gives up its gutter: the
   rail is a band with its own edge and its own padding, and a gutter here
   would inset it by 18 px on both sides and leave the edge floating.

   The status strip of 2h keeps working: it is a child of .panel__column,
   which takes the gutter the grid dropped. */
@media (max-width: 900px) {
  .panel {
    padding-top: 0;
  }

  .panel__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }

  /* The grid keeps the gutter it has on the desktop, because not every screen
     under /panel/ wraps its content in .panel__column — the inbox of 11d sits
     in the grid directly. The two bands that run to both edges of the screen
     cancel the gutter themselves: the rail below and the inbox card in
     section 21. */
  .panel-nav {
    margin: 0 calc(var(--gutter) * -1);
  }

  .panel__column {
    max-width: none;
    padding: 26px 0 88px;
  }

  /* The state and its way out share a row on the desktop. On a phone the
     link would press against the last word of the state, so it goes on a line
     of its own, under the text and indented past the dot (9 px dot, 12 px
     gap). */
  .panel-status {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  .panel-status__actions {
    justify-content: flex-start;
    padding-left: 21px;
  }

  /* The state wraps to two lines at 320 px; the dot stays on the first. */
  .panel-status__state {
    align-items: flex-start;
  }

  .panel-status__dot {
    margin-top: calc((1lh - 9px) / 2);
  }

  /* A row breaks in two: the name and the price over the arrows and the
     links, which keep their size for a finger. */
  .panel-list__row {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .panel-list__move {
    justify-self: start;
  }

  .panel-list__actions {
    justify-self: end;
  }

  .panel-item__pair {
    grid-template-columns: minmax(0, 1fr);
  }

  /* "Imię i nazwisko" beside "Tytuł", and "Miasto" beside "Języki konsultacji".
     At 390 px each track is 170 px, which is not a name. The card form of 10b
     keeps its own pair: an expiry of five characters and a security code are
     the two fields on this site that are genuinely short. */
  .panel-section__pair {
    grid-template-columns: minmax(0, 1fr);
  }

  /* The list goes above the form, still in its order, and the card loses
     some of its padding to the narrow screen. */
  .profile-editor {
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
  }

  .profile-editor__card {
    padding: 18px;
  }
}

/* ==========================================================================
   19. Konto — ustawienia (2l, 2n)
   ==========================================================================

   Two screens and one set of rules. Note 01 beside 2n: "Ta sama siatka i te
   same wiersze co w koncie specjalisty (2l). Różni się menu boczne i jedna
   sekcja." So the grid, the heading, the rows, the switches and the deletion
   block are shared, and only the visibility card (2l) and the "Co widzi
   specjalista" card (2n) belong to one screen each.

   The shell is .panel and .panel__grid from section 18, unchanged. What is
   here is the column and everything inside it.
   -------------------------------------------------------------------------- */

/* 820 px, where the profile editor of 2h is capped at 660. The rows are a
   label on the left and an action on the right rather than a stack of fields,
   so the sentence in the middle has somewhere to go.

   No padding-bottom either: there is no save bar over this content. Note 02
   beside 2l — "zmiana działa od razu, więc nie ma paska zapisu". */
.account {
  display: flex;
  flex-direction: column;
  gap: 32px;
  max-width: 820px;
  padding-bottom: 88px;
}

.account__heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  padding-bottom: 14px;
  border-bottom: 2px solid var(--accent);
}

.account__title {
  font-family: var(--font-serif);
  font-size: 32px;
  font-weight: 500;
  color: var(--text);
}

.account__note {
  font-size: 14px;
  color: var(--muted);
}

/* --- Credential rows ----------------------------------------------------- */

.account__rows {
  display: flex;
  flex-direction: column;
}

/* A rule above every row and below the last, which is how the frame closes the
   group without a box around it. */
.account__rows > :last-child {
  border-bottom: 1px solid var(--line);
}

.account-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 18px 0;
  border-top: 1px solid var(--line);
}

/* min-width: 0 lets the text give way to the action beside it; without it a
   flex item stays as wide as its longest word, and the address on the account
   screen pushed the row past a phone's edge. */
.account-row__text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.account-row__title {
  font-size: 16px;
  color: var(--text);
}

.account-row__note {
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
  overflow-wrap: anywhere;
}

/* The words of the patient's own review, in the group "Opinie" of 2n. */
.account-row__review {
  margin: 6px 0 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
  overflow-wrap: anywhere;
}

.account-row--review .visit-row__confirm {
  margin-top: 10px;
}

.account-row__action {
  flex: none;
  font-size: 15px;
  color: var(--accent);
}

/* The same quiet action when it sends something ("Ustaw hasło" on 2n): a
   button, because it is a POST, drawn as the link beside it. */
.account-row__action--button {
  padding: 0;
  font-family: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

.account-row__action--button:hover {
  text-decoration: underline;
}

/* --- The expanded row ---------------------------------------------------- */

/* What opens behind "Zmień". No frame draws it, so the fields are section 2's
   and the spacing is the editor column's from 2h. */
.account-editor {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 22px 0 26px;
  border-top: 1px solid var(--line);
}

.account-editor__title {
  font-size: 16px;
  color: var(--text);
}

/* What saving costs, said before the button rather than under a field: it is
   about the whole submission and not about what was typed. */
.account-editor__warning {
  max-width: 520px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
}

.account-editor__actions {
  display: flex;
  align-items: center;
  gap: 20px;
}

.account-editor__cancel {
  font-size: 15px;
  color: var(--muted);
}

.account-editor__cancel:hover {
  color: var(--text);
}

/* --- Switches ------------------------------------------------------------ */

.account__group {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.account__group-title {
  font-size: 16px;
  color: var(--text);
}

.account__switches {
  display: flex;
  flex-direction: column;
}

.account__switches > :last-child {
  border-bottom: 1px solid var(--line);
}

.switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 16px 0;
  border-top: 1px solid var(--line);
}

.switch-row__label {
  font-size: 15px;
  color: var(--text);
}

/* The form exists to carry the value and the CSRF token; it must not add a box
   of its own around the button. */
.switch-row__form {
  display: flex;
}

/* --- Cards --------------------------------------------------------------- */

/* The switches that change what a patient sees on 2l. Note 03 beside 2l says why visibility
   is in a card and not in the list; the same card also holds the switch of
   messages from new patients, for the same reason. */
.account-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 20px 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
}

/* The card of 2l with its two switches. Each row keeps the card's own 20 px
   of air and its label-left, switch-right arrangement, and a 1 px line parts
   them — the same separator the notification rows use above. */
.account-card--rows {
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  padding-top: 0;
  padding-bottom: 0;
}

.account-card__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 20px 0;
}

.account-card__row + .account-card__row {
  border-top: 1px solid var(--line);
}

.account-card__text {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.account-card__title {
  font-size: 16px;
  color: var(--text);
}

.account-card__note {
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
}

.account-card__form {
  display: flex;
}

/* "Wizyty w Twoim kalendarzu" on 2l — no frame draws it: a title, the three
   buttons, one sentence under them. */
.account-card--calendar {
  flex-direction: column;
  align-items: stretch;
  gap: 14px;
  padding: 22px 24px;
}

.account-card--calendar .account-card__note {
  margin: 0;
}

.calendar-feed__links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The "widzi / nie widzi" entries of the composer rail on 8c. */
.seen-by__column {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.seen-by__item {
  font-size: 15px;
  line-height: 1.5;
}

.seen-by__item--yes {
  color: var(--text);
}

/* The em dash the frame puts in front of every entry in the right column. It
   is decoration and not content, so it comes from here rather than from the
   template — a screen reader would otherwise read "myślnik" four times. */
.seen-by__item--no {
  color: var(--muted);
}

.seen-by__item--no::before {
  content: "— ";
}

/* --- Deletion ------------------------------------------------------------ */

/* Note 04 beside 2l: "Usunięcie konta bez czerwieni i bez ramki
   ostrzegawczej. Powstrzymuje 14 dni karencji opisane w zdaniu obok, nie kolor
   przycisku." No --alert anywhere in this block, and the action is underlined
   text in --text, exactly as the frame draws it. */
.account-deletion {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding-top: 8px;
}

.account-deletion__action {
  flex: none;
  font-size: 15px;
  color: var(--text);
  text-decoration: underline;
}

/* The two states no frame draws: the confirmation, and the fourteen days after
   it. Both get the tint the panel uses for "unfinished, not broken" — the same
   choice the status strip of 2h makes with its dot. */
.account-deletion--confirm,
.account-deletion--pending {
  flex-direction: column;
  align-items: stretch;
  gap: 16px;
  padding: 20px 22px;
  background: var(--hl);
  border-radius: var(--radius-card);
}

/* --- Two columns of cards (2l and 2k, panel mockup of 24.09) ------------- */

/* The redrawn screens of the panel: as wide as the calendar, a heading with its
   note beside it rather than over a rule, and white cards in two columns. 2k
   takes this frame whole, which is why the names are not 2l's.

   .panel keeps the flat 8 px cards of the screens not yet redrawn (section
   18), so this frame takes the two card tokens back from :root. */
.settings {
  --radius-card: 16px;
  --shadow-card: 0 1px 2px rgba(11, 46, 72, 0.06);
  display: flex;
  flex-direction: column;
  gap: 28px;
  max-width: 1080px;
  padding-bottom: 56px;
}

.settings__heading {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 16px;
}

.settings__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 30px;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--text);
}

.settings__note {
  font-size: 14px;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.settings__cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}

.settings__col {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

.settings-card {
  display: flex;
  flex-direction: column;
  padding: 24px;
  background: var(--surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

/* The mockup's .card-title: the body face, not the serif section 5 gives h2. */
.settings-card__title {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.4;
  color: var(--text);
}

/* The calendar and deletion partials are shared with 2n and bring titles of
   their own; inside a settings card they weigh what the card titles weigh. */
.settings-card > .account-card > .account-card__title,
.settings-card > .account-deletion .account-row__title {
  font-weight: 500;
}

.settings-card__note {
  margin: 4px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
}

/* A card of one line: the title and its sentence on the left, the state and
   the action on the right ("Metoda płatności", "Nie chcesz kontynuować?"). */
.settings-card--row {
  flex-direction: row;
  align-items: center;
  gap: 16px;
}

.settings-card__text {
  flex: 1;
  min-width: 0;
}

.settings-card__actions {
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
}

/* The rows of 2l inside a card: 16 px of air each, a rule between two rows and
   none above the first or under the last, because the card closes the group. */
.settings-card .account-row,
.settings-card .switch-row {
  padding: 16px 0;
}

.settings-card .account__rows > :first-child,
.settings-card .account__switches > :first-child {
  border-top: 0;
}

.settings-card .account__rows > :last-child,
.settings-card .account__switches > :last-child {
  border-bottom: 0;
}

/* The calendar card and the visibility row bring the box they had in the one
   column; inside a card of the mockup they are only its content. */
.settings-card > .account-card {
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
}

.settings-card > .account-card__row {
  padding: 16px 0 0;
}

.settings-card > .account-deletion {
  padding-top: 0;
}

/* "Usuń konto" on 2l: red lettering on no ground, a pale red ground under the
   pointer — the mockup's danger button. The ground is mixed from --alert the
   way the badges of section 7 mix theirs. */
.account-deletion__danger {
  flex: none;
  color: var(--alert);
}

.account-deletion__danger:hover {
  color: var(--alert);
  background: color-mix(in srgb, var(--alert) 9%, var(--surface));
}

@media (max-width: 900px) {
  .settings {
    gap: 20px;
  }

  .settings__title {
    font-size: 26px;
  }

  .settings__cols {
    grid-template-columns: minmax(0, 1fr);
  }

  .settings-card {
    padding: 20px;
  }

  /* The action drops under the sentence rather than squeezing beside it. */
  .settings-card--row,
  .settings-card > .account-deletion {
    flex-direction: column;
    align-items: flex-start;
    gap: 14px;
  }
}

/* ==========================================================================
   20. Wiadomość do specjalisty — frame 8c
   ========================================================================== */

/* The same 1fr / 360 px pair as the profile of 1e, and for the same reason:
   this screen is reached from that one and its rail holds the same kind of
   thing — who you are dealing with, and what happens next. */
.composer {
  background: var(--bg);
  padding: 32px 0 72px;
}

.composer__inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 24px;
}

.composer__layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: 44px;
  align-items: start;
  margin-top: 20px;
}

.composer__main {
  max-width: 720px;
}

.composer__head {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 28px;
}

.composer__kicker {
  color: var(--accent);
}

/* 32 px rather than the 38 px of 1e: this heading opens a form, not a profile,
   and the name it would compete with is in the breadcrumb right above it. */
.composer__title {
  font-family: var(--font-serif);
  font-size: 32px;
  line-height: 1.2;
  font-weight: 500;
  color: var(--text);
}

.composer__lead {
  font-size: 16px;
  line-height: 1.55;
  color: var(--muted);
}

.composer__form {
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.composer__actions {
  display: flex;
  align-items: center;
  gap: 16px;
}

/* Note 05 beside 8c: the second exit, beside the send. */
.composer__booking-exit {
  font-size: 15px;
  color: var(--accent);
  text-decoration: none;
}

/* --- The rail ------------------------------------------------------------ */

.composer-aside {
  position: sticky;
  top: 92px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.composer-aside__card,
.composer-aside__seen,
.composer-aside__alert {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 20px 22px;
  box-shadow: var(--shadow-card);
}

.composer-aside__card {
  display: flex;
  align-items: center;
  gap: 14px;
}

/* 44 px, the avatar of DESIGN_SPEC.md section 11 — the size the thread list
   draws the same person at. The radius is the card's, as every other photo in
   the project has it. */
.composer-aside__photo {
  flex: none;
  width: 44px;
  height: 44px;
  object-fit: cover;
  /* A 44 px thumbnail keeps the corner it had; the 16 px of a card would
     round it into a blob. */
  border-radius: 8px;
}

.composer-aside__photo--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--tint);
  color: var(--accent);
  font-size: 15px;
  font-weight: 500;
}

.composer-aside__identity {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.composer-aside__name {
  font-size: 16px;
  line-height: 1.35;
  font-weight: 500;
  color: var(--text);
}

.composer-aside__response {
  font-size: 14px;
  color: var(--muted);
}

.composer-aside__seen {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.composer-aside__title {
  font-size: 15px;
  font-weight: 500;
  color: var(--text);
}

/* Note 04 beside the frame: "Ostrzeżenie o nagłych sprawach w ramce alertu. To
   jedyne miejsce w serwisie, gdzie używamy tego koloru na obramowaniu." The
   border is the whole of it — no red text, no icon, no filled background. A
   sentence about calling 112 does not have to shout to be read. */
.composer-aside__alert {
  display: flex;
  flex-direction: column;
  gap: 8px;
  border-color: var(--alert);
}

.composer-aside__alert-text {
  font-size: 14px;
  line-height: 1.55;
  color: var(--muted);
}

/* The rail drops under the form before it gets narrow enough to squeeze the
   text column — the breakpoint the profile page uses, for the same reason. */
@media (max-width: 900px) {
  .composer__layout {
    grid-template-columns: minmax(0, 1fr);
    gap: 32px;
  }

  .composer-aside {
    position: static;
  }

  /* At 390 px the send button and the link beside it split the row, and
     "Wyślij wiadomość" broke over two lines. The button takes the width of the
     column, like every other form's on a phone, and the link goes under it. */
  .composer__actions {
    flex-direction: column;
    align-items: stretch;
    gap: 14px;
  }

  .composer__booking-exit {
    align-self: flex-start;
  }
}

/* ==========================================================================
   21. Wiadomości — frames 2i and 8d

   The inbox lives inside the panel column of 2h, which is note 01 beside 2i:
   "Skrzynka mieści się w kolumnie treści panelu, nie zajmuje całego ekranu.
   To nie jest komunikator, tylko poczekalnia przed wizytą."

   Three decisions here differ from DESIGN_SPEC.md section 11, and section 28 of
   that document carries all three with their reasons: the list column is 300 px
   rather than 380, a row has no avatar, and an unread row is marked by a fill
   and a bar rather than by weight — "nie pogrubieniem".
   ========================================================================== */

.inbox {
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-width: 0;
}

.inbox__heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding-bottom: 14px;
  border-bottom: 2px solid var(--accent);
}

.inbox__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 32px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text);
}

.inbox__note {
  font-size: 14px;
  color: var(--muted);
}

/* Two cards side by side, as the panel mockup of 24.09 draws them: the list
   as tall as its rows, the conversation 640 px. The conversation's height is
   fixed rather than grown from the content because a thread of forty messages
   would otherwise push the reply box below the fold, and the reply box is what
   the screen is for. The list stops at the same height and scrolls inside. */
.inbox__card {
  display: grid;
  grid-template-columns: 320px minmax(0, 1fr);
  align-items: start;
  gap: 20px;
}

/* With nothing in the inbox the card is the empty state alone, so it has no
   columns to divide and no reason to be 640 px tall. */
.inbox__card--empty {
  display: block;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
}

/* --- The list ------------------------------------------------------------- */

.thread-list {
  display: flex;
  flex-direction: column;
  min-height: 0;
  max-height: 640px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  overflow: hidden;
}

.thread-list__tabs {
  display: flex;
  flex: none;
  gap: 18px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--line);
}

.thread-list__tab {
  padding-bottom: 4px;
  font-size: 14px;
  color: var(--muted);
}

.thread-list__tab:hover {
  color: var(--text);
}

.thread-list__tab--active,
.thread-list__tab--active:hover {
  color: var(--text);
  border-bottom: 2px solid var(--accent);
}

/* The only scrolling part of the column. The tabs above it and the sentence
   below it stay put, so a long inbox pushes neither out of reach. */
.thread-list__rows {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.thread-row {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 16px 18px 16px 14px;
  border-bottom: 1px solid var(--line);
  /* The unread bar takes 4 px off the left padding rather than adding to the
     width, so a marked row and a plain one share a text edge. */
  border-left: 4px solid transparent;
}

.thread-row:hover {
  background: var(--hl);
}

.thread-row--unread {
  background: var(--tint);
  border-left-color: var(--accent);
}

.thread-row--active,
.thread-row--active:hover {
  background: var(--hl);
  border-left-color: var(--accent);
}

.thread-row__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

/* Weight 500 on every row, marked or not. Note 02 beside 2i: "Lista pozostaje
   spokojna przy dwudziestu wątkach." */
.thread-row__name {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  font-size: 15px;
  font-weight: 500;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.thread-row__dot {
  flex: none;
  width: 8px;
  height: 8px;
  background: var(--accent);
  border-radius: 50%;
}

.thread-row__stamp {
  flex: none;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
}

/* One line, cut by the stylesheet rather than by the view — the fragment of a
   message is as long as the column happens to be. */
.thread-row__preview {
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.thread-list__new {
  flex: none;
  padding: 16px 18px;
  font-size: 15px;
  color: var(--accent);
  border-top: 1px solid var(--line);
}

.thread-list__foot {
  flex: none;
  margin: 0;
  padding: 16px 18px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
  border-top: 1px solid var(--line);
}

.thread-list__foot--note {
  font-family: var(--font-mono);
  font-size: 11px;
}

/* --- The conversation ----------------------------------------------------- */

.thread {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  height: 640px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  overflow: hidden;
}

.thread__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: none;
  gap: 20px;
  padding: 16px 24px;
  border-bottom: 1px solid var(--line);
}

.thread__who {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.thread__name {
  font-family: var(--font-serif);
  font-size: 20px;
  font-weight: 500;
  line-height: 1.25;
  color: var(--text);
}

/* "Strona pacjenta ›" or "Profil ›" under the name, the mockup's link. */
.thread__link {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-size: 14px;
  font-weight: 500;
  color: var(--accent);
}

.thread__link:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* The one action beside the name: "Umów wizytę" on the patient's side. A
   quiet bordered link, not a primary — the head of a conversation is not a
   call to action. */
.thread__action {
  flex: none;
  font-size: 14px;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius-control);
  padding: 8px 14px;
  text-decoration: none;
}

.thread__messages {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 16px;
  min-height: 0;
  padding: 24px;
  background: var(--bg);
  overflow-y: auto;
}

.thread__day {
  align-self: center;
  font-size: 12px;
  color: var(--muted);
}

/* The line under the last message: the period and the download, centred and
   quiet, as the mockup draws it. Note 04 beside 8d puts the patient's privacy
   sentence here too, in the conversation rather than in the terms. */
.thread__privacy {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  text-align: center;
  color: var(--muted);
}

.thread__privacy a {
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* "Zgłoś wiadomość" — quiet, under the paragraph above, at its type size. */
.thread__report {
  margin: 0;
  font-size: 13px;
  text-align: center;
  color: var(--muted);
}

/* --- A message ------------------------------------------------------------ */

/* Mine on the right, theirs on the left — the rule both frames draw, rather
   than the role-based one of section 11, which would put a patient's own words
   on the left of her own screen. Section 28 records the correction. */
.bubble {
  display: flex;
  align-self: flex-start;
  flex-direction: column;
  gap: 6px;
  max-width: 74%;
}

.bubble--mine {
  align-self: flex-end;
  align-items: flex-end;
}

.bubble__body {
  padding: 13px 16px;
  font-size: 15px;
  line-height: 1.55;
  color: var(--text);
  background: var(--tint);
  border-radius: var(--radius-card);
}

.bubble--mine .bubble__body {
  color: var(--on-accent);
  background: var(--accent);
}

/* linebreaks wraps each paragraph of a message. */
.bubble__body p {
  margin: 0;
}

.bubble__body p + p {
  margin-top: 10px;
}

.bubble__stamp {
  font-size: 12px;
  color: var(--muted);
}

/* `DESIGN_SPEC.md` section 45.3.5. On the surface colour in both directions:
   a file is a thing to act on rather than something said, so it keeps its own
   frame whichever side of the conversation it is on. */
.bubble__file {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-card);
}

.bubble__clip {
  flex: none;
  color: var(--muted);
}

.bubble__file-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.bubble__file-name {
  font-size: 15px;
  color: var(--text);
  overflow-wrap: anywhere;
}

.bubble__file-meta {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
}

.bubble__file-action {
  margin-left: auto;
  padding-left: 14px;
  font-size: 14px;
  color: var(--accent);
}

/* --- The reply box -------------------------------------------------------- */

.thread__composer {
  display: flex;
  flex: none;
  flex-direction: column;
  gap: 12px;
  padding: 16px 24px 18px;
  border-top: 1px solid var(--line);
}

.thread-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* The clip, the line about the file and "Wyślij" — one row under the box. */
.thread-form__row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Out of sight and still in the form and the tab order: the label beside it is
   what a pointer clicks, and the ring below is drawn on the label when the
   keyboard reaches the input. */
.thread-form__row input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.thread-form__clip {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  color: var(--accent);
  background: var(--tint);
  border-radius: var(--radius-control);
  cursor: pointer;
}

.thread-form__clip:hover {
  background: var(--hl);
}

.thread-form__row input[type="file"]:focus-visible + .thread-form__clip {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The words are for a screen reader; the clip says it to the eye. */
.thread-form__clip-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.thread-form__file {
  flex: 1;
  min-width: 0;
  font-size: 14px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.thread-form__row .btn {
  flex: none;
}

/* Note 04 beside 2i puts this sentence at the field rather than in the terms:
   "Czytane wtedy, kiedy ma znaczenie." */
.thread-form__note {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

/* Where the reply box stands when the patient may not write: a sentence in
   the box's own place and type, so the conversation above keeps its size. */
.thread__closed {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text);
}

/* --- Phone: the two cards one under the other ---------------------------- */

/* The list first, then the conversation, both still cards. The list loses its
   cap, because on a phone it is the screen and a second scrollbar inside the
   first is what makes a page feel wrong on a touch device. The conversation
   keeps a cap of its own, as a share of the screen's height, because it is the
   one part of this screen that can be arbitrarily long. */
@media (max-width: 900px) {
  .inbox__heading {
    flex-wrap: wrap;
    gap: 4px 20px;
  }

  .inbox__title {
    font-size: 26px;
  }

  .inbox__card {
    grid-template-columns: minmax(0, 1fr);
  }

  .thread-list {
    max-height: none;
  }

  .thread-list__rows {
    max-height: none;
  }

  /* The 640 px give way to the cap on the messages below. */
  .thread {
    height: auto;
  }

  /* The list side is already at 18 px on every width — the phone gutter — so
     only the conversation side, which the frame draws at 24, comes in. */
  .thread__head,
  .thread__messages,
  .thread__composer {
    padding-left: var(--gutter);
    padding-right: var(--gutter);
  }

  .thread__messages {
    max-height: 60vh;
  }

  .thread__head {
    flex-wrap: wrap;
    gap: 8px 14px;
  }

  /* The stand-in has to be shaped like what is coming — note 01 beside 11f —
     and what is coming here is one column. Its 320 px track beside a second
     column would also push the page sideways at 360 px of viewport. */
  .inbox-skeleton.htmx-request {
    grid-template-columns: minmax(0, 1fr);
    height: auto;
    margin: 0 calc(var(--gutter) * -1);
    border-left: 0;
    border-right: 0;
    border-radius: 0;
  }

  .inbox-skeleton__list {
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }
}

.thread__older {
  align-self: center;
  margin: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
}

/* The right column when the "Nieprzeczytane" tab has emptied itself. The list
   and its tabs stay put beside it, which is the whole point: a filter that
   hides its own way out is a dead end. */
.thread--none {
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: var(--bg);
}

.thread__none {
  max-width: 320px;
  margin: 0;
  font-size: 15px;
  line-height: 1.6;
  text-align: center;
  color: var(--muted);
}

/* The card's stand-in while a request is in flight. Shaped like what is
   coming, so nothing jumps when it arrives — note 01 beside frame 11f — and
   still, because DESIGN_SPEC.md rules out pulsing and spinners outright. */
/* **No `display` here, and that is the whole of the rule.** Section 8 hides
   every indicator with `.htmx-indicator { display: none }` and shows it with
   `.htmx-request.htmx-indicator { display: block }` — both a single class, so a
   `display` on this selector would sit later in the sheet, win on equal
   specificity and leave 600 px of grey standing above the real card on every
   ordinary render. The grid goes on the active state below instead, which is
   how .results-skeleton has always done it: it declares no display of its own
   either. */
.inbox-skeleton {
  grid-template-columns: 320px minmax(0, 1fr);
  height: 640px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  overflow: hidden;
}

.inbox-skeleton__list {
  border-right: 1px solid var(--line);
}

.inbox-skeleton__row {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 18px;
  border-bottom: 1px solid var(--line);
}

.inbox-skeleton__thread {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 24px;
  background: var(--bg);
}

.inbox-skeleton__bubble {
  display: flex;
  align-self: flex-start;
  flex-direction: column;
  gap: 8px;
  width: 60%;
  padding: 14px 16px;
  background: var(--tint);
  border-radius: var(--radius-card);
}

.inbox-skeleton__bubble--mine {
  align-self: flex-end;
}

/* Two classes, so this beats the `display: block` section 8 gives every active
   indicator — a block would stack the two columns instead of setting them side
   by side. htmx puts .htmx-request on the indicated element itself, which is
   what makes the selector this shape rather than a descendant one. */
.inbox-skeleton.htmx-request {
  display: grid;
}

/* The card is hidden while the skeleton shows, exactly as the list of 1c hides
   behind its own — otherwise the two stack and the column doubles in height.
   The sibling selector is why the skeleton has to come first in the markup. */
.inbox-skeleton.htmx-request ~ #skrzynka {
  display: none;
}

/* ==========================================================================
   22. Kalendarz specjalisty — panel mockup of 24.09, `#kalendarz` and `#grafik`

   The week as seven day cards on the panel ground, the standing-schedule
   card under it, and the schedule editor behind "Zmień grafik". Values are
   the mockup's: 12 px between cards, 16 px inside, tiles of 48 px with the
   tile radius, the free hour in the --slot pair, a visit in --tint with an
   accent rule on its left edge.
   ========================================================================== */

.calendar {
  display: flex;
  flex-direction: column;
}

.calendar__heading,
.schedule__heading {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 16px;
  margin: 0 0 28px;
}

.calendar__title,
.schedule__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 30px;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--text);
}

/* The anchor of the add windows drawn open (see .cal-pop--open). */
.calendar__board {
  position: relative;
}

/* The strip of a change that landed here keeps its distance from the toolbar. */
.calendar__board > .message {
  margin-bottom: 16px;
}

/* --- The toolbar: arrows, the week, its counts, "Dziś", "Dodaj termin" ----- */

.calendar__toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 16px;
  margin-bottom: 16px;
}

.calendar__arrows {
  display: flex;
  gap: 4px;
}

.calendar__arrow {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  color: var(--text);
  border-radius: 50%;
}

.calendar__arrow:hover {
  background: rgba(255, 255, 255, 0.85);
}

.calendar__range {
  font-size: 20px;
  font-weight: 500;
  color: var(--text);
}

.calendar__sum {
  font-size: 14px;
  color: var(--muted);
}

.calendar__today--here {
  opacity: 0.45;
  cursor: default;
}

.calendar__add {
  gap: 8px;
  margin-left: auto;
}

/* --- The week --------------------------------------------------------------- */

.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 12px;
}

.cal-day {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
  min-height: 320px;
  padding: 16px;
  background: var(--surface);
  border-radius: var(--radius-card);
}

.cal-day--weekend {
  background: rgba(255, 255, 255, 0.55);
}

.cal-day__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  min-height: 48px;
  margin-bottom: 4px;
}

.cal-day__abbr {
  display: block;
  font-size: 13px;
  color: var(--muted);
}

.cal-day__number {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 20px;
  line-height: 1.25;
  color: var(--text);
}

.cal-day--weekend .cal-day__number,
.cal-day--past .cal-day__number {
  color: var(--muted);
}

.cal-day--today .cal-day__number {
  font-weight: 600;
  color: var(--accent);
}

/* The dot after today's number. */
.cal-day--today .cal-day__number::after {
  content: "";
  width: 6px;
  height: 6px;
  background: var(--accent);
  border-radius: 50%;
}

.cal-day__add {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  margin: -2px -6px 0 0;
  padding: 0;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}

.cal-day__add:hover {
  color: var(--accent);
  background: var(--hl);
}

.cal-day__off {
  font-size: 13px;
  color: var(--muted);
}

/* --- Tiles ------------------------------------------------------------------ */

.cal-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
  min-height: 48px;
  padding: 10px 12px;
  font-size: 15px;
  line-height: 1.25;
  border-radius: var(--radius-tile);
}

.cal-tile--free {
  font-weight: 500;
  color: var(--slot);
  background: var(--slot-bg);
}

.cal-tile--busy {
  /* The mockup's accent bar on the left: 3 px of border and 12 px of padding
     keep the text where the other tiles have it plus the bar. */
  padding-left: 12px;
  color: var(--text);
  background: var(--tint);
  border-left: 3px solid var(--accent);
}

a.cal-tile--busy:hover {
  background: var(--tint-hover);
}

.cal-tile__time {
  font-weight: 500;
}

.cal-tile__who {
  display: block;
  font-size: 13px;
  line-height: 1.3;
  color: var(--muted);
  /* A name wraps between its words; one word wider than the tile ends in an
     ellipsis rather than breaking inside itself. */
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The × of a free tile: hidden at rest, shown under the pointer and while
   the tile holds focus — the × is the tile's one tab stop, so :focus-within
   shows it exactly when the keyboard reaches it. */
.cal-tile__x {
  position: absolute;
  top: 50%;
  right: 6px;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  color: var(--muted);
  background: var(--surface);
  border-radius: 50%;
  transform: translateY(-50%);
  opacity: 0;
}

.cal-tile--free:hover .cal-tile__x,
.cal-tile--free:focus-within .cal-tile__x {
  opacity: 1;
}

.cal-tile__x:hover {
  color: var(--alert);
}

/* The confirm state, in the tile: the hour, a tick and a cross. The tint is
   the alert colour at 9 %, the mockup's #FBEDEB. */
.cal-tile--confirm {
  padding: 8px 10px;
  color: var(--text);
  background: #FBEDEB;
}

.cal-tile--confirm .cal-tile__time {
  font-size: 14px;
  font-weight: 400;
}

.cal-tile__answers {
  display: flex;
  gap: 4px;
  margin-top: 4px;
}

.cal-tile__answers form {
  display: contents;
}

.cal-tile__yes,
.cal-tile__no {
  display: grid;
  place-items: center;
  width: 32px;
  height: 28px;
  padding: 0;
  background: var(--surface);
  border: 0;
  border-radius: 8px;
  cursor: pointer;
}

.cal-tile__yes {
  color: var(--alert);
}

.cal-tile__no {
  color: var(--muted);
}

.cal-tile__yes:hover,
.cal-tile__no:hover {
  outline: 1px solid currentColor;
  outline-offset: -1px;
}

/* --- Legend and the horizon note --------------------------------------------- */

.calendar__legend {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  font-size: 13px;
}

.calendar__legend-item {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 10px;
  border-radius: 8px;
}

.calendar__legend-item--free {
  font-weight: 500;
  color: var(--slot);
  background: var(--slot-bg);
}

.calendar__legend-item--busy {
  padding-left: 9px;
  color: var(--text);
  background: var(--tint);
  border-left: 3px solid var(--accent);
}

.calendar__horizon {
  margin: 12px 0 0;
  font-size: 14px;
  color: var(--muted);
}

/* --- The card of a visit, under the week ----------------------------------------- */

/* The booked tile opens the card the person's page draws (.visit-panel), with
   the person's name over it and "Zamknij" back to the week. */
.calendar-visit {
  margin-top: 24px;
  scroll-margin-top: 24px;
}

.calendar-visit:focus {
  outline: none;
}

.calendar-visit__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 38px;
  margin-bottom: 12px;
}

.calendar-visit__who {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: 500;
}

.calendar-visit__who a {
  color: var(--text);
}

/* Why a past visit carries no name. */
.calendar-visit__note {
  margin: 0 0 12px;
  font-size: 14px;
  color: var(--muted);
}

.calendar-visit__close {
  font-size: 14px;
  font-weight: 500;
  color: var(--accent);
}

/* The date and hour of the visit wrap on a phone instead of ending in an
   ellipsis: they are what the specialist opened the card to read. */
.calendar-visit .visit-panel__when {
  white-space: normal;
}

/* --- The add window ------------------------------------------------------------- */

/* A popover: the browser opens it from "Dodaj termin" or the + of a day and
   closes it on Esc and on a click beside it. Where anchor positioning exists
   it hangs under the day's head (or under the toolbar button); elsewhere it
   keeps the browser's own place for a popover, the middle of the window. */
.cal-pop {
  width: 320px;
  max-width: calc(100vw - 32px);
  padding: 20px;
  color: var(--text);
  background: var(--surface);
  /* An edge rather than a shadow, like the user menu: the panel's cards are
     flat, and the window has to stand out from the white day cards under it. */
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
}

.cal-pop__form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.cal-pop__actions {
  display: flex;
  gap: 8px;
}

@supports (anchor-name: --a) {
  .cal-day {
    anchor-scope: --cal-day;
  }

  .cal-day__head {
    anchor-name: --cal-day;
  }

  .cal-day .cal-pop[popover] {
    position-anchor: --cal-day;
  }

  .calendar__add {
    anchor-name: --cal-add;
  }

  .calendar__board > .cal-pop[popover] {
    position-anchor: --cal-add;
  }

  .cal-pop[popover] {
    inset: auto;
    margin: 8px 0 0;
    position-area: bottom span-right;
    position-try-fallbacks: flip-inline;
  }

  .cal-pop--end[popover] {
    position-area: bottom span-left;
  }
}

/* The window drawn open without a script: after a refused POST and under
   ?dodaj=. An ordinary element, laid over the grid under its day or under
   the toolbar. */
.cal-pop--open {
  position: absolute;
  z-index: 40;
  top: 64px;
  left: 0;
}

.cal-pop--open.cal-pop--end {
  right: 0;
  left: auto;
}

/* --- The standing-schedule card and the line under it ----------------------------- */

.schedule-card {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  margin-top: 24px;
  padding: 24px;
  background: var(--surface);
  border-radius: var(--radius-card);
}

.schedule-card__title {
  margin: 0;
  font-size: 16px;
  font-weight: 500;
  color: var(--text);
}

.schedule-card__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 12px 0 10px;
  padding: 0;
  list-style: none;
}

.schedule-card__chip {
  display: inline-flex;
  align-items: center;
  height: 30px;
  padding: 0 12px;
  font-size: 14px;
  white-space: nowrap;
  background: var(--hl);
  border-radius: var(--radius-pill);
}

.schedule-card__none,
.schedule-card__note {
  margin: 12px 0 0;
  font-size: 14px;
  color: var(--muted);
}

.schedule-card__chips + .schedule-card__note {
  margin-top: 0;
}

.calendar__foot {
  margin: 16px 0 0;
  font-size: 14px;
  color: var(--muted);
}

.calendar__feed-link {
  color: var(--accent);
}

/* --- The schedule editor (?grafik=1) ------------------------------------------ */

/* 88 px under the last row, like under the other panel columns: the screen
   ends in its own "Zapisz", and the page footer should not crowd it. */
.schedule {
  display: flex;
  flex-direction: column;
  padding-bottom: 88px;
}

/* The first submit of the form, so that Enter in a field saves: present for
   the browser, absent for everyone else. */
.schedule__default {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.schedule__back {
  display: inline-flex;
  align-self: flex-start;
  align-items: center;
  gap: 4px;
  margin: -8px 0 12px -4px;
  padding: 2px 4px;
  font-size: 14px;
  color: var(--muted);
  border-radius: 8px;
}

.schedule__back:hover {
  color: var(--accent);
}

.schedule__cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: 24px;
  align-items: start;
}

.schedule__side {
  position: sticky;
  top: 24px;
}

.schedule__section-title {
  margin: 0 0 16px;
  font-size: 18px;
  font-weight: 500;
  color: var(--text);
}

.schedule__days {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* The hours' footer stands under the days on the page itself, not at the end
   of a card, so it has no hairline to set it off. */
.schedule__hours .card-foot {
  margin-top: 16px;
  padding-top: 0;
  border-top: 0;
}

.schedule-day {
  display: grid;
  grid-template-columns: 140px 52px minmax(0, 1fr);
  gap: 0 12px;
  align-items: start;
  padding: 20px;
  background: var(--surface);
  border-radius: var(--radius-card);
}

.schedule-day--off {
  background: rgba(255, 255, 255, 0.55);
}

.schedule-day__name {
  font-size: 16px;
  font-weight: 500;
  line-height: 46px;
  color: var(--text);
}

.schedule-day--off .schedule-day__name {
  font-weight: 400;
  color: var(--muted);
}

.schedule-day__switch {
  margin-top: 11.5px;
}

.schedule-day__none {
  line-height: 46px;
  color: var(--muted);
}

.schedule-day__windows {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}

.schedule-window {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.schedule-window__time,
.schedule-window__length {
  height: 46px;
  padding: 0 12px;
  font-family: var(--font-sans);
  font-size: 15px;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-field);
}

.schedule-window__time {
  width: 112px;
}

.schedule-window__length {
  width: 140px;
  margin-left: 8px;
}

.schedule-window__time:focus,
.schedule-window__length:focus {
  border-color: var(--accent);
  outline: none;
  box-shadow: var(--ring);
}

.schedule-window--error .schedule-window__time {
  border-color: var(--alert);
}

.schedule-window__sep {
  color: var(--muted);
}

.schedule-window__x,
.days-off__x {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}

.schedule-window__x:hover,
.days-off__x:hover {
  color: var(--alert);
  background: var(--hl);
}

.schedule-window__error,
.schedule-day__error {
  flex-basis: 100%;
  margin: 0;
  font-size: 14px;
  color: var(--alert);
}

.schedule-day__add {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  margin-left: -12px;
  padding: 0 12px;
  font-family: var(--font-sans);
  font-size: 15px;
  color: var(--accent);
  background: transparent;
  border: 0;
  border-radius: var(--radius-button);
  cursor: pointer;
}

.schedule-day__add:hover {
  background: var(--hl);
}

.days-off {
  padding: 24px;
  background: var(--surface);
  border-radius: var(--radius-card);
}

.days-off__list {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
}

.days-off__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 52px;
  padding: 0 4px 0 12px;
  border-radius: var(--radius-tile);
}

.days-off__row:hover {
  background: var(--hl);
}

.days-off__row + .days-off__row {
  border-top: 1px solid var(--line);
}

/* The question behind ×, in the row it is about — the mockup of 26.09. */
.days-off__row--asking,
.days-off__row--asking:hover {
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  height: auto;
  padding: 12px;
  background: var(--band);
}

.days-off__question {
  margin: 0;
  font-size: 15px;
}

.days-off__none {
  margin: 0;
  font-size: 15px;
  color: var(--muted);
}

.days-off__open {
  gap: 6px;
  margin-top: 16px;
}

.days-off__form {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  margin-top: 16px;
}

.days-off__actions {
  display: flex;
  gap: 8px;
}

/* --- The person's page, entered from a booked tile ------------------------------ */

/* The visit the tile opened the page on is the card open on arrival
   (section 31, .visit-panel); only "Odwołaj" inside it is styled here. */

.visit-cancel__open {
  display: inline-block;
  font-size: 14px;
  color: var(--alert);
  cursor: pointer;
  list-style: none;
}

.visit-cancel__open::-webkit-details-marker {
  display: none;
}

.visit-cancel__body {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  margin-top: 8px;
}

.visit-cancel__question {
  margin: 0;
  font-size: 14px;
  color: var(--text);
}

/* --- The loading skeleton ------------------------------------------------------ */

.calendar-skeleton {
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 12px;
  min-height: 320px;
}

.calendar-skeleton__day {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px;
  background: var(--surface);
  border-radius: var(--radius-card);
}

/* On the active state only: a resting `display` on the class itself shows six
   hundred pixels of grey over every plain render. htmx puts .htmx-request on
   the indicated element itself. .is-slow is the 400 ms wait of section 8:
   the skeleton stays display:none until then, and a fast answer never shows
   it at all. The first rule holds it back from section 8's rule for every
   indicator, which .htmx-request alone would match. */
.calendar-skeleton.htmx-request {
  display: none;
}

.calendar-skeleton.is-slow.htmx-request {
  display: grid;
}

/* Hide the card while its stand-in is showing, or the two stack. The sibling
   selector is why the skeleton stands before #kalendarz in the markup. The
   same two classes as the skeleton's: for the first 400 ms the week stays
   where it is, and a swap landing inside that time changes the dates in place. */
.calendar-skeleton.is-slow.htmx-request ~ #kalendarz {
  display: none;
}

/* Seven columns need 1180 px, a measured width: beside the panel menu a day
   narrower than that leaves a tile less room than "Krzysztof" in 13 px, and
   at 901 px only 14 px. No frame draws a narrower week, so below it the days
   stand one under another as on the phone (DESIGN_SPEC.md section 41.1); the
   touch targets stay the phone's own. */
@media (max-width: 1179px) {
  .cal-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .cal-day,
  .cal-day__head {
    min-height: 0;
  }

  .cal-day__date {
    display: flex;
    align-items: baseline;
    gap: 8px;
  }

  .calendar-skeleton {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
  }

  .calendar-skeleton__day:nth-child(n + 4) {
    display: none;
  }
}

/* --- Phone: the days one under another, the editor in one column ---------------- */

@media (max-width: 900px) {
  .calendar__add {
    margin-left: 0;
  }

  .cal-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .cal-day {
    min-height: 0;
  }

  .cal-day__head {
    min-height: 0;
  }

  .cal-day__date {
    display: flex;
    align-items: baseline;
    gap: 8px;
  }

  /* 44 px of touch target for the × of a tile and the + of a day; on a phone
     there is no hover, so the × is always there. */
  .cal-tile__x {
    width: 44px;
    height: 44px;
    right: 2px;
    background: transparent;
    opacity: 1;
  }

  .cal-day__add {
    width: 44px;
    height: 44px;
    margin: -8px -10px 0 0;
  }

  .cal-pop--open {
    position: static;
    width: auto;
  }

  .schedule-card {
    flex-direction: column;
  }

  .schedule__cols {
    grid-template-columns: minmax(0, 1fr);
  }

  .schedule__side {
    position: static;
  }

  .schedule-day {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 12px;
    padding: 16px;
  }

  .schedule-day__windows,
  .schedule-day__none {
    grid-column: 1 / -1;
  }

  .schedule-day__none {
    line-height: 1.4;
  }

  .schedule-window__length {
    margin-left: 0;
  }

  .calendar-skeleton {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
  }

  .calendar-skeleton__day:nth-child(n + 4) {
    display: none;
  }
}

/* ==========================================================================
   23. Rezerwacja wizyty — frames 8a, 8b, 11b, 2m
   The patient's side of the calendar: the seven-day grid of free terms with
   the sticky summary (8a), the confirmation (8b), the visits panel (2m).
   Grid dimensions and the three hour states come from DESIGN_SPEC.md
   section 10; the phone layout is section 10's "dwie kolumny, dzień jako
   nagłówek sekcji" plus the frame's own top bar and bottom summary bar
   (11b). The one shadow is the sanctioned --shadow-savebar, on the phone bar.
   ========================================================================== */

.booking {
  padding: 48px var(--gutter) 88px;
}

.booking__inner {
  max-width: var(--content-max);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

/* The frame's own top bar exists only on the phone (note 01 beside 11b);
   the desktop keeps the site header and the breadcrumb. */
.booking__topbar {
  display: none;
}

.booking__layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 380px;
  gap: 40px;
  align-items: start;
}

.booking__main {
  display: flex;
  flex-direction: column;
  gap: 22px;
  min-width: 0;
}

.booking__kicker {
  color: var(--accent);
}

.booking__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 36px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text);
}

.booking__group-label {
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
}

.booking__modes {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* The reschedule strip and its way out — no frame draws them
   (DESIGN_SPEC.md section 48). The column and its 10 px are `.booking__modes`
   above, because this block stands exactly where that one does and replaces
   it; the exit takes the size and the treatment of `.visit-card__cancel` on
   2m, which is the other link in this project that leaves an action alone. */
.booking__moving {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.booking__keep {
  align-self: flex-start;
  font-size: 15px;
  color: var(--text);
  text-decoration: underline;
}

.booking__pills {
  display: flex;
  gap: 8px;
}

/* --- The grid (DESIGN_SPEC.md section 10) --------------------------------------- */

.booking__terms {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.booking-terms__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
}

.booking-terms__nav {
  display: flex;
  align-items: center;
  gap: 14px;
}

.booking-terms__arrow {
  font-size: 14px;
  color: var(--accent);
  text-decoration: none;
}

.booking-terms__arrow--off {
  color: var(--muted);
}

.booking-terms__range {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text);
}

.booking-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 8px;
}

.booking-day {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.booking-day__head {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin: 0;
  padding-bottom: 8px;
  border-bottom: 2px solid var(--line-strong);
  font-weight: 400;
}

.booking-day__abbr {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.booking-day__date {
  font-size: 15px;
  color: var(--text);
}

.booking-day--weekend .booking-day__date {
  color: var(--muted);
}

.booking-day__hours {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Section 10: a free hour is --accent on --surface; the picked one inverts;
   a taken hour is not rendered at all. */
.booking-hour {
  text-align: center;
  font-size: 15px;
  color: var(--accent);
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-control);
  padding: 11px 0;
  text-decoration: none;
}

.booking-hour--selected {
  color: var(--on-accent);
  background: var(--accent);
  border-color: var(--accent);
}

/* "Dzień bez terminów zostaje w siatce, wyszarzony." */
.booking-day__none {
  text-align: center;
  color: var(--muted);
  padding: 11px 0;
}

/* --- The summary rail (note 04: the one accent frame on the page) --------------- */

.booking__rail {
  position: sticky;
  top: 24px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* One form holds every child of the rail (the card, the notice, the
   cancellation note and the phone bar), so it lays them out as the rail did. */
.booking__form {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.booking-summary {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 24px;
  background: var(--surface);
  border: 1px solid var(--accent);
  border-top: 5px solid var(--accent);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.booking-summary__specialist {
  display: flex;
  align-items: center;
  gap: 12px;
}

.booking-summary__photo {
  flex: none;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  object-fit: cover;
}

.booking-summary__photo--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--tint);
  color: var(--accent);
  font-size: 15px;
  font-weight: 500;
}

.booking-summary__name {
  font-size: 16px;
  font-weight: 500;
  color: var(--text);
}

.booking-summary__rows {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 0;
}

.booking-summary__row dt {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.booking-summary__row dd {
  margin: 4px 0 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
}

.booking-summary__payment {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  background: var(--tint);
  border-radius: var(--radius-small);
}

.booking-summary__payment-label {
  font-size: 14px;
  color: var(--text);
}

.booking-summary__price {
  font-family: var(--font-serif);
  font-size: 24px;
  font-weight: 500;
  color: var(--text);
}

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

.booking-summary__no-fees {
  font-size: 13px;
  color: var(--muted);
  text-align: center;
}

/* The specialist's notice dresses like the cancellation note below it: note
   04 allows the page one accent frame, and that is the summary card. Unlike
   the card and the note, the phone keeps it, because its box has to be
   ticked before the bar's button books. */
.booking-notice {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 20px 24px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.booking-notice__label {
  color: var(--accent);
}

.booking-notice__lead {
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

.booking-notice__text {
  font-size: 15px;
  line-height: 1.55;
  color: var(--text);
  overflow-wrap: anywhere;
}

.booking-cancellation {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 20px 24px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.booking-cancellation__label {
  color: var(--accent);
}

.booking-cancellation__rule {
  font-size: 14px;
  line-height: 1.55;
  color: var(--muted);
}

/* --- The confirmation screen (step 2, the mockup's "#rezerwacja") ------------------
   8a's two columns: the visit on the left, the price and the button in a
   sticky card on the right. The card is the page's one accent frame, as the
   summary is on 8a; the notice keeps its 8a dress below the visit. */

.booking-confirm__card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 24px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.booking-confirm__rows {
  margin: 12px 0 0;
}

.booking-confirm__row {
  display: grid;
  grid-template-columns: 170px minmax(0, 1fr);
  gap: 16px;
  padding: 16px 0;
  border-top: 1px solid var(--line);
}

.booking-confirm__row dt {
  padding-top: 2px;
  font-size: 14px;
  color: var(--muted);
}

.booking-confirm__row dd {
  margin: 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
  overflow-wrap: anywhere;
}

.booking-confirm__change {
  margin-left: 16px;
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
}

.booking-confirm__sub {
  display: block;
  font-size: 14px;
  color: var(--muted);
}

.booking-confirm__service {
  width: 100%;
  max-width: 360px;
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-small);
}

.booking-confirm__service:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}

.booking-confirm__pay {
  position: sticky;
  top: 24px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 24px;
  background: var(--surface);
  border: 1px solid var(--accent);
  border-top: 5px solid var(--accent);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.booking-confirm__price-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.booking-confirm__price {
  font-family: var(--font-serif);
  font-size: 32px;
  font-weight: 500;
  line-height: 1.2;
  color: var(--text);
}

.booking-confirm__note {
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

.booking-confirm__rule {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}

/* The phone's bottom bar lives in the same #podsumowanie wrapper and only
   the phone shows it. */
.booking-bar {
  display: none;
}

/* --- The loading skeleton -------------------------------------------------------- */

.booking-skeleton {
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 8px;
  min-height: 240px;
}

.booking-skeleton__day {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 12px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
}

/* On the active state only: a resting `display` on the class itself would
   stand over every plain render. .is-slow is the 400 ms wait of section 8,
   the same arrangement as the specialist's calendar in section 22: a week
   arrow that answers inside it changes the dates in place and shows no grey.
   The first rule holds it back from section 8's rule for every indicator. */
.booking-skeleton.htmx-request {
  display: none;
}

.booking-skeleton.is-slow.htmx-request {
  display: grid;
}

.booking-skeleton.is-slow.htmx-request ~ #terminy {
  display: none;
}

/* --- The confirmation (8b) ------------------------------------------------------- */

.confirmation {
  padding: 64px var(--gutter) 84px;
}

.confirmation__inner {
  max-width: 760px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 32px;
}

/* Note 02: the one use of green on the public page — the "potwierdzone"
   state, the same colour as a free term in the catalogue. */
.confirmation__chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  align-self: flex-start;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ok);
  border: 1px solid var(--ok);
  border-radius: var(--radius-pill);
  padding: 6px 14px;
}

.confirmation__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ok);
}

.confirmation__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 38px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text);
}

.confirmation__lead {
  font-size: 17px;
  line-height: 1.6;
  color: var(--muted);
}

.confirmation__card {
  display: grid;
  grid-template-columns: 1fr 1fr;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.confirmation__half {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 20px 26px;
}

.confirmation__half:first-child {
  border-right: 1px solid var(--line);
}

.confirmation__name {
  font-size: 16px;
  font-weight: 500;
  color: var(--text);
}

.confirmation__detail {
  font-size: 14px;
  color: var(--muted);
}

.confirmation__place {
  font-size: 16px;
  color: var(--text);
}

/* "Pokaż na mapie" — the frame draws it inside the detail line, after the
   room number, at the size and colour of the line it sits in. The accent and
   the underline are what separate a link from the grey around it; an external
   destination gets no icon, because the frame draws none. */
.confirmation__map {
  color: var(--accent);
  text-decoration: underline;
}

.confirmation__payment {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
  background: var(--tint);
  border-radius: var(--radius-small);
}

.confirmation__payment-label {
  font-size: 15px;
  color: var(--text);
}

.confirmation__price {
  font-family: var(--font-serif);
  font-size: 22px;
  font-weight: 500;
  color: var(--text);
}

/* Note 03: the take-with-you list shows here, not only in the panel. The
   4 px bars run through the system colours, one per row, like the frame. */
.confirmation__bring {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.confirmation__bring-label {
  margin: 0;
  font-size: 16px;
  font-weight: 500;
  color: var(--text);
}

.confirmation__bring-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.confirmation__bring-item {
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
  padding-left: 14px;
  border-left: 4px solid var(--c1);
}

.confirmation__bring-item--2 {
  border-left-color: var(--c2);
}

.confirmation__bring-item--3 {
  border-left-color: var(--c3);
}

/* Note 04: three exits in one row, in falling order of emphasis. */
.confirmation__actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.confirmation__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

.confirmation__plans {
  font-size: 15px;
  color: var(--muted);
}

.confirmation__visits-link {
  flex: none;
  font-size: 15px;
  color: var(--accent);
  text-decoration: none;
}

/* --- The visits panel (2m) -------------------------------------------------------- */

.visits {
  display: flex;
  flex-direction: column;
  gap: 28px;
  min-width: 0;
}

.visits__heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding-bottom: 14px;
  border-bottom: 2px solid var(--accent);
}

.visits__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 32px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text);
}

.visits__count {
  font-size: 14px;
  color: var(--muted);
}

/* The facts of an upcoming visit, inside its tile (section 38): the kicker,
   the length and the price, a moved term, the profile and the address. */
.visit-card__facts {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}

.visit-card__kicker {
  color: var(--accent);
}

.visit-card__details {
  font-size: 15px;
  color: var(--muted);
}

.visit-card__name {
  font-size: 15px;
  font-weight: 500;
  color: var(--accent);
  text-decoration: none;
}

.visit-card__address {
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
}

/* The same link as .confirmation__map on 8b, inside the same kind of grey
   line — one rule each rather than a shared class, because the two lines are
   different sizes and a shared class would have to say so twice anyway. */
.visit-card__map {
  color: var(--accent);
  text-decoration: underline;
}

/* Note 03: "Odwołaj" as underlined text beside the quieter alternatives —
   the order nudges without hiding the exit. */
.visit-card__actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px 24px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

.visit-card__actions-left,
/* The pair of note 03 — "Przełóż" quiet, "Odwołaj" underlined — held in one
   group so the order survives the flexbox that puts them on the right. The
   gap is the left group's: two lists of links on one row read as one row
   only if they are spaced alike. */
.visit-card__actions-right {
  display: flex;
  align-items: center;
  gap: 20px;
}

/* The trace of a moved term, under the details line of the card. 14 px, the
   step below the 15 px of `.visit-card__details`, because it qualifies that
   line rather than adding a fact beside it. */
.visit-card__moved {
  font-size: 14px;
  color: var(--muted);
}

.visit-card__action {
  font-size: 15px;
  color: var(--accent);
  text-decoration: none;
}

.visit-card__cancel {
  font-size: 15px;
  color: var(--text);
  text-decoration: underline;
}

.visit-card__confirm {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 20px;
  background: var(--tint);
  border-radius: var(--radius-tile);
}

.visit-card__confirm-text {
  font-size: 15px;
  color: var(--text);
}

.visit-card__confirm-actions {
  display: flex;
  align-items: center;
  gap: 14px;
}

.visit-card__keep {
  font-size: 15px;
  color: var(--muted);
  text-decoration: underline;
}

/* The line between the upcoming tiles and the past ones. */
.visits__history-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding: 14px 0 4px;
}

.visits__history-label {
  font-size: 16px;
  color: var(--text);
}

.visits__history-note {
  font-size: 14px;
  color: var(--muted);
}

/* The name and the stamp of a past visit, on the head line of its tile: the
   size and the colour are the line's (.visit-panel__where). */
.visit-row__name {
  color: var(--text);
}

/* Both actions of a history row sit at its right edge, in the frame's order:
   the review first, "Umów ponownie" after it. */
.visit-row__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

.visit-row__rebook {
  flex: none;
  font-size: 15px;
  color: var(--accent);
  text-decoration: none;
}

/* The frame draws "Oceń wizytę" in --muted, quieter than the accent beside
   it: rebooking is the action this screen is for, and the review is a favour
   being asked. */
.visit-row__review {
  flex: none;
  font-size: 15px;
  color: var(--muted);
}

/* Shrinks, so that the no-show sentence (two lines on a phone) wraps inside
   the card; "oceniona" is one word and stays as it was. */
.visit-row__reviewed {
  flex: 0 1 auto;
  min-width: 0;
  font-size: 14px;
  color: var(--muted);
}

/* --- Taking an opinion back ------------------------------------------------- */

/* Quieter than "Umów ponownie" beside it and quieter than the word "oceniona"
   it follows: this is a way out, not the thing the screen is for — the same
   ranking "Odwołaj" takes on the card above, underlined so that it reads as a
   way out rather than as a second label. */
.visit-row__withdraw {
  flex: none;
  font-size: 14px;
  color: var(--muted);
  text-decoration: underline;
}

/* The card's confirm strip, at row scale. It takes the place of the actions
   rather than standing under them, so the row keeps one line and one
   question — hence no background of its own: the card is a card and this is a
   row between two rules. */
.visit-row__confirm {
  display: flex;
  align-items: center;
  gap: 16px;
}

.visit-row__confirm-text {
  font-size: 14px;
  color: var(--text);
}

.visit-row__confirm-actions {
  flex: none;
  display: flex;
  align-items: center;
  gap: 14px;
}

.visit-row__keep {
  flex: none;
  font-size: 14px;
  color: var(--muted);
  text-decoration: underline;
}

.visits__cross-sell {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 20px 22px;
  background: var(--tint);
  border-radius: var(--radius-card);
}

.visits__cross-sell-text {
  font-size: 15px;
  line-height: 1.5;
  color: var(--muted);
}

.visits__cross-sell-link {
  flex: none;
  font-size: 15px;
  color: var(--accent);
  text-decoration: none;
}

/* --- Phone (11b): the frame's own top bar, two hour columns, the bottom bar ------ */

@media (max-width: 900px) {
  /* Note 01 beside 11b: the site header would waste the strip the back
     link needs — the wrapper hides it and the top bar stands in. */
  .booking-site-header {
    display: none;
  }

  .booking {
    padding: 0 var(--gutter) 96px;
  }

  .booking__topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    /* The 58 px of this bar is the phone value of --header-h from section 2,
       so the two bars of frame 11b — the one it replaces and the one it is —
       cannot drift apart. */
    height: var(--header-h);
    margin: 0 calc(var(--gutter) * -1);
    padding: 0 var(--gutter);
    background: var(--surface);
    border-bottom: 1px solid var(--line);
  }

  .booking__back {
    font-size: 15px;
    color: var(--text);
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .booking__step {
    flex: none;
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--muted);
  }

  .booking__breadcrumbs {
    display: none;
  }

  .booking__inner {
    gap: 20px;
  }

  .booking__layout {
    grid-template-columns: minmax(0, 1fr);
    gap: 20px;
  }

  .booking__title {
    font-size: 26px;
  }

  /* The heading and the week switch do not fit on one line at 390 px: the four
     parts squeeze into four narrow columns and every one of them wraps. The
     heading takes its own row and the switch keeps the next (measured). */
  .booking-terms__head {
    flex-wrap: wrap;
    gap: 6px 20px;
  }

  .booking-terms__nav {
    width: 100%;
    justify-content: space-between;
  }

  /* Section 10, phone: two columns, the day as a section header. */
  .booking-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
  }

  .booking-day__head {
    border-bottom-width: 1px;
    border-bottom-color: var(--line);
  }

  .booking-day__hours {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
  }

  .booking-hour {
    font-size: 16px;
    padding: 13px 0;
  }

  .booking-day__none {
    text-align: left;
    padding: 6px 0;
  }

  /* The rail folds into the flow; the card and the cancellation note give
     way to the sticky bar the frame draws over the footer. */
  .booking__rail {
    position: static;
  }

  .booking-summary,
  .booking-cancellation {
    display: none;
  }

  /* The confirmation screen keeps every part and stacks them: the rows
     fold their label above the value, and the price card follows the
     visit in the flow. */
  .booking-confirm__row {
    grid-template-columns: minmax(0, 1fr);
    gap: 4px;
  }

  .booking-confirm__pay {
    position: static;
  }

  .booking-bar {
    position: sticky;
    bottom: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    margin: 0 calc(var(--gutter) * -1);
    padding: 14px var(--gutter);
    background: var(--surface);
    border-top: 1px solid var(--line);
    box-shadow: var(--shadow-savebar);
  }

  .booking-bar__facts {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
  }

  .booking-bar__term {
    font-size: 13px;
    color: var(--muted);
  }

  .booking-bar__price {
    font-family: var(--font-serif);
    font-size: 20px;
    font-weight: 500;
    color: var(--text);
  }

  .booking-skeleton {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
  }

  .booking-skeleton__day:nth-child(n + 4) {
    display: none;
  }

  /* 8b on the phone: one column, the halves stacked. */
  .confirmation {
    padding: 40px var(--gutter) 64px;
  }

  .confirmation__title {
    font-size: 28px;
  }

  .confirmation__card {
    grid-template-columns: minmax(0, 1fr);
  }

  .confirmation__half:first-child {
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }

  .confirmation__actions {
    flex-wrap: wrap;
  }

  .confirmation__footer {
    flex-wrap: wrap;
  }

  /* 2m on the phone. */
  .visits__title {
    font-size: 26px;
  }

  .visit-card__confirm {
    flex-wrap: wrap;
  }

  /* The question and its two answers do not fit one narrow line, and the row
     they replace is itself allowed to stack here. */
  .visit-row__confirm {
    flex-wrap: wrap;
  }

  .visits__cross-sell {
    flex-wrap: wrap;
  }
}

/* ==========================================================================
   24. Opinie — frames 10c and 1e
   The one screen a patient reaches from an e-mail and from nowhere else. Four
   states share the page: the form, and the three sentences that stand in for
   it (link expired, visit not held yet, already reviewed). The reviews
   section of the profile is at the bottom of this block rather than beside the
   rest of 1e.
   Dimensions come from DESIGN_SPEC.md where it has an opinion and from the
   frame where it does not — sections 1 (buttons), 2 (field, textarea,
   counter) and 5 (pill radius and padding). The register of what the frame
   draws differently is DESIGN_SPEC.md section 32.
   ========================================================================== */

.review-page {
  padding: 64px var(--gutter) 88px;
}

.review-page__inner {
  max-width: 640px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 28px;
}

.review-page__kicker {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}

.review-page__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 34px;
  line-height: 1.18;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text);
}

.review-page__lead {
  margin: 0;
  font-size: 17px;
  line-height: 1.7;
  color: var(--muted);
  text-wrap: pretty;
}

/* The three no-form states. The shape of the empty state (section 13): a
   heading naming what happened, a sentence under it and a way out. */
.review-page__state {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.review-page__actions {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

/* --- The specialist card ---------------------------------------------------- */

.review-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 20px 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

/* 48 px on the frame, where the visit card of 2m draws 46. Both are the same
   element in two screens; this one stands alone on a 640 px column and the
   frame's own number is the better fit for it. */
.review-card__avatar {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--t4);
  color: var(--c4);
  font-size: 16px;
  letter-spacing: 0.02em;
}

.review-card__facts {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.review-card__name {
  font-size: 16px;
  font-weight: 500;
  color: var(--text);
  text-decoration: none;
}

.review-card__name:hover {
  text-decoration: underline;
}

.review-card__detail {
  font-size: 14px;
  color: var(--muted);
}

/* --- The stars -------------------------------------------------------------- */

.rating {
  margin: 0;
  padding: 0;
  border: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.rating__label {
  padding: 0;
  font-size: 16px;
  font-weight: 500;
  color: var(--text);
}

.rating__row {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* row-reverse is load-bearing, not layout taste: the markup runs 5 → 1 so
   that `input:checked ~ label` can fill every star *below* the chosen one,
   which is the direction CSS has a combinator for. justify-content puts the
   mirrored row back on the left. */
.rating__stars {
  display: flex;
  flex-direction: row-reverse;
  justify-content: flex-end;
  gap: 6px;
}

.rating__stars input {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* The empty star was --line-strong, which is 1.46:1 on white — the colour of a
   rule between table rows, used here to carry the state of a control. Whether a
   star is filled is the only thing this widget says, so its two colours have to
   be told apart: --muted is 5.51 against the ground and 2.9 against the gold
   beside it, and the caption "N z 5" says the same thing in words.
   Empty against filled is 2.47, which is why the caption is not optional. */
.rating__star {
  font-size: 34px;
  line-height: 1;
  color: var(--muted);
  cursor: pointer;
}

/* The words a screen reader announces — "4 z 5" — beside a glyph that says
   nothing to it. Clipped, not display:none: a removed name is not announced
   at all, and then the five radios are indistinguishable. */
.rating__star-name {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.rating__stars input:checked ~ .rating__star {
  color: var(--star);
}

/* Keyboard focus has to be visible on a control whose input is off-screen:
   the outline goes on the label the input names. Section 1's focus ring. */
.rating__stars input:focus-visible + .rating__star {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* "4 z 5" beside the row. Keyed on the value rather than on an id, because
   Django names the ids and this file must not depend on how. */
.rating__value {
  font-size: 16px;
  color: var(--muted);
}

.rating:has(input[value="1"]:checked) .rating__value::after {
  content: "1 z 5";
}

.rating:has(input[value="2"]:checked) .rating__value::after {
  content: "2 z 5";
}

.rating:has(input[value="3"]:checked) .rating__value::after {
  content: "3 z 5";
}

.rating:has(input[value="4"]:checked) .rating__value::after {
  content: "4 z 5";
}

.rating:has(input[value="5"]:checked) .rating__value::after {
  content: "5 z 5";
}

.rating--error .rating__star {
  color: var(--alert);
}

/* --- The form --------------------------------------------------------------- */

.review-form {
  display: flex;
  flex-direction: column;
  gap: 28px;
}

.review-form__actions {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

/* --- The reviews section of 1e ----------------------------------------------- */

/* The label and the summary share a line, which is the only place on the
   profile where a section heading has something beside it — the frame draws
   "Recenzje" with "4,9 · 24 opinie" on the right of the same row. */
.profile-reviews__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
}

.profile-reviews__summary {
  font-size: 14px;
  color: var(--muted);
}

.profile-reviews {
  display: flex;
  flex-direction: column;
}

/* A rule above each row rather than between them: the first row is separated
   from the heading by the same line, which is how the frame draws it. */
.review-row {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 20px 0;
  border-top: 1px solid var(--line);
}

.review-row__stars {
  font-size: 13px;
  letter-spacing: 0.14em;
  color: var(--star);
}

.review-row__meta {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.review-row__author {
  font-size: 14px;
  color: var(--text);
}

.review-row__date {
  font-size: 13px;
  color: var(--muted);
}

.review-row__body {
  margin: 0;
  font-size: 16px;
  line-height: 1.68;
  color: var(--text);
  text-wrap: pretty;
}

/* "Zgłoś opinię" — as quiet as "Zgłoś problem z profilem" in the rail: a way
   to report, not an invitation to. */
.review-row__report {
  align-self: flex-start;
  font-size: 13px;
  color: var(--muted);
  text-decoration: none;
}

.review-row__report:hover {
  color: var(--text);
  text-decoration: underline;
}

/* Where the frame puts "Wszystkie opinie (24) →". It is a disclosure rather
   than a link, because the address the arrow points at is not in ROUTES.md and
   never was. Two rows stand open above it and the rest of the list is inside;
   ten reviews are what makes this section long, and a page that ends in
   a screen and a half of somebody else's sentences is what the fold is for.

   The same <details> the FAQ and the filter pills use, and here it is the plain
   kind: the content sits inside the element, because this section reads the
   same on both widths. */
/* No margin of its own: the section is a flex column with a 16 px gap and the
   last row carries 20 px of padding under its text, which is the 36 px the rest
   of this page puts between a block and the thing after it. */
.profile-reviews__toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  font-size: 15px;
  color: var(--accent);
  list-style: none;
  cursor: pointer;
}

.profile-reviews__toggle::-webkit-details-marker {
  display: none;
}

.profile-reviews__toggle:hover {
  text-decoration: underline;
}

/* One word shows, the other waits — both live in the template, because a Polish
   sentence built out of `content` would be a Polish sentence in the stylesheet
   (static/js/README.md keeps the same rule for the scripts). */
.profile-reviews__toggle-word--open,
.profile-reviews__more[open] .profile-reviews__toggle-word--shut {
  display: none;
}

.profile-reviews__more[open] .profile-reviews__toggle-word--open {
  display: inline;
}

/* The chevron of the rail pills, turned a quarter further when it is open. */
.profile-reviews__toggle-mark {
  flex: none;
  width: 7px;
  height: 7px;
  margin-top: -3px;
  border-right: 1.4px solid currentcolor;
  border-bottom: 1.4px solid currentcolor;
  transform: rotate(45deg);
}

.profile-reviews__more[open] .profile-reviews__toggle-mark {
  margin-top: 3px;
  transform: rotate(-135deg);
}

/* The rows inside carry the same rule above each of them as the two outside,
   so the fold opens into a list that reads as one. */
.profile-reviews--rest {
  margin-top: 20px;
}

/* --- Phone ------------------------------------------------------------------ */

@media (max-width: 900px) {
  .review-page {
    padding: 40px var(--gutter) 64px;
  }

  .review-page__title {
    font-size: 26px;
  }

  /* The row can wrap on a narrow screen; the stars must not be split from
     their reading. */
  .rating__row {
    flex-wrap: wrap;
  }
}

/* ==========================================================================
   25. Płatności i dodanie karty — frames 2k and 10b
   Two screens of the panel, both whole: 10b, and 2k with its billing card,
   its payment method and its way out. One section rather than two, because
   a second one for the same screen would cut it in half along a line nobody
   reading the sheet can see.
   The three card fields are Stripe's, mounted into empty boxes that copy
   section 2 of DESIGN_SPEC.md down to the pixel: 46 px, because the left
   column of 10b is a one-column form and section 2 gives those 46–48. The
   focus and error borders are set on the wrapper from static/js/card.js,
   since a class of ours cannot cross into Stripe's frame.
   2k stands in the two columns of cards section 19 draws for 2l, and its
   billing row is 2l's .account-row.
   The register of what the frames draw differently is DESIGN_SPEC.md
   section 33.
   ========================================================================== */

/* 2k stands in the frame of section 19 (.settings, .settings__cols,
   .settings-card), the panel mockup of 24.09. What is its own is below. */

/* Said in place of the "Dodaj kartę" button while there is no Stripe
   configuration behind the screen; none is set up yet. */
.subscription__note {
  margin: 0;
  font-size: 14px;
  color: var(--muted);
}

/* The saved card, as the brand and its last four digits. */
.subscription__card {
  font-size: 15px;
  color: var(--text);
  white-space: nowrap;
}

/* The form exists to carry the CSRF token; it must not add a box of its own
   around the button. */
.subscription__remove {
  display: flex;
}

/* The question before the card is removed, in the row „Metoda płatności”. The
   row is a flex line of text and actions; the question takes a line of its
   own under both, the way the removal of the account expands on 2l. */
.settings-card--row:has(.subscription__confirm) {
  flex-wrap: wrap;
}

.subscription__confirm {
  display: flex;
  flex-basis: 100%;
  flex-direction: column;
  gap: 12px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

.subscription__question {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text);
}

.subscription__confirm-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

/* The block „Faktury VAT” under „Rozliczenie”: a table in the card, ruled like
   the rows of 2l. The KSeF number takes the mono face section 6 gives invoice
   numbers; an invoice on its way into KSeF is muted text, not a badge; a row
   held for buyer details is in the warning colour, like the payment strip. */
.vat-invoices__table {
  width: 100%;
  margin-top: 14px;
  border-collapse: collapse;
  font-size: 14px;
  line-height: 1.5;
}

.vat-invoices__table th {
  padding: 0 12px 8px 0;
  font-weight: 400;
  white-space: nowrap;
  text-align: left;
  color: var(--muted);
}

.vat-invoices__table td {
  padding: 12px 12px 12px 0;
  border-top: 1px solid var(--line);
  color: var(--text);
  vertical-align: middle;
}

.vat-invoices__table tbody tr:last-child td {
  border-bottom: 1px solid var(--line);
}

.vat-invoices__table th:last-child,
.vat-invoices__table td:last-child {
  padding-right: 0;
  text-align: right;
}

.vat-invoices__amount {
  white-space: nowrap;
}

.vat-invoices__ksef {
  font-family: var(--font-mono);
  font-size: 12px;
  overflow-wrap: anywhere;
}

.vat-invoices__ksef--pending {
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--muted);
}

.vat-invoices__file a {
  color: var(--accent);
  white-space: nowrap;
}

.vat-invoices__table .vat-invoices__row--missing td {
  text-align: left;
}

.vat-invoices__missing {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  color: var(--warn);
}

/* The column of the PDF link has no visible heading; a screen reader still
   hears one. */
.vat-invoices__hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* On a phone each invoice is a block: number and amount, then date and KSeF
   number, then the link. The left column takes what the number and the date
   need; the right one wraps the long KSeF number. */
@media (max-width: 900px) {
  .vat-invoices__table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }

  .vat-invoices__table tbody tr.vat-invoices__row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 4px 16px;
    padding: 12px 0;
    border-top: 1px solid var(--line);
  }

  .vat-invoices__table tbody tr:last-child {
    border-bottom: 1px solid var(--line);
  }

  .vat-invoices__table tbody tr.vat-invoices__row td,
  .vat-invoices__table tbody tr:last-child td {
    padding: 0;
    border: 0;
    text-align: left;
  }

  .vat-invoices__number {
    grid-column: 1;
    grid-row: 1;
  }

  .vat-invoices__table .vat-invoices__amount {
    grid-column: 2;
    grid-row: 1;
    text-align: right;
  }

  .vat-invoices__date {
    grid-column: 1;
    grid-row: 2;
    color: var(--muted);
  }

  .vat-invoices__table .vat-invoices__ksef {
    grid-column: 2;
    grid-row: 2;
    text-align: right;
  }

  .vat-invoices__table .vat-invoices__file {
    grid-column: 1 / -1;
    grid-row: 3;
  }

  .vat-invoices__file:empty {
    display: none;
  }

  .vat-invoices__table tbody tr.vat-invoices__row--missing {
    display: block;
    padding: 12px 0;
    border-top: 1px solid var(--line);
  }

  .vat-invoices__table tbody tr.vat-invoices__row--missing td {
    display: block;
    padding: 0;
    border: 0;
  }

  .vat-invoices__missing {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }
}

/* --- Dodanie karty (10b) -------------------------------------------------- */

.pay {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 820px;
  padding-bottom: 88px;
}

.pay__back {
  font-size: 14px;
  color: var(--accent);
}

.pay .panel__heading {
  margin-bottom: 16px;
}

.pay__grid {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 44px;
  align-items: start;
}

.pay__form {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* The consent sits in a box of its own, which is what the frame draws around
   it — the sentence is a condition of the screen and not one more field.
   Named under .pay rather than .consent, which is the 12d page wrapper: sharing
   that name gave this box the wrapper's padding and the wrapper this card. */
.pay__consent {
  align-items: flex-start;
  gap: 12px;
  padding: 16px 18px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

/* --- Phone ------------------------------------------------------------------ */

@media (max-width: 900px) {
  /* Section 15: one column. */
  .pay__grid {
    grid-template-columns: 1fr;
    gap: 28px;
  }
}

/* ==========================================================================
   26. Baza wiedzy — frames 9a, 9b, 10f
   Three screens and one band. Every value comes from DESIGN_SPEC.md section
   35, written first for exactly that reason: before it the specification said
   nothing about these screens at all.
   The band "Z bazy wiedzy" of the home page lives here and not in section 11,
   because it is drawn with the card of 9a and one card cannot stand in two
   sections. Section 11 carries a pointer.
   ========================================================================== */

/* Section 4 sets the reset for an underline: `a` carries the browser's by
   default and every component that is a card, a heading or an action opts out
   of it. Grouped here rather than repeated fourteen times; what keeps its
   underline is a link inside running text.

   The two bylines are the ones that moved. "Autorka: <nazwisko> · <data> ·
   <n> min" is running text, and a link inside running text told apart from its
   neighbours by colour alone fails WCAG 1.4.1 unless the two colours
   are 3:1 apart — --accent on --muted is 1.28. The axe run reported it on 9a
   and nowhere else in twenty-eight pages, because 9b lays its byline out as
   flex items and the rule only fires inside a text block; both are underlined
   here, since one byline underlined and the other not would be a difference
   nobody could explain. */
.article-card,
.kb-featured__title a,
.kb-more,
.kb-crumbs__inner a,
.kb-toc__link,
.kb-exit__link,
.kb-tile__button,
.kb-related__card,
.kb-author__linkedin,
.home-knowledge__all,
.home-origin__link,
.calculator__caveat-link {
  text-decoration: none;
}

.kb-crumbs__inner a:hover,
.kb-featured__title a:hover {
  text-decoration: underline;
}

/* The five category colours, bound once per component rather than five times
   per property. The pair is the one section 2 defines: --cN for the ink, --tN
   for the ground. No sixth pair gets added (DECISIONS.md). */
.article-card__kicker--c1,
.kb-featured__pill--c1,
.kb-entry__pill--c1,
.kb-related__card--c1,
.kb-tile--c1 {
  --kb-ink: var(--c1);
  --kb-ground: var(--t1);
}

.article-card__kicker--c2,
.kb-featured__pill--c2,
.kb-entry__pill--c2,
.kb-related__card--c2,
.kb-tile--c2 {
  --kb-ink: var(--c2);
  --kb-ground: var(--t2);
}

.article-card__kicker--c3,
.kb-featured__pill--c3,
.kb-entry__pill--c3,
.kb-related__card--c3,
.kb-tile--c3 {
  --kb-ink: var(--c3);
  --kb-ground: var(--t3);
}

.article-card__kicker--c4,
.kb-featured__pill--c4,
.kb-entry__pill--c4,
.kb-related__card--c4,
.kb-tile--c4 {
  --kb-ink: var(--c4);
  --kb-ground: var(--t4);
}

.article-card__kicker--c5,
.kb-featured__pill--c5,
.kb-entry__pill--c5,
.kb-related__card--c5,
.kb-tile--c5 {
  --kb-ink: var(--c5);
  --kb-ground: var(--t5);
}

/* --- Breadcrumbs, 9b and 10f (35.1) -------------------------------------- */

.kb-crumbs {
  padding: 20px var(--gutter);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.kb-crumbs__inner {
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: var(--content-max);
  margin: 0 auto;
  font-size: 14px;
  color: var(--muted);
}

.kb-crumbs__inner a {
  color: var(--muted);
}

.kb-crumbs__inner a:hover {
  color: var(--accent);
}

.kb-crumbs__sep {
  color: var(--muted);
}

/* The title of the entry is long enough to push the row into a second line on
   a narrow window, and a breadcrumb trail that wraps is worse than one that
   ends in an ellipsis. */
.kb-crumbs__current {
  overflow: hidden;
  color: var(--text);
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* --- 9a, the head band (35.2) -------------------------------------------- */

.kb-head {
  padding: 56px var(--gutter) 40px;
  background: var(--hero);
  border-bottom: 1px solid var(--line-strong);
}

.kb-head__inner {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.kb-head__kicker {
  color: var(--accent);
}

.kb-head__title {
  max-width: 640px;
  font-family: var(--font-serif);
  font-size: 46px;
  line-height: 1.12;
  font-weight: 500;
  letter-spacing: -0.015em;
  text-wrap: pretty;
}

/* Six pills, and the one that is selected is a span. Section 5 gives them
   their shape; the row only has to hold them. */
.article-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding-top: 8px;
}

/* --- 9a, the featured entry (35.2) --------------------------------------- */

.kb-featured {
  padding: 56px var(--gutter) 48px;
  border-bottom: 1px solid var(--line-strong);
}

.kb-featured__inner {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 48px;
  align-items: center;
  max-width: var(--content-max);
  margin: 0 auto;
}

/* Without a photograph the entry keeps the second column and takes the whole
   width; an empty 16:10 box would be the placeholder 35.1 rules out. */
.kb-featured__inner:has(> .kb-featured__body:only-child) {
  grid-template-columns: 1fr;
}

.kb-featured__frame {
  display: block;
  overflow: hidden;
  border-radius: var(--radius-card);
}

/* height: auto because the img carries width and height attributes, and the
   height attribute alone would otherwise outrank the ratio below. */
.kb-featured__photo {
  display: block;
  width: 100%;
  height: auto;
}

/* A photograph fills the 16:10 box. A board with text (--full) is shown whole
   instead, in its own proportions, which until the file arrives come from the
   width and height on the img. */
.kb-featured__photo:not(.kb-featured__photo--full) {
  aspect-ratio: 16 / 10;
  object-fit: cover;
}

.kb-featured__body {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.kb-featured__labels {
  display: flex;
  align-items: center;
  gap: 12px;
}

.kb-featured__pill {
  padding: 4px 11px;
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.4;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--kb-ink);
  background: var(--kb-ground);
  border-radius: var(--radius-pill);
}

.kb-featured__newest {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.kb-featured__title {
  font-family: var(--font-serif);
  font-size: 34px;
  line-height: 1.2;
  font-weight: 500;
  letter-spacing: -0.01em;
  text-wrap: pretty;
}

.kb-featured__title a {
  color: var(--text);
}

.kb-featured__title a:hover {
  color: var(--accent);
}

.kb-featured__lead {
  font-size: 17px;
  line-height: 1.7;
  color: var(--muted);
}

.kb-featured__byline {
  padding-top: 4px;
  font-size: 15px;
  color: var(--muted);
}

.kb-featured__byline a {
  color: var(--accent);
}

/* --- 9a, the grid (35.2) -------------------------------------------------- */

.kb-grid {
  padding: 0 var(--gutter) 72px;
}

.kb-grid__inner {
  display: flex;
  flex-direction: column;
  gap: 26px;
  max-width: var(--content-max);
  margin: 0 auto;
  padding-top: 48px;
}

.kb-grid__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding-bottom: 12px;
  border-bottom: 2px solid var(--accent);
}

.kb-grid__heading {
  font-family: var(--font-serif);
  font-size: 28px;
  line-height: 1.2;
  font-weight: 500;
}

/* A label and not a control: the frame draws no field, and the list has one
   order (35.2). */
.kb-grid__order {
  font-size: 14px;
  color: var(--muted);
}

.kb-grid__cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
}

.kb-grid__more {
  display: flex;
  justify-content: center;
  padding-top: 12px;
}

/* One button, never numbers (note 05 beside the frame). It carries the shape
   of a secondary button of section 1 and its own padding from 35.2. */
.kb-more {
  padding: 14px 30px;
  font-size: 16px;
  color: var(--accent);
  font-weight: 500;
  background: var(--tint);
  border-radius: var(--radius-button);
}

.kb-more:hover {
  color: var(--accent);
  background: var(--tint-hover);
}

/* --- The grid card, 9a and the band on 6a (35.2, 35.6) -------------------- */

.article-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

/* Section 4: hover firms up the border and lifts the card (--shadow-raised,
   panel mockup). No scaling, no motion. */
.article-card:hover {
  border-color: var(--line-strong);
  box-shadow: var(--shadow-raised);
}

/* height: auto for the reason given at .kb-featured__photo. Every picture of a
   card, the photograph or the card's own thumbnail, fills one 16:9 box, so the
   text of every card in a row starts at the same height. */
.article-card__photo {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-bottom: 1px solid var(--line);
}

/* The body takes the height of its own text: no minimum, the date 16 px under
   the title (the 10 px gap and the 6 px of .article-card__meta). */
.article-card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 10px;
  padding: 20px 22px 22px;
}

.article-card__kicker {
  align-self: flex-start;
  padding: 4px 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.4;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--kb-ink);
  background: var(--kb-ground);
  border-radius: var(--radius-pill);
}

.article-card__title {
  font-family: var(--font-serif);
  font-size: 20px;
  line-height: 1.35;
  font-weight: 500;
  color: var(--text);
  text-wrap: pretty;
}

.article-card__meta {
  margin-top: 6px;
  font-size: 13px;
  color: var(--muted);
}

/* --- 9b, the entry (35.3) ------------------------------------------------- */

.kb-entry {
  padding: 52px var(--gutter) 72px;
}

.kb-entry__inner {
  display: grid;
  grid-template-columns: 1fr 300px;
  gap: 80px;
  align-items: start;
  max-width: var(--content-max);
  margin: 0 auto;
}

/* min-width: 0 because a grid item's automatic minimum is its content: a
   table wider than a phone would otherwise widen the column, and with it the
   whole page, instead of scrolling inside its own box. */
.kb-entry__main {
  display: flex;
  flex-direction: column;
  gap: 32px;
  min-width: 0;
  max-width: 720px;
}

.kb-entry__pill {
  align-self: flex-start;
  padding: 5px 12px;
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.4;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--kb-ink);
  background: var(--kb-ground);
  border-radius: var(--radius-pill);
}

.kb-entry__title {
  font-family: var(--font-serif);
  font-size: 44px;
  line-height: 1.14;
  font-weight: 500;
  letter-spacing: -0.015em;
  text-wrap: pretty;
}

.kb-entry__lead {
  font-size: 20px;
  line-height: 1.6;
  color: var(--muted);
}

.kb-entry__byline {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  overflow: hidden;
  padding: 14px 0;
  font-size: 15px;
  color: var(--muted);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

/* The dot sits in the gap before each item. An item that wraps to the start
   of a line pushes its dot past the left edge, where overflow clips it, so no
   line ends or begins with a stray separator. */
.kb-entry__byline > span {
  position: relative;
}

.kb-entry__byline > span + span::before {
  content: "·";
  content: "·" / "";
  position: absolute;
  left: -13px;
}

.kb-entry__date,
.kb-entry__minutes {
  font-family: var(--font-mono);
  font-size: 13px;
}

/* The photo and its caption, spaced like a figure in the text. */
.kb-entry__hero {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.kb-entry__hero picture {
  display: block;
}

.kb-entry__hero figcaption {
  font-size: 15px;
  line-height: 1.6;
  color: var(--muted);
}

/* height: auto because the img carries width and height attributes, and the
   height attribute alone would otherwise outrank the ratio below. */
.kb-entry__photo {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-card);
}

/* A board with text is shown whole, as at .kb-featured__photo, in the
   proportions of the layout the picture element chose, desktop or phone. */
.kb-entry__photo:not(.kb-entry__photo--full) {
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

/* The sources, folded under the questions: one short line in the size of
   the captions, with a small arrow after it, rather than a bar across the
   column (the client's remark of 21.10.2026, "niech będzie subtelne ale
   wystarczająco wyraźne"). The line stays 44px high, so it is still easy to
   tap. */
.kb-sources__summary {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  cursor: pointer;
  list-style: none;
  color: var(--text);
}

.kb-sources__summary::-webkit-details-marker {
  display: none;
}

.kb-sources__summary::after {
  content: "";
  flex: 0 0 6px;
  width: 6px;
  height: 6px;
  margin-top: -3px;
  border-right: 1.5px solid var(--muted);
  border-bottom: 1.5px solid var(--muted);
  transform: rotate(45deg);
}

.kb-sources[open] > .kb-sources__summary::after {
  margin-top: 3px;
  transform: rotate(-135deg);
}

.kb-sources__summary:hover .kb-sources__title {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.kb-sources__summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.kb-sources__title {
  margin: 0;
  font: 500 15px/1.5 var(--font-sans);
  color: inherit;
}

.kb-sources__list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 4px 0 0;
  padding-left: 20px;
}

.kb-sources__item {
  font-size: 14px;
  line-height: 1.6;
  color: var(--muted);
  overflow-wrap: anywhere;
}

/* The reviewer's line under the sources, in the type of the text and not the
   mono of the dates at the top (Graphic Designer, 22.10.2026). The name is a link without the link's colour and underline until
   the pointer or the focus reaches it (the client's remark of 21.10.2026). */
.kb-entry__reviewer {
  margin-top: -20px;
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 20px;
  color: var(--muted);
}

.kb-entry__reviewer a {
  color: inherit;
  text-decoration: none;
}

.kb-entry__reviewer a:hover,
.kb-entry__reviewer a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* The disclaimer at the end of the text, not in the page footer (note 07). */
.kb-entry__disclaimer {
  margin-top: -12px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--muted);
}

/* --- 9b, the running text ------------------------------------------------- */

/* Everything below styles markup the editor writes, filtered through
   apps/content/body.py. The allow-list there and the selectors here are two
   halves of one contract, and docs/CONVENTIONS.md ("Treść wpisu") is where it
   is written down for the person typing. */
/* A size container, so the tables in the text follow the width of the text
   column rather than of the window: beside the rail the column is narrower
   than on a phone held sideways. */
.kb-entry__body {
  container: entry-body / inline-size;
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.kb-entry__body h2 {
  margin: 8px 0 0;
  font-family: var(--font-serif);
  font-size: 28px;
  line-height: 1.25;
  font-weight: 500;
  /* The rail links here; without this the heading lands under the sticky
     header of section 6. */
  scroll-margin-top: 96px;
}

.kb-entry__body h3 {
  font-family: var(--font-serif);
  font-size: 21px;
  line-height: 1.3;
  font-weight: 500;
}

.kb-entry__body p,
.kb-entry__body li {
  font-size: 18px;
  line-height: 1.75;
  color: var(--text);
}

.kb-entry__body ul,
.kb-entry__body ol {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-left: 22px;
  list-style: revert;
}

.kb-entry__body hr {
  height: 1px;
  background: var(--line);
  border: 0;
}

/* The WHO classification, and any other table of a colour, a label and a
   value. Table layout is switched off so that the row can be the grid 35.3
   describes; the swatch is the row's own ::before, so the editor types a class
   and not an empty cell. */
.article-table,
.article-table tbody {
  display: block;
  width: 100%;
}

.article-table tr {
  display: grid;
  grid-template-columns: 14px 1fr 130px;
  gap: 16px;
  align-items: center;
  padding: 13px 0;
  border-top: 1px solid var(--line);
}

.article-table tr:first-child {
  border-top: 1px solid var(--line-strong);
}

.article-table tr:last-child {
  border-bottom: 1px solid var(--line-strong);
}

.article-table tr::before {
  content: "";
  width: 14px;
  height: 14px;
  background: var(--swatch, transparent);
  border-radius: var(--radius-badge);
}

.article-table .s1 {
  --swatch: var(--s1);
}

.article-table .s2 {
  --swatch: var(--s2);
}

.article-table .s3 {
  --swatch: var(--s3);
}

.article-table .s4 {
  --swatch: var(--s4);
}

.article-table .s5 {
  --swatch: var(--s5);
}

.article-table .s6 {
  --swatch: var(--s6);
}

.article-table th,
.article-table td {
  font-size: 17px;
  font-weight: 400;
  color: var(--text);
  text-align: left;
}

.article-table td:last-child {
  font-family: var(--font-mono);
  font-size: 15px;
  color: var(--muted);
  text-align: right;
}

/* Any other table in the text: columns of words, such as substances, brand
   names and how each is taken. A card of its own, in the page's colours: the
   header row on the band blue, rows separated by hairlines, every other row
   on the page background so the eye keeps its line across, and the first
   column, which names the row, in semibold. */
.kb-entry__body table:not(.article-table) {
  width: 100%;
  border: 1px solid var(--line-strong);
  border-collapse: separate;
  border-spacing: 0;
  border-radius: 12px;
  overflow: hidden;
  background: var(--surface);
}

/* The box body.py puts around the table: a table wider than the text, its
   columns no narrower than their longest words, scrolls inside it. */
.kb-entry__table {
  overflow-x: auto;
}

/* break-word and not anywhere: anywhere also lowers the narrowest a column
   may get to one letter, so the automatic layout handed the width to the
   longest text and cut short words in the other columns ("tygod / ni",
   "place / bo"). break-word keeps every word whole when the column can hold
   it. A table that still cannot fit scrolls in .kb-entry__table. With whole
   words five columns need about 520px, and the padding of 14px on each side
   keeps them inside the narrowest column that still draws a table (561px,
   the cards take over below). */
.kb-entry__body table:not(.article-table) th,
.kb-entry__body table:not(.article-table) td {
  overflow-wrap: break-word;
  padding: 13px 14px;
  font-size: 16px;
  line-height: 1.5;
  color: var(--text);
  text-align: left;
  vertical-align: top;
  background: var(--surface);
}

.kb-entry__body table:not(.article-table) th {
  padding-top: 12px;
  padding-bottom: 12px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--deep);
  background: var(--band);
  border-bottom: 1px solid var(--line-strong);
}

/* The first cell of a data row is the row's header (th scope="row"): it
   keeps the look of the first column, not of the header row. */
.kb-entry__body table:not(.article-table) tbody th {
  padding-top: 13px;
  padding-bottom: 13px;
  font-size: 16px;
  letter-spacing: normal;
  color: var(--text);
  background: var(--surface);
  border-bottom: 0;
}

.kb-entry__body table:not(.article-table) td,
.kb-entry__body table:not(.article-table) tbody th {
  border-top: 1px solid var(--line);
}

.kb-entry__body table:not(.article-table) tbody tr:first-child td,
.kb-entry__body table:not(.article-table) tbody tr:first-child th {
  border-top: 0;
}

.kb-entry__body table:not(.article-table) tbody tr:nth-child(even) td,
.kb-entry__body table:not(.article-table) tbody tr:nth-child(even) th {
  background: var(--bg);
}

.kb-entry__body table:not(.article-table) td:first-child {
  font-weight: 600;
}

/* In a narrow column (a phone, and a desktop window just wide enough for the
   rail beside the text) three columns of words do not fit side by side, so
   every row becomes a card: the first cell, which names the row, as its title, and each
   other value under the name of its column, which apps/content/body.py copies
   from the header into data-label. The header row itself is then redundant on
   screen and stays only for screen readers. */
@container entry-body (max-width: 560px) {
  .kb-entry__body table:not(.article-table),
  .kb-entry__body table:not(.article-table) tbody,
  .kb-entry__body table:not(.article-table) tr,
  .kb-entry__body table:not(.article-table) th,
  .kb-entry__body table:not(.article-table) td {
    display: block;
  }

  .kb-entry__body table:not(.article-table) {
    border: 0;
    border-radius: 0;
    background: none;
  }

  .kb-entry__body table:not(.article-table) thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .kb-entry__body table:not(.article-table) tr {
    overflow: hidden;
    border: 1px solid var(--line-strong);
    border-radius: 12px;
    background: var(--surface);
  }

  .kb-entry__body table:not(.article-table) tr + tr {
    margin-top: 12px;
  }

  .kb-entry__body table:not(.article-table) td,
  .kb-entry__body table:not(.article-table) tbody th,
  .kb-entry__body table:not(.article-table) tbody tr:first-child td,
  .kb-entry__body table:not(.article-table) tbody tr:first-child th,
  .kb-entry__body table:not(.article-table) tbody tr:nth-child(even) td,
  .kb-entry__body table:not(.article-table) tbody tr:nth-child(even) th {
    padding: 10px 16px;
    border-top: 1px solid var(--line);
    font-size: 16px;
    background: var(--surface);
  }

  /* As specific as the striping rule above, which it has to beat. */
  .kb-entry__body table:not(.article-table) tbody tr td:first-child,
  .kb-entry__body table:not(.article-table) tbody tr th:first-child {
    padding-top: 12px;
    padding-bottom: 12px;
    border-top: 0;
    font-size: 17px;
    color: var(--deep);
    background: var(--band);
  }

  .kb-entry__body table:not(.article-table) td[data-label]:not(:first-child)::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 2px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--muted);
  }
}

/* One highlight per entry, where the text says "go and see a doctor"
   (note 04). A frame with an accent edge rather than another card. */
.article-callout {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 26px;
  background: var(--surface);
  border: 1px solid var(--accent);
  border-left: 5px solid var(--accent);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

/* Both selectors carry .kb-entry__body, and the label is excluded from the
   paragraph rule rather than trying to out-weigh it: inside the body every <p>
   is already matched by `.kb-entry__body p`, so a bare `.article-callout__label`
   loses on specificity and the mono label comes out at 18 px. */
.kb-entry__body .article-callout p:not(.article-callout__label) {
  font-size: 17px;
  line-height: 1.7;
}

.kb-entry__body .article-callout__label {
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}

/* A picture between two blocks of the text (35.3, "Grafika w treści"). The
   markup is built by apps/content/body.py, never typed. The picture takes the
   width of the column and keeps its proportions; the width and height on the
   img are what hold its place before the file arrives. The caption is set
   like the disclaimer. */
.article-figure {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.article-figure picture {
  display: block;
}

.article-figure img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-card);
}

.article-figure figcaption {
  font-size: 15px;
  line-height: 1.6;
  color: var(--muted);
}

/* --- 9b, the FAQ block (35.3) --------------------------------------------- */

/* One heading, in the size of the headings of the text above it: the
   "FAQ" kicker over "Częste pytania" said the same thing twice (the
   client's remark of 21.10.2026). */
.kb-faq {
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding-top: 20px;
}

.kb-faq__heading {
  font-family: var(--font-serif);
  font-size: 28px;
  line-height: 1.25;
  font-weight: 500;
  scroll-margin-top: 96px;
}

/* Under an entry the questions stand flush with the heading, without the
   accent dots of the home page; the rules and the plus signs stay (Graphic
   Designer, 22.10.2026). Two classes outweigh the phone rules of the list. */
.kb-faq .faq-list__bullet {
  display: none;
}

.kb-faq .faq-list__summary {
  grid-template-columns: minmax(0, 1fr) 14px;
}

.kb-faq .faq-list__answer {
  padding-left: 0;
}

@media (max-width: 900px) {
  .kb-faq {
    padding-top: 16px;
  }
}

/* --- 9b, related entries (35.3) ------------------------------------------- */

.kb-related {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding-top: 12px;
  border-top: 1px solid var(--line-strong);
}

.kb-related__heading {
  font-family: var(--font-serif);
  font-size: 24px;
  line-height: 1.2;
  font-weight: 500;
}

.kb-related__cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}

.kb-related__card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 20px 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-top: 5px solid var(--kb-ink);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.kb-related__card:hover {
  border-color: var(--line-strong);
  border-top-color: var(--kb-ink);
  box-shadow: var(--shadow-raised);
}

.kb-related__meta {
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.kb-related__title {
  font-family: var(--font-serif);
  font-size: 19px;
  line-height: 1.35;
  font-weight: 500;
  color: var(--text);
  text-wrap: pretty;
}

/* --- 9b, the rail (35.4) --------------------------------------------------- */

.kb-aside {
  position: sticky;
  top: 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Slot one. --kb-ink is the accent unless a category set it, which is the only
   difference between the calculator tile and the catalogue tile — the rule
   picking between them is on the model (apps/content/tiles.py). */
.kb-tile {
  --kb-ink: var(--accent);
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 22px;
  background: var(--surface);
  border: 1px solid var(--kb-ink);
  border-top: 5px solid var(--kb-ink);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.kb-tile__kicker {
  color: var(--kb-ink);
}

.kb-tile__title {
  font-family: var(--font-serif);
  font-size: 22px;
  line-height: 1.25;
  font-weight: 500;
}

.kb-tile__text {
  font-size: 15px;
  line-height: 1.55;
  color: var(--muted);
}

.kb-tile__button {
  padding: 13px 20px;
  font-size: 15px;
  color: var(--on-accent);
  text-align: center;
  background: var(--kb-ink);
  border-radius: var(--radius-button);
}

.kb-tile__button:hover {
  color: var(--on-accent);
  filter: brightness(0.92);
}

/* The same segments and the same weights as the calculator page — the flex
   values are in section 12 on .calc-scale--bmi and .calc-scale--whtr. Only the
   two dimensions the frame changes are restated: the bar is 8 px here, and it
   carries no ticks and no marker. */
.kb-tile__scale .calc-scale__bar {
  height: 8px;
  margin-top: 0;
}

.kb-tile__scale .calc-scale__segment:first-child {
  border-radius: 4px 0 0 4px;
}

.kb-tile__scale .calc-scale__segment:last-child {
  border-radius: 0 4px 4px 0;
}

/* Slot two. No active item: working out which heading the reader is standing
   at needs a script, and a static marker would point at the first one for the
   whole entry (35.4). */
.kb-toc {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 22px;
  background: var(--band);
  border-radius: var(--radius-card);
}

.kb-toc__kicker {
  color: var(--accent);
}

.kb-toc__list {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding-left: 14px;
  border-left: 2px solid var(--accent);
  list-style: none;
}

.kb-toc__link {
  font-size: 14px;
  line-height: 1.4;
  color: var(--muted);
}

.kb-toc__link:hover,
.kb-toc__link:focus-visible {
  color: var(--text);
}

/* Slot three, and it is absent when slot one is already the catalogue. */
.kb-exit {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.kb-exit__text {
  font-size: 16px;
  line-height: 1.5;
  color: var(--text);
}

.kb-exit__link {
  font-size: 15px;
  color: var(--accent);
}

/* --- 10f, the author (35.5) ------------------------------------------------ */

.kb-author {
  padding: 56px var(--gutter) 64px;
}

.kb-author__inner {
  display: grid;
  grid-template-columns: minmax(0, 680px) 240px;
  column-gap: 40px;
  align-items: start;
  max-width: var(--content-max);
  margin: 0 auto;
}

/* A square beside the heading and the biography; on a phone it moves above
   the heading (max-width 767px below). The same box holds the placeholder
   and the photograph, so uploading one moves nothing. */
.kb-author__photo {
  display: block;
  grid-column: 2;
  grid-row: 1;
  width: 240px;
  height: 240px;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 8px;
}

.kb-author__body {
  grid-column: 1;
  grid-row: 1;
  display: flex;
  flex-direction: column;
  gap: 22px;
  max-width: 680px;
}

.kb-author__head {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.kb-author__kicker {
  color: var(--accent);
}

.kb-author__name {
  font-family: var(--font-serif);
  font-size: 42px;
  line-height: 1.14;
  font-weight: 500;
  letter-spacing: -0.015em;
  text-wrap: pretty;
}

.kb-author__title {
  font-size: 17px;
  color: var(--muted);
}

.kb-author__para {
  font-size: 18px;
  line-height: 1.75;
  color: var(--muted);
}

/* The first block carries the full contrast, the rest are quiet — the frame's
   own two colours, out of one column (35.5). */
.kb-author__para--lead {
  color: var(--text);
}

/* The reviewer's facts: a label and its value per row, the list idiom of
   the grid's "W skrócie" box without its frame. */
.kb-author__facts {
  margin: 0;
  display: flex;
  flex-direction: column;
}

.kb-author__fact {
  display: flex;
  gap: 16px;
  padding: 12px 0;
  border-top: 1px solid var(--line);
  font-size: 16px;
}

.kb-author__fact:last-child {
  border-bottom: 1px solid var(--line);
}

.kb-author__fact dt {
  flex: 0 0 160px;
  color: var(--muted);
}

.kb-author__fact dd {
  margin: 0;
  color: var(--text);
}

.kb-author__linkedin {
  align-self: flex-start;
  margin-top: 4px;
  padding: 12px 18px;
  font-size: 16px;
  color: var(--accent);
  font-weight: 500;
  background: var(--tint);
  border-radius: var(--radius-button);
}

.kb-author__linkedin:hover {
  color: var(--accent);
  background: var(--tint-hover);
}

@media (max-width: 767px) {
  .kb-author__inner {
    grid-template-columns: 1fr;
  }

  .kb-author__photo {
    grid-column: 1;
    width: 144px;
    height: 144px;
    margin-bottom: 24px;
  }

  .kb-author__body {
    grid-row: 2;
  }
}

/* --- The band "Z bazy wiedzy" on 6a (35.6) --------------------------------- */

.home-knowledge {
  padding: 64px var(--gutter) 78px;
  background: var(--band);
}

.home-knowledge__inner {
  display: flex;
  flex-direction: column;
  gap: 26px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.home-knowledge__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding-bottom: 12px;
  border-bottom: 2px solid var(--accent);
}

.home-knowledge__heading {
  font-family: var(--font-serif);
  font-size: 28px;
  line-height: 1.2;
  font-weight: 500;
}

.home-knowledge__all {
  font-size: 14px;
  color: var(--accent);
}

.home-knowledge__cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

/* In the row of three, on 9a and in this band, a card keeps to the top of its
   row instead of stretching to a taller neighbour, which left a white field
   between its title and its date, and the body has 24 px of padding all round.
   In one column the padding stays as it is at .article-card__body. */
@media (min-width: 901px) {
  .article-card {
    align-self: start;
  }

  .article-card__body {
    padding: 24px;
  }
}

/* --- The closing band of 6a (18, "Rytm teł") ------------------------------- */

/* The band the frame does not draw, added because the page had no repeat of
   its call to action. --surface rather than --deep: the footer is navy and
   two navies touching is what note 09 argues against. */
.home-close {
  padding: 66px var(--gutter) 72px;
  background: var(--surface);
  border-top: 1px solid var(--line);
}

.home-close__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 48px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.home-close__text {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 620px;
}

.home-close__title {
  font-family: var(--font-serif);
  font-size: 34px;
  line-height: 1.18;
  letter-spacing: -0.01em;
}

/* --- The two links that waited for an article ------------------------------ */

/* Out of the dark band of 6a. --s1 and not --accent: the accent is unreadable
   on --deep, and the footer scale is what the frame uses there. */
.home-origin__link {
  align-self: flex-start;
  font-size: 16px;
  color: var(--s1);
}

.home-origin__link:hover {
  color: var(--on-deep);
}

/* Out of the caveat strip of 1i. flex: none, so the sentence beside it wraps
   and the link does not. */
.calculator__caveat-link {
  flex: none;
  font-size: 15px;
  color: var(--accent);
}

/* --- Telefon (section 15) --------------------------------------------------- */

@media (max-width: 900px) {
  .kb-head {
    padding: 40px var(--gutter) 32px;
  }

  .kb-head__title {
    font-size: 32px;
  }

  /* The row of six pills scrolls sideways rather than stacking into three
     lines of chips. `min-width: 0` is what makes it scroll instead of pushing:
     a flex item defaults to `min-width: auto`, so without it the row refuses to
     be narrower than its contents and the whole page grows a horizontal
     scrollbar. */
  .article-filters {
    flex-wrap: nowrap;
    min-width: 0;
    overflow-x: auto;
  }

  .kb-featured {
    padding: 40px var(--gutter) 36px;
  }

  .kb-featured__inner,
  .kb-grid__cards,
  .kb-related__cards,
  .home-knowledge__cards {
    grid-template-columns: 1fr;
    gap: 20px;
  }

  /* The closing band stacks: a button beside 620px of text has nowhere to sit
     at 390px, and the call must stay the last thing before the footer. */
  .home-close {
    padding: 44px var(--gutter) 48px;
  }

  .home-close__inner {
    flex-direction: column;
    align-items: stretch;
    gap: 24px;
  }

  .home-close__title {
    font-size: 26px;
  }

  .kb-featured__title {
    font-size: 26px;
  }

  /* The rail goes under the text and stops following it: sticky in a single
     column would pin the tiles to the bottom of the entry. */
  .kb-entry__inner {
    grid-template-columns: 1fr;
    gap: 40px;
  }

  .kb-aside {
    position: static;
  }

  .kb-entry__title {
    font-size: 30px;
  }

  .kb-entry__lead {
    font-size: 18px;
  }

  .kb-author__name {
    font-size: 30px;
  }

  /* Three columns of a classification cannot narrow any further, so the table
     scrolls sideways as one piece — the treatment section 25 gives the invoice
     table, and for the same reason. */
  .article-table,
  .article-table tbody {
    overflow-x: auto;
  }

  .article-table tr {
    min-width: 460px;
  }
}


/* ==========================================================================
   27. Siatka SEO — frames 12a and 12b, and the voivodeship page
   Every value comes from DESIGN_SPEC.md sections 36 and 37, written from the
   frames before this CSS — the mode of sections 18, 19, 26. The compact
   specialist card lives here with its page: 12a is the only frame that draws
   it (§3). The voivodeship page has no frame; §36.8 lists its minimal
   composition. The city index 12b closes the block.
   ========================================================================== */

/* --- Okruszki (36.1) -------------------------------------------------------- */

.seo-crumbs {
  padding: 20px var(--gutter);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.seo-crumbs__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  max-width: var(--content-max);
  margin: 0 auto;
  font-size: 14px;
  color: var(--muted);
}

.seo-crumbs__link {
  color: var(--muted);
  text-decoration: none;
}

.seo-crumbs__current {
  color: var(--text);
}

/* --- Hero (36.2) ------------------------------------------------------------ */

.seo-hero {
  padding: 52px var(--gutter) 44px;
  background: var(--hero);
  border-bottom: 1px solid var(--line-strong);
}

.seo-hero__inner {
  display: grid;
  grid-template-columns: 1fr 340px;
  gap: 64px;
  align-items: start;
  max-width: var(--content-max);
  margin: 0 auto;
}

.seo-hero__lead {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.seo-hero__eyebrow {
  display: flex;
  align-items: center;
  gap: 9px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.seo-hero__eyebrow::before {
  content: "";
  width: 22px;
  height: 4px;
  background: currentColor;
}

/* The label wears the type's colour — the same system as the knowledge base:
   the patient knows the group by the colour (note 02, DECISIONS.md). */
.seo-hero__eyebrow--c1 { color: var(--c1); }
.seo-hero__eyebrow--c2 { color: var(--c2); }
.seo-hero__eyebrow--c3 { color: var(--c3); }
.seo-hero__eyebrow--c4 { color: var(--c4); }
.seo-hero__eyebrow--c5 { color: var(--c5); }

/* 44 px, not the 52 of the page-H1 scale: the heading shares the hero row
   with the 340 px box, the reason 1e's name is 38 (§3, §36.2). */
.seo-hero__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 44px;
  line-height: 1.12;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--text);
  text-wrap: pretty;
}

.seo-hero__intro {
  margin: 0;
  max-width: 640px;
  font-size: 17px;
  line-height: 1.7;
  color: var(--text);
  text-wrap: pretty;
}

/* --- Ramka „w skrócie" (36.3) ------------------------------------------------ */

.seo-summary {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.seo-summary__label {
  color: var(--accent);
}

.seo-summary__rows {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
}

.seo-summary__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-size: 15px;
  color: var(--muted);
}

.seo-summary__value {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 17px;
  color: var(--text);
}

/* --- Sekcja kart (36.4) ------------------------------------------------------ */

.seo-cards {
  padding: 44px var(--gutter) 56px;
}

.seo-cards__inner {
  display: flex;
  flex-direction: column;
  gap: 22px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.seo-cards__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding-bottom: 12px;
  border-bottom: 2px solid var(--accent);
}

.seo-cards__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 26px;
  font-weight: 500;
  color: var(--text);
}

.seo-cards__all {
  font-size: 14px;
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
}

/* The day field under the heading and "Wszystkie filtry →", a row of its own
   at every width: the label over the field, "Szukaj" beside the field, the
   two shortcuts under it. */
.seo-day {
  display: grid;
  flex-basis: 100%;
  grid-template-columns: minmax(0, 220px) auto;
  column-gap: 12px;
  justify-content: start;
}

.seo-day .filter-day__label {
  grid-column: 1 / -1;
  margin-bottom: 6px;
}

.seo-day .btn {
  grid-column: 2;
  grid-row: 2;
  align-self: start;
}

.seo-cards__list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.seo-cards__footer {
  margin: 0;
  padding-top: 6px;
  text-align: center;
}

.seo-cards__more {
  display: inline-flex;
  padding: 14px 30px;
  font-size: 16px;
  color: var(--accent);
  font-weight: 500;
  background: var(--tint);
  border-radius: var(--radius-button);
  text-decoration: none;
}

.seo-cards__more:hover {
  color: var(--accent);
  background: var(--tint-hover);
}

/* --- Karta zwarta (§3 „kompakt", 36.4) --------------------------------------- */

.compact-card {
  display: flex;
  align-items: flex-start;
  gap: 20px;
  padding: 22px 24px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.compact-card__photo {
  flex: none;
  width: 68px;
  height: 68px;
  border-radius: 50%;
  object-fit: cover;
}

/* No photo means initials on --tint — never a silhouette (§3). */
.compact-card__photo--placeholder {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  color: var(--accent);
  background: var(--tint);
}

.compact-card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  gap: 8px;
}

.compact-card__name {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 21px;
  font-weight: 500;
  overflow-wrap: anywhere;
}

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

.compact-card__meta {
  margin: 0;
  font-size: 15px;
  color: var(--muted);
}

.compact-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.compact-card__tag {
  padding: 5px 10px;
  font-size: 13px;
  color: var(--text);
  background: var(--tint);
  border-radius: 4px;
  white-space: nowrap;
}

.compact-card__aside {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  flex: none;
  gap: 10px;
}

.compact-card__price {
  font-family: var(--font-mono);
  font-size: 16px;
  color: var(--text);
}

/* --- FAQ (36.5) -------------------------------------------------------------- */

.seo-faq {
  padding: 56px var(--gutter);
  background: var(--surface);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.seo-faq__inner {
  display: grid;
  grid-template-columns: 340px 1fr;
  gap: 72px;
  align-items: start;
  max-width: var(--content-max);
  margin: 0 auto;
}

.seo-faq__rail {
  display: flex;
  flex-direction: column;
  gap: 14px;
  position: sticky;
  top: 24px;
}

.seo-faq__label {
  color: var(--accent);
  letter-spacing: 0.14em;
}

.seo-faq__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 30px;
  line-height: 1.18;
  font-weight: 500;
  color: var(--text);
  text-wrap: pretty;
}

.seo-faq__list {
  display: flex;
  flex-direction: column;
  max-width: 700px;
}

.seo-faq__item {
  display: flex;
  gap: 18px;
  padding: 22px 0;
  border-top: 1px solid var(--line);
}

.seo-faq__item:first-child {
  border-top-color: var(--line-strong);
}

.seo-faq__item:last-of-type {
  border-bottom: 1px solid var(--line-strong);
}

.seo-faq__bullet {
  flex: none;
  width: 8px;
  height: 8px;
  margin-top: 11px;
  border-radius: 50%;
  background: var(--accent);
}

/* 10 px, not the 16 of 9b's FAQ — the frame's own value (§36.9). */
.seo-faq__text {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.seo-faq__question {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 20px;
  font-weight: 500;
  color: var(--text);
}

.seo-faq__answer {
  margin: 0;
  font-size: 17px;
  line-height: 1.7;
  color: var(--text);
  text-wrap: pretty;
}

.seo-faq__note {
  margin: 14px 0 0;
  font-size: 14px;
  color: var(--muted);
}

/* --- Pas linków krzyżowych (36.6) --------------------------------------------- */

.seo-links {
  padding: 52px var(--gutter) 64px;
  background: var(--band);
}

.seo-links__inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.seo-links__column {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.seo-links__label {
  color: var(--accent);
}

.seo-links__row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* The one pill shape section 5 does not have: accent on --surface with an
   accent border — the frame draws it once, as the exit to 12b. */
.seo-links__all {
  display: inline-flex;
  align-items: center;
  padding: 8px 15px;
  font-size: 15px;
  line-height: 1.2;
  color: var(--accent);
  background: var(--surface);
  border: 1px solid var(--accent);
  border-radius: var(--radius-pill);
  text-decoration: none;
  white-space: nowrap;
}

.seo-links__all:hover {
  color: var(--accent);
}

/* --- Strona województwa (36.8) ------------------------------------------------ */

.seo-region {
  padding: 52px var(--gutter) 64px;
}

.seo-region__inner {
  display: flex;
  flex-direction: column;
  gap: 18px;
  max-width: 760px;
  margin: 0 auto;
}

.seo-region__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 34px;
  line-height: 1.18;
  font-weight: 500;
  color: var(--text);
  text-wrap: pretty;
}

/* The computed lead under the heading, in the type index's lead type. */
.seo-region__lead {
  margin: 0;
  font-size: 17px;
  line-height: 1.7;
  color: var(--muted);
  text-wrap: pretty;
}

.seo-region__list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
}

.seo-region__item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 0;
  border-top: 1px solid var(--line);
}

.seo-region__item:last-child {
  border-bottom: 1px solid var(--line);
}

/* The same treatment the city index gives its rows, so the two SEO lists of
   cities do not read as two different components. */
.seo-region__link {
  color: var(--text);
  text-decoration: none;
}

.seo-region__link:hover {
  color: var(--accent);
}

.seo-region__count {
  font-family: var(--font-mono);
  font-size: 15px;
  color: var(--muted);
  white-space: nowrap;
}

/* --- Indeks miast (12b, 37) ---------------------------------------------------- */

.seo-index-hero {
  padding: 48px var(--gutter) 40px;
  background: var(--band);
  border-bottom: 1px solid var(--line-strong);
}

.seo-index-hero__inner {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: var(--content-max);
  margin: 0 auto;
}

/* 0.14em, not the 0.12 of .t-overline: the frame's own value (37.1). */
.seo-index-hero__eyebrow {
  letter-spacing: 0.14em;
  color: var(--accent);
}

/* 40 px, between 12a's 44 and the 52 of the README scale: the band is one
   column here, but the page is an index rather than a landing (37.1). */
.seo-index-hero__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 40px;
  line-height: 1.15;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--text);
  text-wrap: pretty;
}

.seo-index-hero__lead {
  margin: 0;
  max-width: 640px;
  font-size: 17px;
  line-height: 1.7;
  color: var(--muted);
  text-wrap: pretty;
}

/* Both section heads. The major one carries the 2 px accent rule, the second
   a 1 px line — the cards outrank the list (37.2, 37.3). */
.seo-index__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line-strong);
}

.seo-index__head--major {
  border-bottom: 2px solid var(--accent);
}

.seo-index__heading {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 26px;
  font-weight: 500;
  color: var(--text);
}

.seo-index__heading--small {
  font-size: 22px;
}

.seo-index__note {
  font-size: 14px;
  color: var(--muted);
}

.seo-index-cards {
  padding: 48px var(--gutter) 40px;
}

.seo-index-cards__inner,
.seo-index-rest__inner {
  display: flex;
  flex-direction: column;
  gap: 24px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.seo-index-cards__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}

/* The content card of section 4, plus the 5 px stripe the frame gives each
   card by position. The colour cannot mean a specialist type here — this
   screen has none — so it reads as rank (37.7). */
.seo-index-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-top: 5px solid var(--line-strong);
  border-radius: 8px;
  text-decoration: none;
}

/* Section 4: hover changes the border and nothing else — no lift, no shadow,
   no scaling. The top edge keeps its own colour, so only the other three
   move. */
.seo-index-card:hover {
  border-right-color: var(--line-strong);
  border-bottom-color: var(--line-strong);
  border-left-color: var(--line-strong);
}

.seo-index-card--c1 {
  border-top-color: var(--c1);
}

.seo-index-card--c2 {
  border-top-color: var(--c2);
}

.seo-index-card--c3 {
  border-top-color: var(--c3);
}

.seo-index-card--c4 {
  border-top-color: var(--c4);
}

/* The fifth colour (47.2). 12b never needed it — it draws four cards — but
   the city page draws one card per specialist type, and there the stripe means
   the type, which is what DECISIONS.md says a colour always means. */
.seo-index-card--c5 {
  border-top-color: var(--c5);
}

/* Five type cards instead of four city cards (47.2). The phone rule below
   still applies: it matches the base class, sits later in the sheet and has
   the same specificity, so both grids fall to two columns at 900 px. */
.seo-index-cards__grid--five {
  grid-template-columns: repeat(5, 1fr);
}

/* Five across need 1234 px, a measured width: "Psychodietetycy" in 24 px
   serif is 172 px, a card adds 2 × 22 px of padding and 2 × 1 px of border,
   four gaps are 64 px and the gutters 80. Narrower, the longest names broke
   inside themselves, so the cards go three across, three and two, until the
   phone rule takes them to two. */
@media (max-width: 1233px) {
  .seo-index-cards__grid--five {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.seo-index-card__name {
  font-family: var(--font-serif);
  font-size: 24px;
  font-weight: 500;
  color: var(--text);
  overflow-wrap: anywhere;
}

.seo-index-card__count {
  font-size: 15px;
  color: var(--muted);
}

.seo-index-card__go {
  font-size: 14px;
  color: var(--accent);
}

.seo-index-rest {
  padding: 0 var(--gutter) 64px;
}

.seo-index-rest__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0 40px;
}

.seo-index-rest__column {
  display: flex;
  flex-direction: column;
}

.seo-index-rest__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The rule is on top, not underneath: the columns end at different heights and
   a bottom rule would draw four ragged lines across the section (37.3). */
.seo-index-rest__item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 0;
  border-top: 1px solid var(--line);
  font-size: 16px;
  color: var(--text);
  text-decoration: none;
}

/* A row is a city name and a count on one line; underlining the whole flex
   item would rule through the gap between them, so hover colours instead. */
.seo-index-rest__item:hover {
  color: var(--accent);
}

.seo-index-rest__count {
  flex: none;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--muted);
}

/* Note 03 puts this at the end of the last column, where somebody who did not
   find their city arrives (37.4). */
.seo-index-online {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 20px;
  padding: 18px 20px;
  background: var(--tint);
  border-radius: 8px;
}

.seo-index-online__question {
  font-size: 15px;
  line-height: 1.55;
  color: var(--text);
}

.seo-index-online__link {
  font-size: 15px;
  color: var(--accent);
  text-decoration: none;
}

.seo-index-online__link:hover {
  text-decoration: underline;
}

/* --- Telefon (section 15) ------------------------------------------------------ */

@media (max-width: 900px) {
  /* The 340 px box goes under the heading; sticky in one column would pin the
     FAQ rail to the bottom of the questions — the kb-aside treatment. */
  .seo-hero__inner,
  .seo-faq__inner,
  .seo-links__inner {
    grid-template-columns: 1fr;
    gap: 40px;
  }

  /* Four columns of cities do not survive a phone. The cards go two across and
     the list to one column, which reads as one alphabetical run — the split
     into four was a layout device, not an ordering (37.3). */
  .seo-index-cards__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .seo-index-rest__grid {
    grid-template-columns: 1fr;
    gap: 0;
  }

  .seo-index-hero__title {
    font-size: 32px;
  }

  .seo-faq__rail {
    position: static;
  }

  .seo-hero__title {
    font-size: 32px;
  }

  .seo-faq__title {
    font-size: 26px;
  }

  /* The price-and-button column takes a row of its own under the photo and
     the body. Left to wrap only when it no longer fits, it stayed beside a
     body that shrinks to nothing (flex: 1 starts from zero), and at 390 px
     the name broke every two letters while the page still fit the screen. */
  .compact-card {
    flex-wrap: wrap;
  }

  .compact-card__aside {
    flex-basis: 100%;
    align-items: flex-start;
  }

  /* A tag is the specialist's own words ("Specjalista chorób wewnętrznych" is
     215 px) and at 320 px a single one ran past the screen. It still moves to
     the next row whole first; only a tag wider than the row breaks. */
  .compact-card__tag {
    white-space: normal;
    overflow-wrap: anywhere;
  }
}

/* One card per row below 488 px, a measured width (section 41.1). The longest
   name the cards carry, "Psychodietetycy" in 24 px serif, is 172 px wide in
   Chromium and in Firefox; a card adds 2 × 22 px of padding and 2 × 1 px of
   border, so two of them, the 16 px gap and 2 × 18 px of gutter need 488 px.
   Narrower, two columns would break the word inside itself. The city cards of
   /miasta/ and of the type pages share the grid: their longest name today,
   "Bydgoszcz" at 110 px, fits two across at 390 px but not at 320. */
@media (max-width: 487px) {
  .seo-index-cards__grid {
    grid-template-columns: minmax(0, 1fr);
  }
}


/* ==========================================================================
   28. Strona dla specjalistów — /dla-specjalistow/
   Head of Marketing's TEKSTY_37_DLA_SPECJALISTOW_V17 and the layout of its
   brief, on the tokens and type of the home page. Five bands: the hero on
   --hero with the mesh and a photograph, the calendar, patient data and the
   price together on --deep, the questions in the home page's .home-faq and
   .faq-list, and the close on --hero.

   The calendar is a screenshot of the panel, a detail cut out of a screen of
   dev. A screen of 1440 px scaled into a phone column is unreadable, so the
   phone gets a second, narrower detail (.offer-shot__phone).
   ========================================================================== */

.offer-kicker {
  display: block;
  margin-bottom: 16px;
  color: var(--accent);
}

/* --- Screenshots ------------------------------------------------------------ */

.offer-shot {
  display: block;
  overflow: hidden;
  line-height: 0;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 10px;
}

.offer-shot img {
  display: block;
  width: 100%;
  height: auto;
}

/* Two classes, so .offer-shot img above does not show the phone's detail too. */
.offer-shot .offer-shot__phone {
  display: none;
}

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

/* The same mesh declaration as .home-hero. */
.offer-hero {
  padding: 68px var(--gutter) 64px;
  overflow: hidden;
  background-color: var(--hero);
  background-image: linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 36px 36px, 36px 36px;
  border-bottom: 1px solid var(--line-strong);
}

.offer-hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: 60px;
  max-width: var(--content-max);
  margin: 0 auto;
}

.offer-hero__title {
  max-width: 600px;
  margin: 2px 0 24px;
  font-size: 56px;
}

.offer-hero__text {
  font-size: 18px;
  line-height: 1.65;
}

.offer-hero__who {
  margin: 14px 0 22px;
  font-size: 15px;
  color: var(--muted);
}

.offer-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.offer-hero__photo {
  display: block;
  width: 100%;
  height: 440px;
  object-fit: cover;
  object-position: center 64%;
  border-radius: 80px 12px 12px;
}

/* --- Bands ------------------------------------------------------------------ */

.offer-band {
  padding: 76px var(--gutter);
}

.offer-band__inner {
  max-width: var(--content-max);
  margin: 0 auto;
}

.offer-band__title {
  max-width: 800px;
  margin-bottom: 24px;
  font-size: 38px;
  line-height: 1.18;
  letter-spacing: -0.015em;
}

.offer-band__lead {
  max-width: 800px;
  font-size: 18px;
  line-height: 1.65;
}

.offer-band h3 {
  font-size: 24px;
  line-height: 1.18;
}

/* The ring of .offer-dark :focus-visible is in section 4. */
.offer-dark {
  color: var(--on-deep);
  background: var(--deep);
}

.offer-dark .offer-kicker {
  color: var(--s1);
}

.offer-dark h2,
.offer-dark h3,
.offer-dark a {
  color: var(--on-deep);
}

/* --- Benefits bar ---------------------------------------------------------- */

/* Three points between the hero and the calendar: columns parted by a rule on a
   computer, rows on the phone with the tick held to the first line of a point
   that wraps (Graphic Designer's PASEK_KORZYSCI_37_V04). Every point starts at
   the left of its column, 24px in: centred, the shorter point began further
   right than the others (WYROWNANIE_PASKA_37_V01). */
.offer-benefits {
  padding: 36px var(--gutter);
  background: var(--surface);
  border-top: 3px solid var(--accent);
  border-bottom: 1px solid var(--line-strong);
}

.offer-benefits__list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

.offer-benefits__item {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  margin: 0;
  padding: 12px 24px;
  color: var(--deep);
  font: 600 18px/1.4 var(--font-sans);
}

.offer-benefits__mark {
  width: 20px;
  height: 20px;
  flex: none;
  color: var(--accent);
}

.offer-benefits__item + .offer-benefits__item {
  border-left: 1px solid var(--line-strong);
}

@media (max-width: 900px) {
  .offer-benefits {
    padding-block: 8px;
  }

  .offer-benefits__list {
    grid-template-columns: 1fr;
  }

  .offer-benefits__item {
    align-items: flex-start;
    padding: 24px 0;
    font-size: 20px;
  }

  .offer-benefits__mark {
    margin-top: 4px;
  }

  .offer-benefits__item + .offer-benefits__item {
    border-left: 0;
    border-top: 1px solid var(--line);
  }
}

/* --- Calendar --------------------------------------------------------------- */

.offer-calendar {
  background: var(--hero);
}

.offer-calendar .offer-band__title {
  max-width: 730px;
  font-size: 46px;
}

.offer-calendar__picture {
  margin-top: 36px;
  padding: 24px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 14px;
  box-shadow: 0 16px 48px rgba(11, 46, 72, 0.04);
}

/* --- The price ------------------------------------------------------------- */

/* Three steps in one line (CENNIK_3_ETAPY_GD_V01): a numbered ring over each
   step and a 1 px rule from one ring to the next, then the title, the amount
   as the strongest mark and the sentence that says when it is paid. Under
   1100 px the steps stack and the rule runs down the left. The numbers repeat
   the order of the list, so they are aria-hidden. */
.price-flow {
  padding: 72px var(--gutter);
}

.price-flow__inner {
  max-width: var(--content-max);
  margin: auto;
}

.price-flow .t-overline {
  color: var(--s1);
}

.price-flow h2 {
  margin: 12px 0 48px;
  font: 400 42px/1.18 var(--font-serif);
  color: var(--surface);
}

.price-flow__steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 40px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.price-flow__step {
  position: relative;
  min-width: 0;
}

.price-flow__step::before {
  content: "";
  position: absolute;
  top: 18px;
  left: 36px;
  width: calc(100% + 4px);
  height: 1px;
  background: var(--line-control);
}

.price-flow__step:last-child::before {
  display: none;
}

.price-flow__number {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  font-size: 14px;
  color: var(--on-deep);
  border: 1px solid var(--s1);
  border-radius: 50%;
}

.price-flow__step h3 {
  max-width: 260px;
  min-height: 55px;
  margin: 24px 0 18px;
  font: 500 21px/1.3 var(--font-sans);
  color: var(--surface);
}

.price-flow__amount {
  margin: 0 0 24px;
  font: 500 64px/1 var(--font-sans);
  letter-spacing: -2px;
  color: var(--surface);
}

.price-flow__amount small {
  font-size: 25px;
  font-weight: 400;
  letter-spacing: 0;
}

.price-flow__period {
  display: block;
  min-height: 24px;
  margin-top: 10px;
  font: 400 16px/1.5 var(--font-sans);
  letter-spacing: 0;
  color: var(--on-deep);
}

.price-flow__copy {
  margin: 0;
  font: 400 17px/1.65 var(--font-sans);
  color: var(--on-deep);
}

.price-flow__copy strong {
  font-weight: 600;
  color: var(--surface);
}

@media (max-width: 1099px) {
  .price-flow {
    padding-top: 48px;
    padding-bottom: 48px;
  }

  .price-flow h2 {
    max-width: 560px;
    margin-bottom: 36px;
    font-size: 34px;
  }

  .price-flow__steps {
    grid-template-columns: 1fr;
    gap: 0;
  }

  .price-flow__step {
    padding: 0 0 36px 56px;
  }

  .price-flow__step:last-child {
    padding-bottom: 0;
  }

  .price-flow__number {
    position: absolute;
    top: 0;
    left: 0;
  }

  .price-flow__step::before {
    top: 36px;
    left: 17px;
    width: 1px;
    height: calc(100% - 36px);
  }

  .price-flow__step h3 {
    max-width: none;
    min-height: 0;
    margin: 0 0 16px;
    padding-top: 4px;
    font-size: 20px;
  }

  .price-flow__amount {
    margin-bottom: 18px;
    font-size: 54px;
  }

  .price-flow__period {
    min-height: 0;
    margin-top: 8px;
  }

  .price-flow__period:empty {
    display: none;
  }

  .price-flow__copy {
    max-width: 620px;
  }
}

@media (max-width: 599px) {
  .price-flow h2 {
    font-size: 32px;
  }

  .price-flow__step {
    padding-left: 48px;
  }

  .price-flow__copy {
    font-size: 16px;
    line-height: 1.6;
  }
}

/* --- Close ------------------------------------------------------------------ */

.offer-close {
  padding: 76px var(--gutter);
  text-align: center;
  background: var(--hero);
  border-top: 5px solid transparent;
  border-image: linear-gradient(
      to right,
      var(--c1) 0 20%,
      var(--c2) 20% 40%,
      var(--c3) 40% 60%,
      var(--c4) 60% 80%,
      var(--c5) 80%
    )
    1;
}

.offer-close__inner {
  max-width: var(--content-max);
  margin: 0 auto;
}

.offer-close__title {
  margin-bottom: 24px;
  font-size: 38px;
  line-height: 1.18;
}

/* --- Narrower screens ------------------------------------------------------- */

/* The phone, at the site's one breakpoint. */
@media (max-width: 900px) {
  .offer-hero {
    padding: 34px var(--gutter);
  }

  .offer-hero__inner {
    grid-template-columns: 1fr;
    gap: 30px;
  }

  .offer-hero__title {
    margin: 0 0 22px;
    font-size: 39px;
    line-height: 1.12;
  }

  .offer-hero__text,
  .offer-band__lead {
    font-size: 17px;
  }

  .offer-hero__actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
  }

  .offer-hero__photo {
    height: 230px;
    border-radius: 55px 10px 10px;
  }

  .offer-shot--phone .offer-shot__wide {
    display: none;
  }

  .offer-shot--phone .offer-shot__phone {
    display: block;
  }

  .offer-band,
  .offer-close {
    padding: 44px var(--gutter);
  }

  .offer-band__title,
  .offer-calendar .offer-band__title {
    margin-bottom: 20px;
    font-size: 33px;
  }

  .offer-calendar__picture {
    padding: 10px;
    border-radius: 10px;
  }

  /* The phone's detail is 764 px: at most half of it, so a screen of 900 px
     does not stretch it past its file. */
  .offer-calendar__picture .offer-shot {
    max-width: 382px;
    margin: 0 auto;
  }

  .offer-close__title {
    font-size: 32px;
  }
}


/* ==========================================================================
   29. Strony formalne i zgody — frames 10d and 12d
   Four documents behind one layout (note 01 beside 10d: "Zmienia się tytuł,
   data i treść — nic więcej") and the consent settings, which the frame draws
   as a modal and this project renders as a page: DESIGN_SPEC.md "Czego nie ma
   w systemie" rules out both modals and shadows, and ROUTES.md gives 12d an
   address of its own.

   Values are the frames' own, written up as DESIGN_SPEC.md section 39. The
   title band is shared by both screens, so /regulamin/ and /zgody/ open the
   same way.
   ========================================================================== */

/* --- The title band, shared by 10d and 12d (39.1) ------------------------- */

.doc-head {
  background: var(--band);
  border-bottom: 1px solid var(--line-strong);
  padding: 48px var(--gutter) 40px;
}

.doc-head__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 8px 24px;
  max-width: var(--content-max);
  margin: 0 auto;
}

/* 12d only. The band is shared with 10d, whose column is the full 1180 px, but
   the consent card is 640 — and a heading that started 260 px to the left of
   the thing it names would read as belonging to something else. The modal of
   the frame had no such relationship to answer for; a page does. */
.doc-head--narrow .doc-head__inner {
  max-width: 640px;
}

.doc-head__kicker {
  flex-basis: 100%;
  color: var(--accent);
}

.doc-head__title {
  font-family: var(--font-serif);
  font-size: 40px;
  line-height: 1.15;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--text);
}

/* Note 04: "Data wejścia w życie w nagłówku, nie na końcu. Przy zmianie
   regulaminu to pierwsza rzecz, której się szuka." Sits on the baseline of the
   title rather than under it, which is what the frame draws. */
.doc-head__date {
  padding-bottom: 6px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
}

/* The archive beside the date, and on a dated version the way back to the text
   in force. Same baseline and type as the date, so the three read as one line
   of facts about the version, not as a navigation bar. */
.doc-head__links {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  padding-bottom: 6px;
  font-family: var(--font-mono);
  font-size: 12px;
}

.doc-head__link {
  color: var(--accent);
}

/* The announced version's strip, above the summary box. */
.doc-body__notice {
  margin-bottom: 14px;
}

/* --- Every version of a document ----------------------------------------- */

/* A short list of dated rows, newest first, under the title it continues: it
   starts where the title starts and keeps to the width of a document's body. */
.doc-versions {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

.doc-versions__item {
  max-width: 720px;
  padding: 20px 0;
  border-bottom: 1px solid var(--line);
}

.doc-versions__item:first-child {
  padding-top: 0;
}

.doc-versions__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
}

.doc-versions__period {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
}

.doc-versions__summary {
  margin: 10px 0 8px;
}

.doc-versions__link {
  color: var(--accent);
  font-weight: 500;
}

/* --- 10d, the two columns (39.1) ----------------------------------------- */

.doc {
  padding: 52px var(--gutter) 80px;
}

.doc__inner {
  display: grid;
  grid-template-columns: 260px 1fr;
  align-items: start;
  gap: 72px;
  max-width: var(--content-max);
  margin: 0 auto;
}

/* --- The contents rail (39.2) -------------------------------------------- */

/* Note 03: "Nagłówki paragrafów są kotwicami, a spis treści jest przyklejony.
   Regulamin bywa czytany wyrywkowo, w sporze o konkretny punkt." */
.doc-rail {
  position: sticky;
  top: 24px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.doc-rail__kicker {
  color: var(--muted);
}

/* No current entry. Marking one means watching the scroll position, which is a
   script, and docs/CONVENTIONS.md names its exceptions one by one — the same
   answer templates/content/partials/article_aside.html gives for the rail of 9b. */
.doc-rail__list {
  display: flex;
  flex-direction: column;
  gap: 9px;
  margin-top: 12px;
  padding-left: 14px;
  border-left: 2px solid var(--accent);
  list-style: none;
}

.doc-rail__list--plain {
  padding-left: 0;
  border-left: 0;
}

/* Section 4 sets the browser underline as the default and every component that
   is navigation opts out of it, the way .kb-toc__link does for the rail of 9b.
   What keeps it here is the inline link inside running text, one block below. */
.doc-rail__link {
  font-size: 14px;
  line-height: 1.4;
  color: var(--muted);
  text-decoration: none;
}

.doc-rail__link:hover,
.doc-rail__link:focus-visible {
  color: var(--text);
}

.doc-rail__more {
  padding-top: 20px;
  border-top: 1px solid var(--line-strong);
}

/* --- The document body (39.3) -------------------------------------------- */

.doc-body {
  max-width: 720px;
}

/* Note 02: "Ramka W skrocie na górze. Cztery zdania po ludzku, przed
   paragrafami; nikt nie czyta regulaminu, ale to przeczyta." The thick left
   edge is what makes it read as a summary rather than as a first paragraph. */
.doc-summary {
  padding: 22px;
  margin-bottom: 14px;
  background: var(--surface);
  border: 1px solid var(--accent);
  border-left: 5px solid var(--accent);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.doc-summary__kicker {
  margin-bottom: 10px;
  color: var(--accent);
}

.doc-summary p {
  font-size: 17px;
  line-height: 1.7;
  color: var(--text);
  text-wrap: pretty;
}

.doc-section {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 24px 0;
  border-top: 1px solid var(--line);
  /* The header is 72 px and sticky, so an anchor landing at the top of the
     viewport lands underneath it. Same figure and same reason as
     .kb-entry__body h2. */
  scroll-margin-top: 96px;
}

.doc-section:first-of-type {
  border-top-color: var(--line-strong);
}

.doc-section:last-of-type {
  border-bottom: 1px solid var(--line-strong);
}

.doc-section__title {
  font-family: var(--font-serif);
  font-size: 24px;
  line-height: 1.25;
  font-weight: 500;
  color: var(--text);
}

.doc-section p,
.doc-section li {
  font-size: 17px;
  line-height: 1.75;
  color: var(--text);
  text-wrap: pretty;
}

/* Ustępy and the punkty inside them. The documents refer to "§ 6 ust. 4
   pkt 1", so the numbers have to show: "1." for an ustęp, "1)" for a punkt,
   the way Polish legal texts write them. */
.doc-section ol {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-left: 26px;
  list-style: decimal;
}

.doc-section ol ol {
  margin-top: 8px;
  gap: 6px;
  list-style: doc-point;
}

.doc-section li::marker {
  color: var(--muted);
}

@counter-style doc-point {
  system: extends decimal;
  suffix: ") ";
}

/* Section 12, unchanged: mono uppercase head, no zebra, row separator --line.
   The retention table of the privacy policy is its only caller. */
.doc-table {
  width: 100%;
  margin-top: 4px;
  border-collapse: collapse;
}

.doc-table th,
.doc-table td {
  padding: 16px 0;
  font-size: 16px;
  line-height: 1.5;
  text-align: left;
  vertical-align: top;
  border-top: 1px solid var(--line);
}

.doc-table thead th {
  padding: 12px 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  border-top: 0;
  border-bottom: 1px solid var(--line-strong);
}

.doc-table tbody th {
  width: 45%;
  padding-right: 24px;
  font-weight: 400;
  color: var(--text);
}

.doc-table tbody td {
  color: var(--muted);
}

.doc-table tbody tr:hover {
  background: var(--hl);
}

/* --- 12d, the consent settings (39.4) ------------------------------------ */

.consent {
  padding: 40px var(--gutter) 80px;
}

.consent__inner {
  display: flex;
  flex-direction: column;
  gap: 20px;
  /* The card of the frame is 640 px and stays 640 px; what changed is that it
     is a column on a page instead of a card on an overlay. */
  max-width: 640px;
  margin: 0 auto;
}

/* Note 04: "Zdanie o możliwości zmiany zgody na górze, z nazwą linku
   w stopce. Zgoda bez wyjścia nie jest zgodą." */
.consent__lead {
  font-size: 16px;
  line-height: 1.65;
  color: var(--muted);
  text-wrap: pretty;
}

.consent__rows {
  border-top: 1px solid var(--line-strong);
}

.consent-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 28px;
  padding: 20px 0;
}

.consent-row + .consent-row {
  border-top: 1px solid var(--line);
}

/* Note 03: "Każdy wiersz mówi, co konkretnie zbieramy, a nie poprawiamy
   doświadczenie użytkownika." */
.consent-row__name {
  font-size: 17px;
  color: var(--text);
}

label.consent-row__name {
  cursor: pointer;
}

.consent-row__note {
  margin-top: 5px;
  font-size: 15px;
  line-height: 1.55;
  color: var(--muted);
  text-wrap: pretty;
}

.consent-row__control {
  display: flex;
  flex: none;
  align-items: center;
  gap: 10px;
  padding-top: 2px;
}

.consent-row__state {
  font-size: 14px;
  color: var(--muted);
}

/* The card footer of the frame, and not components/card_footer.html: that one
   offers a quiet link where this needs a second submit. Both buttons here are one click, which is
   "Odmowa musi być tak samo łatwa jak zgoda" expressed in markup. */
.consent__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px 20px;
  padding: 22px 32px;
  margin-top: 8px;
  background: var(--band);
  border-radius: var(--radius-card);
}

.consent__policy {
  font-size: 15px;
  color: var(--accent);
  text-decoration: none;
}

.consent__policy:hover {
  text-decoration: underline;
}

.consent__buttons {
  display: flex;
  gap: 10px;
}

/* The consent screen's one box, kept off the footer of buttons under it. */
.consent__form > .field {
  margin-bottom: 20px;
}

/* The confirmation of a withdrawn consent to health information: the
   consequences as a list, in the lead's type, above the same footer. */
.consent__text {
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-size: 16px;
  line-height: 1.65;
  color: var(--text);
}

.consent__list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-left: 22px;
  list-style: disc;
}

/* --- The consent bar ------------------------------------------------------ */

/* At the bottom of the window while a measured page is read, and after the
   footer in the page's own flow: sticky rather than fixed, so the bar never
   covers the last lines of the footer and needs no padding reserved for it.
   No overlay and no shadow — the page behind stays readable and scrollable,
   and DESIGN_SPEC.md keeps shadows to the save bar. A top border separates it
   from the text passing underneath. */
.consent-bar {
  position: sticky;
  bottom: 0;
  z-index: 10;
  background: var(--surface);
  border-top: 1px solid var(--line);
}

.consent-bar__inner {
  display: flex;
  align-items: center;
  gap: 24px;
  max-width: 1200px;
  margin: 0 auto;
  padding: 16px 24px;
}

.consent-bar__text {
  flex: 1;
  font-size: 14px;
  line-height: 1.55;
  color: var(--text);
}

.consent-bar__link {
  color: var(--accent);
  text-decoration: underline;
}

/* The two buttons side by side and never apart, "Odrzuć" first. */
.consent-bar__buttons {
  display: flex;
  flex: none;
  gap: 10px;
}

/* --- Phone (39.8) -------------------------------------------------------- */

@media (max-width: 900px) {
  .doc-head {
    padding: 30px 18px 24px;
  }

  .doc-head__title {
    font-size: 30px;
  }

  .doc {
    padding: 32px 18px 56px;
  }

  /* One column, and the rail stops being sticky — pinned to the top of a phone
     it would take a third of the screen away from the text it links to. It
     stays above the document rather than below it, because a contents list
     read after the document is not a contents list. */
  .doc__inner {
    grid-template-columns: 1fr;
    gap: 32px;
  }

  .doc-rail {
    position: static;
  }

  .consent {
    padding: 28px 18px 56px;
  }

  .consent__actions {
    padding: 20px;
  }

  /* The link and the two buttons need about 500 px side by side and the design
     width of section 15 is 390. The buttons take a row of their own and keep
     their order, so refusing stays exactly as reachable as consenting. */
  .consent__buttons {
    flex-basis: 100%;
  }

  .consent__buttons .btn {
    flex: 1;
  }

  /* On a phone the sentence takes the full width and the buttons a row of
     their own below it, both the same width. */
  .consent-bar__inner {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    padding: 14px 18px;
  }

  .consent-bar__buttons .btn {
    flex: 1;
  }
}

/* ==========================================================================
   30. Zgłoszenie nieprawidłowości — frame 12f
   One screen, reached from the rail of 1e and from nowhere else. The column is
   640 px like 10c, and most of it is section 2's field with two additions the
   frame draws: a list of five radio rows that are the whole target, and the
   "co się dzieje potem" band at the bottom.
   Dimensions come from DESIGN_SPEC.md where it has an opinion — sections 1
   (buttons), 2 (field, textarea, counter) and 15 (touch targets) — and from
   the frame where it does not. The register of what the frame draws
   differently is DESIGN_SPEC.md section 40.
   ========================================================================== */

/* The crumb strip sits on --surface between the header and the page, exactly
   as the frame draws it: a full-width band with its own bottom line, not a row
   inside the 640 px column. */
.report-crumbs-band {
  padding: 20px var(--gutter);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.report-crumbs {
  max-width: 1180px;
  margin: 0 auto;
}

.report {
  padding: 56px var(--gutter) 88px;
}

.report__inner {
  max-width: 640px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.report__head {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* .t-overline carries the face, the size and the tracking; the colour is the
   caller's, the way every other kicker in this sheet sets it. */
.report__kicker {
  margin: 0;
  color: var(--accent);
}

.report__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 34px;
  line-height: 1.18;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text);
}

/* --- The specialist card --------------------------------------------------- */

.report-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 18px 20px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

/* 44 px, the frame's own number here — 10c draws 48 on the same element and
   section 24 says why it kept it: that card stands alone on its column, this
   one sits between a lead paragraph and a form. */
.report-card__photo {
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  object-fit: cover;
}

/* No photo means initials on --tint, never a silhouette (DESIGN_SPEC.md
   section 3). --t4 on the frame, because the profile it draws is a doctor;
   the placeholder here cannot follow a type, since a specialist may hold
   several — so it takes the neutral tint the rest of the site uses. */
.report-card__photo--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--tint);
  color: var(--accent);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.02em;
}

.report-card__identity {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.report-card__name {
  font-size: 16px;
  font-weight: 500;
  color: var(--text);
  text-decoration: none;
}

.report-card__name:hover {
  text-decoration: underline;
}

.report-card__facts {
  font-size: 14px;
  color: var(--muted);
}

/* --- The five reasons ------------------------------------------------------ */

.report-reasons {
  margin: 0;
  padding: 0;
  border: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.report-reasons__label {
  padding: 0;
  font-size: 16px;
  font-weight: 500;
  color: var(--text);
}

.report-reasons__list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* The whole row is the label, so the target is 50 px tall rather than the
   18 px dot — section 15 asks for 44 and this clears it on a mouse as well. */
.report-reason {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 15px 18px;
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-card);
  cursor: pointer;
}

.report-reason:hover {
  border-color: var(--accent);
}

/* Clipped rather than display:none. A radio hidden by display takes no
   keyboard focus and the whole group stops working for anybody not using a
   mouse — the recipe the calculators and the stars of 10c both use. */
.report-reason input {
  position: absolute;
  width: 1px;
  height: 1px;
  clip-path: inset(50%);
  overflow: hidden;
}

.report-reason__dot {
  flex: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1px solid var(--line-control);
  background: var(--surface);
}

.report-reason__name {
  font-size: 16px;
  line-height: 1.45;
  color: var(--text);
}

/* The picked row, drawn from the input's own state rather than from a class
   the server writes: with no re-render between the click and the submit,
   a server-rendered class would show the previous answer. */
.report-reason:has(input:checked) {
  border-color: var(--accent);
}

.report-reason:has(input:checked) .report-reason__dot {
  border: 5px solid var(--accent);
}

/* The input is off-screen, so the focus ring goes on the row a person can
   see — section 1's outline, on the element that is actually visible. */
.report-reason:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.report-reasons--error .report-reason {
  border-color: var(--alert);
}

/* --- The form -------------------------------------------------------------- */

.report-form {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.report-form__email {
  display: flex;
  flex-direction: column;
}

/* Beside the label and above the field, which is where the frame puts it. */
.report-form__optional {
  align-self: flex-end;
  order: -1;
  margin-bottom: -22px;
  font-size: 13px;
  color: var(--muted);
}

.report-form__actions {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding-top: 4px;
}

/* "Anuluj" is a quiet exit beside the primary button, not a cross in a corner
   — the shape note 05 of frame 10c gave "Nie chcę oceniać". */
.report-form__cancel {
  font-size: 16px;
  color: var(--muted);
  text-decoration: none;
}

.report-form__cancel:hover {
  color: var(--text);
  text-decoration: underline;
}

/* The fixed box above the button of /zglos-tresc/ — the 112 sentence. The
   band of "Co dalej" with the alert colour on its edge: it is a warning that
   stands before anything is sent, not an error about something that was. */
.report-emergency {
  margin: 0;
  padding: 14px 18px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--text);
  background: var(--band);
  border-left: 3px solid var(--alert);
  border-radius: var(--radius-card);
}

/* --- What happens next ----------------------------------------------------- */

.report-after {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 18px 20px;
  background: var(--band);
  border-radius: var(--radius-card);
}

.report-after__title {
  color: var(--accent);
}

.report-after__text {
  margin: 0;
  font-size: 15px;
  line-height: 1.6;
  color: var(--text);
}

/* --- Phone (40.6) ---------------------------------------------------------- */

@media (max-width: 900px) {
  .report-crumbs-band {
    padding: 14px 18px;
  }

  .report {
    padding: 32px 18px 56px;
  }

  .report__title {
    font-size: 28px;
  }

  /* The five sentences wrap to two and three lines at 390 px, so the dot has
     to keep to the first of them instead of centring against the block. */
  .report-reason {
    align-items: flex-start;
  }

  .report-reason__dot {
    margin-top: 3px;
  }

  /* The button and the quiet exit need about 300 px side by side and the
     design width of section 15 is 390 — they fit, but only just, so the
     button takes the full row and "Anuluj" keeps its own line under it. */
  .report-form__actions .btn {
    width: 100%;
  }
}

/* ==========================================================================
   31. Pacjenci w panelu specjalisty
   ==========================================================================
   Panel mockup of 24.09, `#pacjenci` and the page of one person; DESIGN_SPEC.md
   section 43 carries the numbers. Both screens take the width of the
   calendar: tiles in three columns on the list, the conversation and the
   visits in two halves on the person's page.

   .person-tile is an <a> and deliberately does not say `cursor: pointer` — a
   link is already one, and the word is what section 42's test looks for when
   it asks whether a control was drawn with a decorative token.
   -------------------------------------------------------------------------- */

/* Redrawn after the panel mockup of 24.09, so the cards take the corner and
   the shadows back from :root inside the flat .panel, as .settings does. */
.people,
.person {
  --radius-card: 16px;
  --shadow-card: 0 1px 2px rgba(11, 46, 72, 0.06);
  --shadow-raised: 0 8px 24px rgba(11, 46, 72, 0.1);
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding-bottom: 40px;
}

/* --- The list ------------------------------------------------------------- */

.people__heading {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin-bottom: 24px;
}

.people__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 30px;
  line-height: 1.2;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text);
}

.people__count {
  font-size: 14px;
  color: var(--muted);
}

/* Who is on the list and for how long, above the search box. */
.people__note {
  margin: 0 0 12px;
  font-size: 14px;
  color: var(--muted);
}

/* The box of the mockup: 300 px beside the tabs on a wide screen, the magnifier
   drawn inside it and not clickable. */
.people__search {
  position: relative;
  max-width: 300px;
  margin-bottom: 12px;
}

.people__search .icon {
  position: absolute;
  top: 13px;
  left: 14px;
  color: var(--muted);
  pointer-events: none;
}

.people__search-input {
  width: 100%;
  height: 44px;
  padding: 0 14px 0 44px;
  font-family: var(--font-sans);
  font-size: 16px;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-field);
}

.people__search-input::placeholder {
  color: var(--muted);
  opacity: 1;
}

.people__search-input:focus {
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.people__tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 20px;
}

.people__tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 36px;
  padding: 0 12px;
  font-size: 14px;
  color: var(--muted);
  text-decoration: none;
  border-radius: var(--radius-pill);
}

.people__tab:hover {
  color: var(--text);
  background: var(--hl);
}

.people__tab--active,
.people__tab--active:hover {
  font-weight: 500;
  color: var(--accent);
  background: var(--tint);
}

.people__tab-count {
  font-size: 13px;
  font-weight: 400;
  color: var(--muted);
}

.people__tab--active .people__tab-count {
  color: var(--accent);
}

.people__tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
  gap: 16px;
}

.person-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
  min-height: 188px;
  padding: 20px;
  color: var(--text);
  text-decoration: none;
  background: var(--surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  transition: box-shadow 0.15s ease;
}

.person-tile:hover {
  box-shadow: var(--shadow-raised);
}

/* The bar of a person with a visit ahead, in the accent colour. */
.person-tile--soon::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 4px;
  background: var(--accent);
}

.person-tile__top {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.person-tile__avatar,
.person__avatar {
  display: grid;
  place-items: center;
  flex: none;
  width: 40px;
  height: 40px;
  font-size: 14px;
  font-weight: 500;
  color: var(--accent);
  background: var(--tint);
  border-radius: 50%;
}

/* A name part may be 100 characters (apps/catalog/names.py); one unbroken word
   of them would set the tile's width, so the name shortens instead. */
.person-tile__name {
  flex: 1;
  min-width: 0;
  font-family: var(--font-serif);
  font-size: 18px;
  font-weight: 500;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.person-tile--unread .person-tile__name {
  font-weight: 600;
}

.person-tile__message {
  margin-top: -4px;
  padding: 8px 12px;
  font-size: 14px;
  line-height: 1.45;
  background: var(--hl);
  border-radius: 10px;
  overflow-wrap: anywhere;
}

.person-tile__message > span {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.person-tile__rows {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 6px 14px;
  font-size: 14px;
}

.person-tile__key {
  color: var(--muted);
  white-space: nowrap;
}

.person-tile__value {
  min-width: 0;
}

.person-tile__strong {
  font-weight: 500;
}

.person-tile__sub {
  display: block;
  color: var(--muted);
}

.person-tile__none {
  color: var(--muted);
}

.person-tile__foot {
  margin-top: auto;
  padding-top: 12px;
  font-size: 13px;
  color: var(--muted);
  border-top: 1px solid var(--line);
}

.people__more {
  align-self: flex-start;
  margin-top: 16px;
}

.people__nothing,
.person__empty {
  margin: 0;
  padding: 20px 24px;
  font-size: 15px;
  color: var(--muted);
  background: var(--surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

/* The mockup's pill badge, not the mono badge of section 6: these are words
   about a visit or a person, not codes. */
.person-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 24px;
  padding: 0 10px;
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
  background: var(--hl);
  border-radius: var(--radius-pill);
}

.person-badge--accent {
  color: var(--accent);
  background: var(--tint);
}

.person-badge--file {
  padding: 0 8px 0 6px;
}

/* The word beside the number of files, for a screen reader: clipped rather
   than hidden, so it stays in the reading order. */
.person-badge__words {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- One person ----------------------------------------------------------- */

.person__back {
  display: inline-flex;
  align-self: flex-start;
  align-items: center;
  gap: 4px;
  margin-bottom: 12px;
  font-size: 14px;
  color: var(--muted);
}

.person__back:hover {
  color: var(--accent);
}

.person > .message {
  margin-bottom: 16px;
}

.person__heading {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 24px;
}

.person__avatar {
  width: 56px;
  height: 56px;
  font-size: 20px;
}

.person__who {
  min-width: 0;
}

/* The heading may hold the same 100-character name and has no second column to
   protect, so it wraps instead of shortening — a title is read, not scanned. */
.person__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 24px;
  line-height: 1.25;
  font-weight: 500;
  color: var(--text);
  overflow-wrap: anywhere;
}

.person__summary {
  margin: 2px 0 0;
  font-size: 14px;
  color: var(--muted);
}

/* The conversation and the rest in two halves (Konrad 24.09: "rozmowa ma
   połowę szerokości strony"). */
.person__columns {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: start;
  gap: 24px;
}

.person__chat,
.person__main {
  min-width: 0;
}

.person__main {
  display: flex;
  flex-direction: column;
  gap: 32px;
}

/* One style for Rozmowa and Wizyty, above their cards. */
.person__section-head {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 38px;
  margin-bottom: 12px;
}

.person__section-title {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: 500;
  color: var(--text);
}

/* The conversation card is section 21's .thread, 640 px, with no head of its
   own: the section title above it names it. */
.person__thread {
  scroll-margin-top: 24px;
}

.person__visits {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.visit-panel {
  background: var(--surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  scroll-margin-top: 24px;
}

.visit-panel__head {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 76px;
  padding: 10px 16px 10px 20px;
  list-style: none;
  border-radius: var(--radius-card);
}

.visit-panel__head::-webkit-details-marker {
  display: none;
}

.visit-panel__head:hover {
  background: var(--hl);
}

.visit-panel__head:hover .person-badge {
  background: var(--surface);
}

.visit-panel[open] .visit-panel__head {
  border-radius: var(--radius-card) var(--radius-card) 0 0;
}

.visit-panel__main {
  flex: 1;
  min-width: 0;
}

.visit-panel__when {
  display: block;
  font-size: 16px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.visit-panel__line {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 12px;
  min-width: 0;
  margin-top: 4px;
}

/* At least 140 px for the place, so on a phone the badges move to a line of
   their own instead of cutting the place down to "Gabinet …". */
.visit-panel__where {
  flex: 1 1 140px;
  min-width: 0;
  font-size: 14px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.visit-panel__marks {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.visit-panel__chevron {
  display: flex;
  color: var(--muted);
}

.visit-panel[open] .visit-panel__chevron {
  transform: rotate(180deg);
}

.visit-panel__body {
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 20px 20px 24px;
  border-top: 1px solid var(--line);
}

/* A body with nothing in it yet — no documentation and nothing to cancel —
   draws no empty band under the head. */
.visit-panel__body:not(:has(> *)) {
  display: none;
}

@media (max-width: 900px) {
  .people__search {
    max-width: none;
  }

  .person__columns {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ==========================================================================
   32. Kontakt — bez ramki
   The third screen of the project with no frame behind it, after the two of
   section 31. Every number here is borrowed from a screen that has one, and
   DESIGN_SPEC.md section 46 says which and why: the column, the rhythm and the
   heading are section 30's, because 12f is the pattern for a form open
   without an account, and the band at the
   bottom is that screen's "co się dzieje potem" doing the same job.
   Nothing here is measured off a picture, because there is no picture.
   ========================================================================== */

.contact {
  padding: 56px var(--gutter) 88px;
}

.contact__inner {
  max-width: 640px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.contact__head {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.contact__kicker {
  margin: 0;
  color: var(--accent);
}

/* 34 px, the value section 40 gives 12f — not the 40 px of the formal documents
   next door in ROUTES.md. Those open a wall of paragraphs under a title band;
   this opens a form, and section 30 already settled that a heading over a form
   is the smaller of the two. */
.contact__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 34px;
  line-height: 1.18;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text);
}

.contact__lead {
  margin: 0;
  font-size: 17px;
  line-height: 1.7;
  color: var(--muted);
  text-wrap: pretty;
}

.contact-form {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

/* One button and no quiet exit beside it. 12f has "Anuluj" because it was
   entered from a profile and there is somewhere to go back to; this page is
   entered from the footer of whatever the reader was looking at, and a cancel
   link pointing at nothing in particular is worse than the browser's own way
   back. */
.contact-form__actions {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding-top: 4px;
}

/* The fields of one topic (static/js/contact.js shows one group at a time),
   spaced like the form around them. */
.contact-form__group {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.contact-form__group[hidden],
.contact-form__actions [hidden] {
  display: none;
}

/* What a browser without scripts reads instead of a form it cannot send. */
.contact__no-script {
  margin: 0;
  font-size: 16px;
  line-height: 1.6;
  color: var(--text);
}

/* --- Phone ------------------------------------------------------------------ */

@media (max-width: 900px) {
  .contact {
    padding: 32px 18px 56px;
  }

  .contact__title {
    font-size: 28px;
  }

  /* The design width of section 15 is 390 px, and a button sized to its label
     in the middle of that is a target somebody has to aim at. Section 30 gives
     its own the full row for the same reason. */
  .contact-form__actions .btn {
    width: 100%;
  }
}

/* The strip under the header of an account blocked by the service
   (apps/accounts/blocking.py). The alert colour on a quiet band: it is a
   state of the account, not an error about the last click. */
.blocked-banner {
  margin: 0;
  padding: 12px 24px;
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
  background: var(--band);
  border-bottom: 2px solid var(--alert);
  text-align: center;
}

/* The strip under the header while the account's own deletion can still be
   taken back (apps/accounts/deletion.py). No alert colour, like the pending
   row on the account screen it points at: the grace period says enough. */
.deletion-banner {
  margin: 0;
  padding: 12px 24px;
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
  background: var(--band);
  border-bottom: 1px solid var(--line-strong);
  text-align: center;
}

.deletion-banner a {
  color: var(--accent);
  font-weight: 500;
}

/* ==========================================================================
   33. Panel wspólne
   The frame of every screen under /panel/ and /moje/ (core/panel_base.html),
   panel mockup of 24.09, "Układ panelu": the --panel-bg ground, the geometry
   the grid and the footer share, and the footer of one line.
   The menu itself is a component and stands in section 7.
   ========================================================================== */

/* The mockup's grid at 1440: a 200 px menu, a 40 px gap and a content
   column; 1356 px wide at most, not counting the gutters. Custom properties
   on the body so the grid (section 18) and the footer below line up from one
   set of numbers. */
.page-panel {
  --panel-menu: 200px;
  --panel-gap: 40px;
  --panel-max: calc(1356px + 2 * var(--gutter));
  background: var(--panel-bg);
}

/* The footer the panel has instead of the navy one (DECISIONS.md "Jedna
   stopka wszędzie"): one muted line, standing under the content column rather
   than under the menu. */
/* width: 100% because the body is a flex column (section 6): without it the
   auto margins shrink the footer to its line and centre it. */
.panel-foot {
  width: 100%;
  max-width: var(--panel-max);
  margin: 0 auto;
  padding: 8px var(--gutter) 40px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
}

.panel-foot__line {
  margin: 0;
  padding-left: calc(var(--panel-menu) + var(--panel-gap));
}

.panel-foot__link {
  color: var(--muted);
  text-decoration: none;
}

.panel-foot__link:hover {
  color: var(--accent);
}

@media (max-width: 900px) {
  /* No menu column on the phone, so the line starts at the gutter. */
  .panel-foot__line {
    padding-left: 0;
  }
}

/* ==========================================================================
   34. Przyjęcie
   ========================================================================== */

/* The gate before the first intake: the entrustment clause, one sentence and
   two buttons (apps/accounts/entrustment.py). No frame draws it; it stands
   where the intake form would, as a card of the panel, and on its own page in
   the same column width as the person screen. The clause is a legal text of
   eleven paragraphs, so it scrolls inside the card instead of pushing the
   buttons a few screens down. */
.entrustment {
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-width: 820px;
  padding: 24px;
  background: var(--surface);
  border-radius: var(--radius-card);
}

.entrustment__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 24px;
  line-height: 1.25;
  font-weight: 500;
  color: var(--text);
}

.entrustment__clause {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-height: 420px;
  padding-right: 8px;
  overflow-y: auto;
}

.entrustment__clause p {
  margin: 0;
  font-size: 15px;
  line-height: 1.65;
  color: var(--text);
  text-wrap: pretty;
}

.entrustment__note {
  margin: 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--muted);
}

.entrustment__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 12px;
}

/* The intake of one visit — panel mockup of 24.09, `#pacjenci` → a person,
   frames 207 and 208 (.vsec, .iblock, .ptgrid, .fixbox, .hrow and .frow there).
   It sits in the body of a visit card (section 31), so it draws no card of
   its own; on the page of its own it does (.intake--page). */
.intake {
  display: flex;
  flex-direction: column;
}

.intake__region:focus {
  outline: none;
}

.intake__top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  min-height: 38px;
}

.intake__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 18px;
  line-height: 1.3;
  font-weight: 500;
  color: var(--text);
}

/* "Widzisz tylko Ty": who can read this section, beside its title. */
.intake__who {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 10px;
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
  background: var(--hl);
  border-radius: var(--radius-pill);
}

.intake__actions {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-left: auto;
}

.intake__body {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-top: 16px;
}

.intake__unchanged {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
}

.intake__foot {
  margin: 20px 0 0;
  padding-top: 14px;
  font-size: 13px;
  color: var(--muted);
  border-top: 1px solid var(--line);
}

/* The sentence a save ends with: for a screen reader, nothing on screen.
   Clipped rather than hidden, like .announcement, so it stays a live region. */
.intake__announce {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.intake__gate {
  margin-top: 16px;
}

.intake__gate .entrustment {
  max-width: none;
  padding: 0;
}

/* --- One version: blocks, the patient's grid, the results ------------------ */

.intake-block__label {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 20px;
  font-size: 13px;
  color: var(--muted);
}

/* "zmienione" and "obecna". */
.intake-badge {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 8px;
  font-size: 12px;
  font-weight: 400;
  color: var(--accent);
  background: var(--tint);
  border-radius: var(--radius-pill);
}

.intake-block__text {
  margin: 4px 0 0;
  font-size: 15px;
  line-height: 1.6;
  color: var(--text);
  white-space: pre-line;
  overflow-wrap: anywhere;
}

.intake-grid {
  display: grid;
  grid-template-columns: 170px minmax(0, 1fr);
  gap: 6px 16px;
  margin: 6px 0 0;
  padding: 14px 18px;
  font-size: 15px;
  background: var(--bg);
  border-radius: var(--radius-tile);
}

.intake-grid dt {
  font-size: 14px;
  line-height: 22.5px;
  color: var(--muted);
}

.intake-grid dd {
  margin: 0;
  color: var(--text);
  white-space: pre-line;
  overflow-wrap: anywhere;
}

.intake-file {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 52px;
  padding: 6px 4px 6px 0;
}

.intake-file + .intake-file {
  border-top: 1px solid var(--line);
}

.intake-file__icon {
  display: grid;
  flex: none;
  place-items: center;
  width: 36px;
  height: 36px;
  color: var(--accent);
  background: var(--hl);
  border-radius: 10px;
}

.intake-file__main {
  flex: 1;
  min-width: 0;
}

.intake-file__name {
  display: block;
  overflow: hidden;
  font-size: 15px;
  color: var(--text);
  text-overflow: ellipsis;
  white-space: nowrap;
}

a.intake-file__name {
  color: var(--accent);
}

a.intake-file__name:hover {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.intake-file__meta {
  display: block;
  font-size: 13px;
  color: var(--muted);
}

/* The × of a result in the form: a checkbox, so taking a result out works
   with no script. The box is clipped and the cross drawn; a ticked result is
   greyed and struck through until the form is saved. */
.intake-file__remove {
  display: grid;
  flex: none;
  place-items: center;
  width: 36px;
  height: 36px;
  color: var(--muted);
  border-radius: var(--radius-button);
  cursor: pointer;
}

.intake-file__remove:hover {
  color: var(--alert);
  background: var(--hl);
}

.intake-file__remove input {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.intake-file__remove:has(input:focus-visible) {
  box-shadow: var(--ring);
}

.intake-file:has(.intake-file__remove input:checked) .intake-file__main {
  text-decoration: line-through;
  opacity: 0.5;
}

.intake-file:has(.intake-file__remove input:checked) .intake-file__remove {
  color: var(--alert);
}

/* --- Historia zmian -------------------------------------------------------- */

.intake-history {
  margin-top: 12px;
}

.intake-history > summary,
.intake-version > summary {
  list-style: none;
}

.intake-history > summary::-webkit-details-marker,
.intake-version > summary::-webkit-details-marker {
  display: none;
}

.intake-history__toggle {
  display: inline-flex;
  gap: 6px;
  width: fit-content;
}

.intake-history__rows {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 12px;
}

.intake-version {
  border-radius: var(--radius-tile);
}

.intake-version[open] {
  background: var(--hl);
}

.intake-version__head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border-radius: var(--radius-tile);
  cursor: pointer;
}

.intake-version__head:hover {
  background: var(--hl);
}

.intake-version[open] .intake-version__head:hover {
  background: var(--tint);
}

.intake-version__main {
  flex: 1;
  min-width: 0;
}

.intake-version__title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  font-weight: 500;
  color: var(--text);
}

.intake-version__meta {
  display: block;
  font-size: 13px;
  color: var(--muted);
}

.intake-version__chevron {
  display: grid;
  color: var(--muted);
}

.intake-version[open] .intake-version__chevron {
  transform: rotate(180deg);
}

.intake-version__body {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 4px 14px 18px;
}

.intake-version[open] .intake-grid,
.intake-version[open] .intake-file__icon {
  background: var(--surface);
}

/* --- The form -------------------------------------------------------------- */

.intake-form {
  display: flex;
  flex-direction: column;
  gap: 20px;
  margin-top: 20px;
}

.intake-field__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.intake-field__count {
  font-size: 13px;
  color: var(--muted);
}

.intake-field textarea {
  min-height: 96px;
}

.intake-field--tall textarea {
  min-height: 168px;
}

.intake-patient {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 20px;
  background: var(--bg);
  border-radius: var(--radius-tile);
}

.intake-patient__title {
  margin: 0;
  font-size: 16px;
  font-weight: 500;
  color: var(--text);
}

.intake-patient__cap {
  margin: 4px 0 0;
  font-size: 14px;
  font-weight: 500;
  color: var(--muted);
}

.intake-patient__pesel input {
  max-width: 240px;
}

.intake-patient__postcode input {
  max-width: 200px;
}

.intake-patient__without {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* "Pacjent nie ma numeru PESEL" swaps the PESEL for the three fields that
   stand in for it, and takes the postcode's format hint with it — a foreign
   address has a postcode of another shape. */
.intake-patient:has(.intake-patient__switch input:checked) .intake-patient__pesel,
.intake-patient:has(.intake-patient__switch input:checked) .intake-patient__postcode .field__hint,
.intake-patient:not(:has(.intake-patient__switch input:checked)) .intake-patient__without {
  display: none;
}

.intake-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}

/* "Kobieta", "Mężczyzna": the two options as pills, the mockup's .opt. */
.intake-sex .field > div {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.intake-sex .field > div label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding: 0 16px;
  font-size: 15px;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-pill);
  cursor: pointer;
}

.intake-sex .field > div label:has(input:checked) {
  color: var(--accent);
  background: var(--tint);
  border-color: var(--accent);
}

.intake-sex input[type="radio"] {
  margin: 0;
  accent-color: var(--accent);
}

/* "Dane stałe" of a later intake: the list with "Zmień", and the fields in
   its place once "Zmień" is open. */
.intake-fixed {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 20px;
  background: var(--bg);
  border-radius: var(--radius-tile);
}

.intake-fixed__top {
  display: flex;
  align-items: baseline;
  gap: 12px;
  min-height: 32px;
  padding-right: 96px;
}

.intake-fixed__title {
  font-size: 16px;
  font-weight: 500;
  color: var(--text);
}

.intake-fixed__meta {
  font-size: 13px;
  color: var(--muted);
}

.intake-fixed .intake-fixed__list {
  margin: 0;
  padding: 0;
  background: transparent;
}

.intake-fixed__more > summary {
  position: absolute;
  top: 16px;
  right: 16px;
  list-style: none;
}

.intake-fixed__more > summary::-webkit-details-marker {
  display: none;
}

.intake-fixed__fields {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.intake-fixed:has(> .intake-fixed__more[open]) {
  padding: 0;
  background: transparent;
}

.intake-fixed:has(> .intake-fixed__more[open]) > .intake-fixed__top,
.intake-fixed:has(> .intake-fixed__more[open]) > .intake-fixed__list,
.intake-fixed__more[open] > summary {
  display: none;
}

.intake-results input[type="file"] {
  margin-top: 4px;
}

.intake-form__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.intake-form__rule {
  margin: 0 0 0 8px;
  font-size: 14px;
  color: var(--muted);
}

/* --- The page of its own --------------------------------------------------- */

.intake-page {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 820px;
}

.intake-page__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 28px;
  line-height: 1.25;
  font-weight: 500;
  color: var(--text);
}

.intake-page__when {
  margin: 0;
  font-size: 15px;
  color: var(--muted);
}

.intake--page {
  margin-top: 8px;
  padding: 24px;
  background: var(--surface);
  border-radius: var(--radius-card);
}

@media (max-width: 600px) {
  .intake-pair {
    grid-template-columns: minmax(0, 1fr);
  }

  .intake-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 0 16px;
  }

  .intake-grid dd + dt {
    margin-top: 8px;
  }

  .intake--page {
    padding: 16px;
  }
}

/* ==========================================================================
   35. Pomiary
   ========================================================================== */

/* The measurements card — panel mockup of 24.09, "Pomiary": the section under
   the visits on the page of a person, and the card of /moje/pomiary/ (section
   38). Four tiles, the chart of the chosen one, "Historia pomiarów". */
.mcard {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 24px;
  background: var(--surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.mcard > .message {
  margin: 0;
}

.mpanel {
  scroll-margin-top: 24px;
}

.mpanel__add {
  margin-left: auto;
  gap: 6px;
}

.mcard__none {
  margin: 0;
  font-size: 15px;
  color: var(--muted);
}

/* Four in a row on the patient's screen; two by two in the narrower column
   of the page of a person. */
.mtiles {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
}

.person .mtiles {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* A tile is a link that changes the chart: a ground of its own, so no
   underline (section 4). */
.mtile {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  padding: 12px 14px;
  color: var(--text);
  text-decoration: none;
  background: var(--hl);
  border-radius: var(--radius-tile);
}

.mtile:hover {
  background: var(--tint);
}

/* The edge drawn inside the tile, so choosing one does not move the others. */
.mtile--on,
.mtile--on:hover {
  background: var(--surface);
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.mtile__label,
.mtile__change {
  overflow: hidden;
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
  text-overflow: ellipsis;
}

.mtile__value {
  font-family: var(--font-serif);
  font-size: 24px;
  line-height: 1.2;
  font-weight: 500;
  white-space: nowrap;
}

.mtile__value--none {
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.9;
  font-weight: 400;
  color: var(--muted);
}

.mtile__unit {
  margin-left: 4px;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 400;
  color: var(--muted);
}

.mtile__change b {
  display: block;
  font-weight: 500;
  color: var(--text);
}

/* The chart: SVG drawn on the server (apps/measurements/board.py). A full
   dot is a specialist's entry, a hollow one the patient's own. */
.mchart {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

/* The patient's screen draws the chart twice: wide, and for the phone's
   column, where the wide one would shrink its lettering past reading. */
.mchart--narrow {
  display: none;
}

.mchart__grid {
  stroke: var(--line);
  stroke-width: 1;
}

.mchart__axis {
  font-family: var(--font-sans);
  font-size: 12px;
  fill: var(--muted);
}

.mchart__line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.mchart__dot {
  fill: var(--accent);
  stroke: var(--surface);
  stroke-width: 2;
}

.mchart__dot--patient {
  fill: var(--surface);
  stroke: var(--accent);
}

.mchart__hit {
  fill: transparent;
}

.mchart__last {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  fill: var(--text);
}

.mchart__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  margin: 6px 0 0;
  font-size: 13px;
  color: var(--muted);
}

.mchart__legend span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.mchart__key {
  width: 10px;
  height: 10px;
  background: var(--accent);
  border-radius: 50%;
}

.mchart__key--patient {
  box-sizing: border-box;
  background: var(--surface);
  border: 2px solid var(--accent);
}

.mchart__none {
  margin: 0;
  padding: 8px 0;
  font-size: 14px;
  color: var(--muted);
}

/* "Historia pomiarów": shut on the page of a person, open on the patient's
   screen. The table scrolls sideways on the phone rather than squeezing. */
.mhist summary {
  font-size: 14px;
  font-weight: 500;
  color: var(--accent);
  cursor: pointer;
}

.mtable__scroll {
  overflow-x: auto;
}

.mtable {
  width: 100%;
  margin-top: 12px;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  border-collapse: collapse;
}

.mtable th {
  padding: 0 8px 8px 0;
  font-size: 13px;
  font-weight: 400;
  color: var(--muted);
  text-align: left;
  white-space: nowrap;
}

.mtable td {
  padding: 8px 8px 8px 0;
  white-space: nowrap;
  border-top: 1px solid var(--line);
}

.mtable__gap,
.mtable__by {
  color: var(--muted);
}

.mtable__action {
  text-align: right;
}

.mtable__edit {
  font-weight: 500;
  color: var(--accent);
  text-decoration: none;
}

.mtable__edit:hover {
  text-decoration: underline;
}

/* The patient's "Usuń": a form of its own beside "Edytuj", its button drawn
   as the link beside it. */
.mtable__delete {
  display: inline;
  margin-left: 12px;
}

.mtable__delete button {
  padding: 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  cursor: pointer;
}

/* The form opens inside the card, above the tiles: the date on a row of its
   own, the height beside the weight, the two girths under them. */
.mform {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--line);
}

.mform .field {
  margin: 0;
}

.mform__date {
  grid-column: 1 / -1;
}

.mform__consent {
  grid-column: 1 / -1;
}

.mform__actions {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

.mform__actions .field__error {
  margin: 0 auto 0 0;
}

/* "Historia zmian" of a specialist's corrected entry, under its form. */
.mversions {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--line);
}

.mversions__title {
  margin: 0;
  font-size: 16px;
  font-weight: 500;
}

.mversions__list {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.mversions__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0;
}

.mversions__name {
  font-weight: 500;
}

.mversions__stamp {
  margin: 2px 0 0;
  font-size: 13px;
  color: var(--muted);
}

@media (max-width: 900px) {
  .mtiles {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .mform {
    grid-template-columns: minmax(0, 1fr);
  }

  .mchart--wide {
    display: none;
  }

  .mchart--narrow {
    display: block;
  }
}

/* ==========================================================================
   36. Zalecenia i pliki wizyty
   ========================================================================== */

/* The sections a visit's documentation adds to an opened visit tile, on the
   specialist's page of a person and on the patient's visits alike: a serif
   title with a pill beside it and the actions on the right, then the content
   (panel mockup of 24.09, "Zalecenia", "Pliki od pacjenta", "Pliki"). */
.visit-doc {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}

/* The landing of static/js/focus.js when the pressed link does not survive its
   own swap, like .intake__region. */
.visit-doc:focus {
  outline: none;
}

.visit-doc__top {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 12px;
  min-height: 38px;
}

.visit-doc__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 18px;
  line-height: 1.3;
  font-weight: 500;
  color: var(--text);
}

.visit-doc__right {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-left: auto;
}

.visit-doc__count {
  font-size: 13px;
  color: var(--muted);
}

/* "Widzi pacjent": who reads the section, in the accent of the patient. */
.visit-doc__who {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 10px;
  font-size: 13px;
  white-space: nowrap;
  color: var(--accent);
  background: var(--tint);
  border-radius: var(--radius-pill);
}

/* The text keeps the specialist's line breaks. */
.visit-doc__text {
  margin: 0;
  font-size: 15px;
  line-height: 1.65;
  white-space: pre-line;
  overflow-wrap: anywhere;
  color: var(--text);
}

/* The patient's copy of the recommendations: the same text on the --hl
   ground of the mockup's recommendation block. */
.visit-doc__rec {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 18px 20px;
  background: var(--hl);
  border-radius: var(--radius-tile);
}

.visit-doc__changed,
.visit-doc__foot,
.visit-doc__note {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

.visit-doc__note {
  font-size: 14px;
}

.visit-doc__foot {
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

.visit-doc__empty {
  margin: 0;
  font-size: 15px;
  color: var(--muted);
}

.visit-doc__form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.visit-doc__textarea {
  min-height: 170px;
  line-height: 1.65;
  resize: vertical;
}

.visit-doc__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.visit-doc__actions p {
  margin: 0 0 0 8px;
  font-size: 14px;
  color: var(--muted);
}

/* "Historia zmian": every version newest first, each one opening in place. */
.visit-doc__history {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.visit-doc__history-title {
  margin: 0 0 2px;
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
}

.doc-version[open] {
  background: var(--hl);
  border-radius: var(--radius-tile);
}

.doc-version__head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  list-style: none;
  border-radius: var(--radius-tile);
  color: var(--muted);
}

.doc-version__head::-webkit-details-marker {
  display: none;
}

.doc-version__head:hover {
  background: var(--hl);
}

.doc-version__main {
  flex: 1;
  min-width: 0;
}

.doc-version__title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  font-weight: 500;
  color: var(--text);
}

.doc-version__meta {
  display: block;
  font-size: 13px;
  color: var(--muted);
}

.doc-version__body {
  padding: 4px 14px 18px;
}

/* The files of a visit: an icon tile, the name as the link, the line under it. */
.visit-files {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
}

.visit-file {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 52px;
  padding: 6px 0;
}

.visit-file + .visit-file {
  border-top: 1px solid var(--line);
}

.visit-file__icon {
  display: grid;
  flex: none;
  place-items: center;
  width: 36px;
  height: 36px;
  color: var(--accent);
  background: var(--hl);
  border-radius: 10px;
}

.visit-file__main {
  flex: 1;
  min-width: 0;
}

.visit-file__name {
  display: block;
  overflow: hidden;
  font-size: 15px;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--accent);
  text-decoration: none;
}

.visit-file__name:hover {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.visit-file__meta {
  display: block;
  font-size: 13px;
  color: var(--muted);
}

/* The picker and "Dodaj plik" on one line: a plain file field and a submit,
   so the upload works without a script. */
.visit-files__add {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
}

.visit-files__input {
  min-width: 0;
  max-width: 100%;
  font-size: 14px;
  color: var(--muted);
}

/* ==========================================================================
   38. Wizyty i pomiary pacjenta
   ========================================================================== */

/* /moje/pomiary/ — the card of section 35 under the heading, as wide as the
   calendar. */
.mine-measurements {
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-width: 0;
}

.mine-measurements__head {
  display: flex;
  align-items: center;
  gap: 12px;
}

.mine-measurements__title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 32px;
  line-height: 1.2;
  font-weight: 500;
}

/* The shortcut beside the patient's visits: the latest weight and the way
   to /moje/pomiary/. */
.msummary {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 24px;
  background: var(--surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.msummary__title {
  margin: 0;
  font-size: 16px;
  font-weight: 500;
}

.msummary__key {
  margin: 12px 0 0;
  font-size: 13px;
  color: var(--muted);
}

.msummary__value {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 28px;
  line-height: 1.2;
  font-weight: 500;
}

.msummary__change {
  margin: 0;
  font-size: 14px;
}

.msummary__none {
  margin: 8px 0 0;
  font-size: 14px;
  color: var(--muted);
}

.msummary__add {
  margin-top: 16px;
}

.msummary__link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 16px;
  font-size: 14px;
  font-weight: 500;
  color: var(--accent);
  text-decoration: none;
}

.msummary__link:hover {
  text-decoration: underline;
}

/* The patient's visits as tiles of one size, the specialist's .visit-panel of
   section 31 with the specialist's initials in front (panel mockup of 24.09,
   "Widok pacjenta"). The list takes the corner and the shadow back from :root
   inside the flat .panel, as .person does. */
.visits__list {
  --radius-card: 16px;
  --shadow-card: 0 1px 2px rgba(11, 46, 72, 0.06);
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* The patient's tile names the specialist and the form on one line, which
   is longer than the specialist's place: both lines wrap instead of cutting
   the date or the name short. */
.patient-visit .visit-panel__when,
.patient-visit .visit-panel__where {
  white-space: normal;
}

.patient-visit__avatar {
  display: grid;
  flex: none;
  place-items: center;
  width: 40px;
  height: 40px;
  font-size: 14px;
  font-weight: 500;
  color: var(--accent);
  background: var(--tint);
  border-radius: 10px;
}

/* ==========================================================================
   39. Wydruk dokumentacji
   ========================================================================== */

/* The card "Dokumentacja" under the visits on the page of a person, the
   mockup's .rowcard: the counts line on the left, the two buttons on the
   right. A button with an icon puts 6 px between the icon and its word. */
.btn--icon {
  gap: 6px;
}

.doc-card {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 24px;
  background: var(--surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

/* The same warning on the card of a person, under its buttons. */
.doc-card + .message {
  margin-top: 12px;
}

.doc-card__line {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: 15px;
  color: var(--text);
}

/* The print page: the bar with the title and the two buttons, then the
   sheet — 820 px wide, the mockup's .print-bar and .sheet. */
.print-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 820px;
  margin-bottom: 20px;
}

/* The warning about missing practice data stands between the bar and the
   sheet, as wide as both. */
.print-warning {
  max-width: 820px;
  margin-bottom: 20px;
}

/* "Drukuj" ships with the `hidden` attribute and static/js/print.js uncovers
   it. The attribute alone does not hold: .btn carries a display of its own and
   an author rule outranks the browser's own [hidden], so with no JavaScript the
   button would stand there doing nothing. Section 24 answers the same thing the
   same way for the strength meter. Scoped to the bar so it reaches nothing
   else. */
.print-bar [hidden] {
  display: none;
}

.print-bar__title {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-family: var(--font-sans);
  font-size: 22px;
  font-weight: 500;
  line-height: 1.3;
  color: var(--text);
}

.sheet {
  max-width: 820px;
  padding: 56px 64px 64px;
  font-size: 14px;
  line-height: 1.55;
  color: var(--text);
  background: var(--surface);
  border-radius: 6px;
  box-shadow: var(--shadow-raised);
}

.sheet p {
  margin: 0;
}

.sheet__head {
  display: flex;
  justify-content: space-between;
  gap: 24px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--text);
}

.sheet__org {
  font-size: 15px;
  font-weight: 600;
}

.sheet__meta {
  color: var(--muted);
  text-align: right;
}

.sheet__title {
  margin: 28px 0 12px;
  font: 500 24px/1.25 var(--font-serif);
  color: var(--text);
}

/* The patient's grid, the intake's .intake-grid without its tile. */
.sheet__patient {
  display: grid;
  grid-template-columns: 170px minmax(0, 1fr);
  gap: 6px 16px;
  margin: 0;
}

.sheet__patient dt,
.sheet__dl dt {
  color: var(--muted);
}

.sheet__patient dd,
.sheet__dl dd {
  margin: 0;
  overflow-wrap: anywhere;
}

.sheet__visit {
  margin-top: 28px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  break-inside: avoid;
}

.sheet__visit h3 {
  margin: 0 0 4px;
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 600;
  color: var(--text);
}

.sheet__visit h4 {
  margin: 16px 0 6px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.sheet__dl {
  display: grid;
  grid-template-columns: 130px minmax(0, 1fr);
  gap: 6px 16px;
  margin: 0;
}

.sheet__dl dd,
.sheet__text {
  white-space: pre-line;
}

.sheet__sign {
  margin-top: 8px;
  font-size: 12.5px;
  color: var(--muted);
}

.sheet__rec .sheet__sign {
  margin: 0 0 2px;
}

@media (max-width: 900px) {
  .doc-card {
    flex-wrap: wrap;
  }

  .doc-card__line {
    flex-basis: 100%;
  }

  .print-bar {
    flex-wrap: wrap;
  }

  .print-bar__title {
    flex-basis: 100%;
  }

  .sheet {
    padding: 28px 20px 32px;
  }

  .sheet__head {
    flex-direction: column;
    gap: 12px;
  }

  .sheet__meta {
    text-align: left;
  }

  .sheet__patient,
  .sheet__dl {
    grid-template-columns: minmax(0, 1fr);
    gap: 2px 0;
  }

  .sheet__patient dd,
  .sheet__dl dd {
    margin-bottom: 6px;
  }
}

/* On paper only the sheet: the site's chrome, the panel's menu and the
   print bar are not part of the record. */
@media print {
  @page {
    size: A4;
    margin: 16mm;
  }

  body {
    min-width: 0;
    background: #fff;
  }

  .skip-link,
  .site-header,
  .blocked-banner,
  .deletion-banner,
  .panel-nav,
  .panel-foot,
  .person__back,
  .print-bar,
  .print-warning {
    display: none !important;
  }

  .panel {
    padding: 0;
  }

  .panel__grid {
    display: block;
    max-width: none;
  }

  .sheet {
    max-width: none;
    padding: 0;
    border-radius: 0;
    box-shadow: none;
  }
}


/* ==========================================================================
   Signing in through Google: the button and the "albo" rule
   ========================================================================== */

/* Google's brand guidelines for a custom button, light theme: white ground,
   1 px #747775 outline, #1F1F1F text in Roboto Medium 14/20, the "G" in its own
   colours, 12 px before the logo, 10 px after it and 12 px at the end. The
   colours are Google's and not tokens of ours, which is why they are written
   out here. Height and radius are the "Zaloguj się" button's, so the two read
   as a pair in one column. */
.google-button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 44px;
  padding: 0 12px;
  font-family: "Roboto", var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  line-height: 20px;
  color: #1F1F1F;
  background: #FFFFFF;
  border: 1px solid #747775;
  border-radius: var(--radius-button);
  cursor: pointer;
}

.google-button:hover {
  background: #F8F9FA;
}

.google-button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.google-button__logo {
  flex: none;
  width: 18px;
  height: 18px;
  margin-right: 10px;
}

.google-form {
  margin: 0;
}

/* "albo" between the two ways in: a rule on both sides of the word, in the
   colour of the field borders, the word in the colour of field notes. */
.auth-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 14px;
  color: var(--muted);
}

.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--line-control);
}

/* After Google, frame 2b's specialist card starts the trip again (the surname
   is asked for only there), so it is a button: the same card, without what a
   browser draws on a button by default. */
.path-choice__form {
  display: flex;
}

button.path-card {
  width: 100%;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

/* ==========================================================================
   40. Podpowiedzi miejscowości i adresu
   ========================================================================== */

/* The town and office-address fields of the specialist forms
   (apps/catalog/place_fields.py, static/js/place-suggest.js). The list hangs
   under the box at its full width and over what follows, so opening it does
   not push the form down. Five rows at most; where the window has no room
   for them under the box, the script caps the rows' height and they scroll
   inside the popup, with the credit standing still beneath them. */
.place-field {
  position: relative;
}

.place-field__popup {
  display: flex;
  flex-direction: column;
  position: absolute;
  top: 100%;
  right: 0;
  left: 0;
  z-index: 20;
  margin-top: 4px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-field);
  box-shadow: var(--shadow-raised);
  overflow: hidden;
}

.place-field__popup[hidden] {
  display: none;
}

.place-field__list {
  margin: 0;
  padding: 4px 0;
  overflow-y: auto;
  list-style: none;
}

.place-field__option {
  padding: 10px 14px;
  font-size: 15px;
  line-height: 1.4;
  color: var(--text);
  overflow-wrap: anywhere;
  cursor: pointer;
}

.place-field__option:hover,
.place-field__option[aria-selected="true"] {
  background: var(--hl);
}

/* The sentence when nothing matches: a row of the list, not a choice. */
.place-field__option--note,
.place-field__option--note:hover {
  color: var(--muted);
  background: none;
  cursor: default;
}

/* The credit Geoapify's free plan and OpenStreetMap ask for, under the rows. */
.place-field__attribution {
  margin: 0;
  padding: 6px 14px 8px;
  font-size: 13px;
  line-height: 1.4;
  color: var(--muted);
  border-top: 1px solid var(--line);
}

.place-field__attribution a {
  color: inherit;
}

/* Without a script: the towns of one name, to choose one, under the box. */
.place-field__picks {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 12px;
}

.place-field__picks .place-field__attribution {
  padding: 0;
  border-top: 0;
}

/* The same credit under the patient's town box, in sight without a script. */
.place-field__credit .place-field__attribution {
  padding: 4px 0 0;
  border-top: 0;
}

/* The patient's town box (templates/catalog/partials/patient_place_field.html).
   In the rail of 1c it sits in a .field and is dressed by its rule like the
   select was. In the search of 6a it takes the select's own dress: no border
   and no background, so the band keeps the one border around both controls
   and the button. */
.place-field--town {
  width: 100%;
}

/* A town's row is its name, county and voivodeship, all three needed to
   tell two towns of one name apart. At the width of the box (190 px in the
   search of 6a) each row broke into three lines and five rows ran off the
   screen, so on a desktop the list takes 400 px from the box's left edge,
   never closer than 16 px to the window's. Where the search turns into a
   column (900 px and below) the box is wide enough and the list keeps its
   width. */
.place-field--town .place-field__option {
  padding: 12px 16px;
}

@media (min-width: 901px) {
  .place-field--town .place-field__popup {
    right: auto;
    width: max(100%, 400px);
    max-width: calc(100vw - 32px);
  }
}

/* The box is a 44 px target with 12 px of room each side, so the focus ring
   does not touch the text. The negative margin keeps the text under the
   label, as the select's text beside it is. */
.home-search__field .place-field--town {
  width: calc(100% + 24px);
  margin: 0 -12px;
}

.home-search__field .place-field__box {
  width: 100%;
  min-width: 0;
  min-height: 44px;
  padding: 0 12px;
  font-family: inherit;
  font-size: 16px;
  color: var(--text);
  background: none;
  border: none;
  border-radius: var(--radius-field);
}

.home-search__field .place-field__box:focus-visible {
  outline-offset: 0;
}

/* After a typed name was refused: the sentence under the box, in the voice
   of a field error, and the towns it could mean as links. */
.place-field__note {
  margin-top: 6px;
}

.place-field__sentence {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--alert);
}

.place-field__links {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 8px 0 0;
  padding: 0;
  font-size: 15px;
  line-height: 1.4;
  list-style: none;
}
