/* hard-paywall funnel — O2 (quiz) and O3 (computing moment), page rules only.
 *
 * Source: handoff/design/2026-07-30-hard-paywall-funnel/claude-design-output-v2
 *         README § O2 / § O3 + screens/o2-quiz.html, screens/o3-computing.html.
 *
 * Everything shared (tokens, .btn, .field, .tile, .pill, .prog, .scta dock,
 * .cband, .od digit roll, the reduced-motion reset) lives in hp.css and is NOT
 * repeated here. This file only carries what is specific to these two screens,
 * plus the handful of overrides the production port needs that the static
 * prototype did not: the tiles and pills became real radios inside labels, so
 * their selected and focus states have to come from :checked rather than a JS
 * class.
 *
 * Scoped under .hp exactly like hp.css: the class names (.top, .calc, .helper)
 * are generic enough to collide with uni.css and the Tailwind build.
 *
 * Mobile redesign 2026-08-01 (handoff/design/2026-08-01-mobile-redesign):
 * both screens gave their bottom edge to the shared dock, so the sticky
 * counter band, the in-column `.next` row and the computing `.skiprow` are all
 * gone from here. What replaced them lives in hp.css, because the teaser and
 * the paywall render the same component.
 */

/* ── O2 · the funnel column (D4: 680, gutter 40 / 20 under 640) ──
   The column used to be a flex child stretched to `100vh - 64px` so the sticky
   counter band landed on the bottom edge even on a short step. ON A PHONE the
   band is gone (the dock owns that edge now), and stretching without it only
   bought grey: the page stayed a viewport tall, the dock reservation was added
   on top, and the extra scroll revealed nothing. Content height plus the
   dock's own reservation is the whole page there (audit B1).

   That was written as though it were true at every width, and the stretch was
   deleted at every width to match. It was not: at 1280 the deletion pulled the
   site footer above the fold on the short steps. The `@media (min-width:641px)`
   block below puts `.hp.hp-quiz`'s flex column and min-height back for the
   desktop only. If you are here to delete a viewport-filling rule again, check
   which width you are deleting it for (2026-08-02 visual diff, 11.2). */
.hp .qz {
  max-width: 680px;
  width: 100%;
  margin-inline: auto;
  padding: 48px 40px 40px;
}

/* ── step header ── */
.hp .qz .top {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-bottom: 40px;
}
.hp .qz .back {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 14px;
  font-weight: 600;
  color: var(--hp-ink-2);
  /* The prototype lets "← Zpět" wrap onto two lines at this measure; it is a
     two-word label and must stay one. */
  white-space: nowrap;
}
.hp .qz .back:hover {
  color: var(--hp-navy);
}
/* Both the bar and the "2 ze 3 · Typ kola" label are flex:1 in the prototype,
   so they split the row evenly and the label keeps trailing space after it.
   Making the label flex:none would stretch the bar by ~140px at 1728. */
.hp .qz .top > .prog {
  flex: 1;
}
.hp .qz .top > .pnum {
  /* "3 ze 3 · Rozpočet" is one label and must never break across two lines. */
  white-space: nowrap;
}

/* h1.step-h, not .step-h: tokens.css ships :root[data-ui-mode="new"] h1 with
   var(--sans) at (0,2,1), which out-specifies hp.css's own `.hp h1` display-font
   rule. Without the element in the selector every funnel H1 silently renders in
   Inter instead of Space Grotesk. (Reported upstream — the shared rule needs the
   same treatment or every funnel page has this bug.) */
.hp .qz h1.step-h {
  font-family: var(--hp-disp);
  font-size: 36px;
  font-weight: 800;
  /* -.02em, matching the rendered prototype the screenshots came from. The
     README token table says -.03em; where the two disagree the screens win,
     because they are what was reviewed and signed off. */
  letter-spacing: -0.02em;
  margin: 0 0 8px;
}
.hp .qz .helper {
  color: var(--hp-ink-3);
  font-size: 15px;
  margin: 0 0 30px;
}

/* ── step 1 · height as ONE control (screen 08) ──
   Number, track and note in a single card. Before, the field and the slider
   were two controls in a row that also carried the submit button; the button
   has moved to the dock and what is left is one question with one answer. */
.hp .qz .hcard {
  background: var(--hp-card);
  border: 1px solid var(--hp-line);
  border-radius: var(--hp-r);
  box-shadow: var(--hp-sh);
  padding: 24px 20px;
  display: grid;
  gap: 18px;
  margin-top: 20px;
  text-align: center;
}
/* The number IS the control: a real input, styled as the 52px display value
   rather than boxed in a field, so tapping it opens the numeric keypad. */
.hp .qz .hval {
  font-family: var(--hp-disp);
  font-size: 52px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  margin: 0;
}
.hp .qz .hval input {
  border: 0;
  outline: 0;
  background: none;
  font: inherit;
  /* 3ch, not a percentage: the field holds at most three digits and the card
     is centred, so the caret sits where the number ends. */
  width: 3ch;
  text-align: right;
  color: var(--hp-ink);
  padding: 0;
}
.hp .qz .hval input:focus-visible {
  outline: 2px solid var(--hp-navy);
  outline-offset: 4px;
  border-radius: 4px;
}
.hp .qz .hval .unit {
  font-family: var(--hp-sans);
  font-size: 18px;
  font-weight: 500;
  color: var(--hp-ink-4);
  margin-left: 4px;
  letter-spacing: normal;
}
.hp .qz .hslot {
  display: grid;
  gap: 6px;
}
.hp .qz .hslot[hidden] {
  display: none;
}
.hp .qz .hminmax {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--hp-ink-4);
}
.hp .qz .hnote {
  font-size: 13px;
  color: var(--hp-ink-3);
  margin: 6px 0 0;
}

/* The height slider (owner feedback 2026-07-31 #2). Visually the same control
   as the landing's `.hslider` (landing-pf.css), which is the one the owner
   remembers, restated in funnel tokens rather than imported: that file is the
   old design system and this screen must not start depending on it -- the
   same class of coupling the `.seg` leak came from. `--p` is written by hp.js
   and fills the track behind the thumb; the 50% here is only what the control
   looks like for the instant before the script runs.

   The track is drawn as the element's own background rather than on
   `::-webkit-slider-runnable-track`, so one gradient serves both engines and
   the phone's 44px touch height can pad it with `background-clip: content-box`
   without the track growing with it. */
.hp .qz .hrange {
  --p: 50%;
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 6px;
  margin: 0;
  border-radius: 999px;
  background: linear-gradient(
    to right,
    var(--hp-navy) 0 var(--p),
    var(--hp-line-2) var(--p) 100%
  );
  outline: none;
  cursor: pointer;
}
/* 28px thumb with a 2px navy ring (screen 08). The border used to be 7px,
   which drew a navy disc rather than a ring. */
.hp .qz .hrange::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--hp-navy);
  box-shadow: var(--hp-sh-2);
  cursor: grab;
}
.hp .qz .hrange::-moz-range-thumb {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--hp-navy);
  box-shadow: var(--hp-sh-2);
  cursor: grab;
}
.hp .qz .hrange:focus-visible::-webkit-slider-thumb,
.hp .qz .hrange:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 3px #fff, 0 0 0 6px var(--hp-navy-soft-2);
}

/* ── steps 2 and 3 · selection state.
   Tiles and pills wrap a visually-hidden radio so the quiz works with no
   JavaScript and keyboard selection is the browser's. That means :checked,
   not a .on class, drives the selected look — same declarations as hp.css's
   .tile.on / .pill.on so the two states cannot drift apart. ── */
/* The c84feffe declarations, unchanged and UNCONDITIONAL, because desktop
   keeps them (Codex final review finding 5, 2026-08-02). The S2 phone variant
   lives in the `@media (max-width: 640px)` block at the foot of this file. */
.hp .tiles .tile:has(input:checked) {
  border-color: var(--hp-navy);
  box-shadow: 0 0 0 1px var(--hp-navy) inset;
  background: var(--hp-navy-soft);
}
.hp .tiles .tile:focus-within {
  border-color: var(--hp-navy);
  box-shadow: 0 0 0 3px var(--hp-navy-soft);
}
.hp .tile.off {
  cursor: default;
}
.hp .tile .prep {
  margin-left: auto;
  border-style: dashed;
}

.hp .pillrow {
  margin-top: 4px;
}
.hp .pillrow .pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* "do 30 000" is one answer and must read as one line. */
  white-space: nowrap;
}
/* Again the c84feffe declarations, unchanged and UNCONDITIONAL: the solid navy
   fill with white text is what the DESKTOP budget pill has always looked like
   and desktop is the frozen etalon. The S2 phone variant (navy-soft ground,
   2px inset ring, navy text — audit C1, "presne jedno navy vyplnenie na
   obrazovku") lives in the `@media (max-width: 640px)` block at the foot of
   this file. */
.hp .pillrow .pill:has(input:checked) {
  background: var(--hp-navy);
  border-color: var(--hp-navy);
  color: #fff;
}
.hp .pillrow .pill:focus-within {
  border-color: var(--hp-navy);
  box-shadow: 0 0 0 3px var(--hp-navy-soft);
}

/* ══ DESKTOP RESTORATION (Codex P1 / Kimi #4, 2026-08-01) ══════════════════
   The dock is a PHONE control. `.hp .scta` is `position:fixed` at every width,
   so shipping it unqualified moved the quiz's primary action out of the column
   and put a new fixed bar (plus 98px of body reservation) on the computing
   screen, which had no bottom bar at all before. Measured before/after at
   1280x900 on a live page:

     quiz 1/2/3   in-column CTA -> none;   bottom band 52px sticky -> 81px fixed
     computing    no bottom bar at all -> 81px fixed bar + 98px body padding

   The handoff's first line is "Desktop sa NEMENÍ a zostáva etalónom" and
   ARCHITECTURE § 0.1 restates it as a binding ground rule; WP6 read the same
   contract the same way and hid the paywall's dock from 641px up. So above
   640px the column gets its controls back and the dock stands down: on the
   quiz to its old counter-band shape, on computing entirely.

   Everything the dock's mechanism keys on is untouched — the markup, the `on`
   class, `[data-hp-sticky]`, `--hp-dock-h`, `--hp-scta-h` and the consent
   stacking all behave exactly as before. This block only decides which of the
   two controls is DISPLAYED, and hp.css's reservation follows the measured
   height on its own. ══ */
.hp .qz .next {
  margin-top: 34px;
  display: flex;
  justify-content: flex-end;
}
.hp .calc .skiprow {
  margin-top: 34px;
}
.hp .calc .skip {
  display: inline-flex;
  align-items: center;
  font-size: 14px;
  font-weight: 600;
  color: var(--hp-ink-4);
}
.hp .calc .skip:hover {
  color: var(--hp-navy);
}
.hp .calc .skip .arr {
  margin-left: 7px;
}
@media (min-width: 641px) {
  /* ── the quiz page fills the viewport again ──
     `.hp.hp-quiz { display:flex; flex-direction:column; min-height: calc(100vh
     - 64px) }` was DELETED by the redesign rather than moved into the phone
     query, and deleting it changed the desktop. Measured at 1280x800 on step
     1, which is the shortest step: the page was 549px tall and the site
     footer's top edge sat at y=614 — above the fold, so a desktop visitor
     answering the first question was looking at the footer. The baseline at
     c84feffe is 736px with the footer at y=801, one pixel below the fold.

     Restored here rather than at the top level because on a phone the delete
     was correct and deliberate (audit B1: the page stayed a viewport tall, the
     dock reservation was added on top, and the extra scroll revealed nothing).
     Desktop only, which is the whole point of this block. */
  .hp.hp-quiz {
    display: flex;
    flex-direction: column;
    min-height: calc(100vh - 64px);
  }
  /* …and the column that consumes the slack, which is the other half of the
     same c84feffe rule and was missed the first time (Codex final review
     finding 5, 2026-08-02). Without `flex: 1 0 auto` the column does not grow,
     so the band it is supposed to push to the bottom edge floats directly
     under the last answer with the page's spare height below it. The 72px
     bottom gutter is the baseline's too: measured at 1280 the column had
     shipped 40px. */
  .hp .qz {
    flex: 1 0 auto;
    padding-bottom: 72px;
  }

  /* ── step 1 · the desktop control, restored ──
     The number+slider card (screen 08) is the PHONE treatment and shipped at
     every width, so above 640px the step lost the `field + inline button` row
     it had at c84feffe. Measured at 1280x800: a 423x52 field with the button
     beside it at x=775 became a 600x197 card with the button 178px below it,
     and the slider gained a min/max scale and a "drag me" note that the
     desktop never had.

     ONE markup, two layouts. `.hcard` is `display:contents` here, so the value
     and the slider slot become flex items of the form itself, alongside the
     `.next` button — which quiz.html renders INSIDE this form for exactly this
     reason. That reproduces the baseline row: field and button on line one,
     slider full width on line two, and the phone keeps the card untouched
     because none of this exists below 641px.

     `.ask` is the pre-redesign class name for this row and is the step-1
     marker; nothing else in any stylesheet claims it. */
  .hp .qz .ask {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 12px;
  }
  .hp .qz .ask .hcard {
    display: contents;
  }
  /* The value goes back to being `.field` (hp.css:207) rather than the 52px
     display number. Restated instead of adding the class in the template: the
     phone's card must keep reading `.hval`, and one element cannot be both. */
  .hp .qz .ask .hval {
    order: 1;
    flex: 1;
    display: flex;
    align-items: center;
    gap: 10px;
    background: #fbfbfc;
    border: 1px solid var(--hp-line-2);
    border-radius: var(--hp-rb);
    padding: 0 16px;
    height: 52px;
    font-family: var(--hp-sans);
    font-size: 16px;
    font-weight: 400;
    letter-spacing: normal;
    text-align: left;
  }
  .hp .qz .ask .hval:focus-within {
    border-color: var(--hp-navy);
    background: #fff;
    box-shadow: 0 0 0 3px var(--hp-navy-soft);
  }
  .hp .qz .ask .hval input {
    flex: 1;
    width: auto;
    min-width: 0;
    text-align: left;
    font-weight: 600;
  }
  /* The card's focus ring is drawn on the input; the field draws it on the box
     (`:focus-within` above), and two rings is one more than the baseline had. */
  .hp .qz .ask .hval input:focus-visible {
    outline: 0;
  }
  .hp .qz .ask .hval .unit {
    font-size: 14px;
    margin-left: 0;
  }
  .hp .qz .ask .next {
    order: 2;
    margin-top: 0;
  }
  .hp .qz .ask .hslot {
    order: 3;
    flex: 1 0 100%;
  }
  /* The scale and the note are the card's second row, written for a thumb on a
     44px track. The baseline desktop had a bare 6px slider under the row and
     nothing else, and "Desktop sa NEMENÍ" covers additions as well as moves.
     `margin: 6px 0 0` is the baseline's own offset. */
  .hp .qz .ask .hminmax,
  .hp .qz .ask .hnote {
    display: none;
  }
  .hp .qz .ask .hrange {
    margin: 6px 0 0;
  }

  /* The quiz dock reverts to what it was before the redesign: the counter
     band. `.cband`'s own metrics, restated on `.scta` — 14px centred text in a
     1400px column, plain white, the lighter band shadow — so the bottom edge
     at 1280 is the 52px band it always was rather than an 81px CTA bar.
     Specificity: `.hp.hp-quiz .scta .in` is (0,4,0) against `.hp .scta .in`'s
     (0,2,0), so this wins outright rather than by source order.

     POSITION TOO (Codex final review finding 5, 2026-08-02). Restating the
     band's paint but leaving `.hp .scta`'s `position: fixed` in place left the
     desktop with a bar welded to the viewport bottom on a page that used to
     end with a band welded to the DOCUMENT bottom: it covered the last 49px of
     the step at every scroll offset and never let the site footer through.
     `.cband` was `position: sticky; bottom: 0; z-index: 5` and `.scta` is the
     same direct child of `.hp.hp-quiz` that `.cband` used to be, so with the
     column's `flex: 1 0 auto` restored above, sticky puts it back on exactly
     the same edge. `transform`/`transition` are the reveal animation of a dock
     that no longer reveals anything here, and `padding-bottom` is the phone's
     safe-area inset — both zeroed so the desktop band measures what it
     measured before. */
  .hp.hp-quiz .scta {
    position: sticky;
    left: auto;
    right: auto;
    bottom: 0;
    z-index: 5;
    background: #fff;
    backdrop-filter: none;
    box-shadow: 0 -6px 18px rgba(16, 24, 40, 0.04);
    padding-bottom: 0;
    transform: none;
    transition: none;
  }
  .hp.hp-quiz .scta .in {
    max-width: 1400px;
    justify-content: center;
    gap: 14px;
  }
  .hp.hp-quiz .scta .ctx {
    font-size: 14px;
    /* `.cband .in` declared no line-height, so it inherited `.hp`'s 1.55; the
       dock's 1.45 is the phone value and it made the restored band 49px against
       the baseline's ~51. Two pixels, and the point of a freeze is that two
       pixels are not a rounding error. */
    line-height: 1.55;
  }
  .hp.hp-quiz .scta .btn.lg {
    display: none;
  }
  /* …and with the band back IN the document flow, the body must stop reserving
     room for a dock that is no longer overlapping anything. hp.css's
     `body:has(.hp.has-dock)` pair is written for the phone's fixed dock; at
     1280 it was adding 65px of blank page under the footer that c84feffe never
     had. Same shape as the `.hp-calc` zeroing below. */
  body:has(.hp.hp-quiz.has-dock) {
    padding-bottom: 0;
  }
  :root.cookie-banner-open body:has(.hp.hp-quiz.has-dock) {
    padding-bottom: 0;
  }
  /* R3's consent reservation, restored to the two rules c84feffe had. The band
     is bottom-stuck, so with the strip up it is LIFTED rather than padded, and
     the column pays the extra 24px gutter above it. hp.css's
     `.hp.has-dock{padding-bottom:0}` (0,2,0) would otherwise zero the column;
     these are (0,3,0) / (0,4,0). */
  :root.cookie-banner-open .hp.hp-quiz > .scta {
    bottom: var(--hp-cookie-safe, 200px);
  }
  :root.cookie-banner-open .hp.hp-quiz .qz {
    padding-bottom: calc(var(--hp-cookie-safe, 200px) + 24px);
  }
  /* Computing had NO bottom bar before the redesign. Hiding the dock leaves
     `--hp-dock-h` published as 0 by hp.js (it measures the element, and a
     display:none element measures 0), so the body reservation collapses to the
     16px gutter on its own — but the fallback in
     `body:has(.hp.has-dock){padding-bottom:calc(var(--hp-dock-h,128px)+16px)}`
     would still reserve 144px in the frame before hp.js runs and in a no-JS
     render, so it is zeroed here too. Same specificity as hp.css's rule
     (0,2,1); this sheet loads after it. */
  .hp.hp-calc .scta {
    display: none;
  }
  body:has(.hp.hp-calc.has-dock) {
    padding-bottom: 0;
  }
  :root.cookie-banner-open body:has(.hp.hp-calc.has-dock) {
    padding-bottom: 0;
  }
  /* …and with the dock gone, the screen is the pre-redesign one again: a full
     viewport-height centring box with 40px of gutter, reserving for the
     consent strip the way every other funnel screen does. hp.css's
     `.hp.has-dock{padding-bottom:0}` (0,2,0) zeroes that; these are (0,3,0)
     and (0,4,0). The min-height subtraction is undone because there is no dock
     left below the fold to subtract. */
  .hp.hp-calc.has-dock {
    min-height: calc(100vh - 64px);
    padding-bottom: 40px;
  }
  :root.cookie-banner-open .hp.hp-calc.has-dock {
    min-height: calc(100vh - 64px);
    padding-bottom: calc(var(--hp-cookie-safe, 200px) + 24px);
  }
}

/* ── step 2 · the category notify capture (fade-up 200 ms) ── */
.hp .qz .notify {
  margin-top: 14px;
}
.hp .qz .notify.in {
  animation: hp-notify-in 0.2s ease-out;
}
@keyframes hp-notify-in {
  from {
    opacity: 0;
    translate: 0 4px;
  }
}

/* ── cookie-consent safe area (design ruling §13b R3) ──
   R3 is a RESERVATION, not a z-index fight: raising anything above the consent
   bar would bury the one control on the page that must stay reachable.

   Both of this file's overrides are GONE, and neither was replaced here:

   * the counter band is no longer on this screen, so there is nothing left to
     lift off the bottom edge;
   * `hp.css` reserves the dock (and, with the strip up, the whole dock+strip
     stack) on BODY — `body:has(.hp.has-dock)` and its `.cookie-banner-open`
     variant, hp.css:748-757. Its `.hp.has-dock{padding-bottom:0}` pair is the
     RESET that keeps the column from reserving the same room a second time,
     not the reservation itself; pointing a comment at it is how an editor ends
     up changing the wrong rule (Kimi #3-14). The quiz column adding its own
     second reservation on top is what made the step scroll past ~300px of
     nothing.
     The desktop restoration block above overrides the BODY rules for
     `.hp-calc`, because above 640px that screen has no dock to reserve for.

   The one thing this file still has to do is stay out of the way: `.hp.hp-calc`
   below deliberately declares no `padding-bottom`, because a shorthand here
   would out-order hp.css's reservation (equal specificity, later sheet). */

/* ── O3 · the computing moment (column 600, vertically centred) ── */
.hp.hp-calc {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Centre in the space the reader can actually SEE. The box used to be the
     whole viewport below the header, so half the slack went above the H1 and
     the other half went under the dock: measured at 390x844 that put a 246px
     band over the heading on the one screen whose job is to look like it is
     working. Subtracting the dock (and the whole dock+strip stack while the
     consent bar is up) splits the slack evenly above and below. */
  min-height: calc(100vh - 64px - var(--hp-dock-h, 128px));
  padding-top: 40px;
  padding-inline: 40px;
}
:root.cookie-banner-open .hp.hp-calc {
  min-height: calc(100vh - 64px - var(--hp-cookie-safe, 200px));
}
/* ══ 2026-08-06 redesign ═════════════════════════════════════════════════
   Source: handoff/design/2026-08-06-claude-design-output/POCITAME.md +
   `Pocitame Loading.html` / `pocitame.css` (all 8 states, desktop 1728 +
   mobile 390, live animations). Values (colour, spacing, timing) are taken
   from that reference exactly; markup and class names are this codebase's
   own (the handoff HTML is a design reference, not production code).

   The escape hatch (`.skiprow` / the dock) is UNCHANGED from the prior
   iteration and still styled below this block. ══ */

/* 680, matching hp.css's own `.wrap-f` funnel-column measure (D4) — the
   pre-redesign 600 was this screen's own one-off, not a shared convention. */
.hp .calc {
  width: 680px;
  max-width: 100%;
}

.hp .calc h1 {
  font-family: var(--hp-disp);
  font-weight: 600;
  font-size: 44px;
  letter-spacing: -0.025em;
  line-height: 1.05;
  margin: 18px 0 0;
}
.hp .calc h1 .ell {
  margin-left: 1px;
}
/* The dots only belong to "Počítáme…" — "Máme to." and the error headline
   are full stops, not open-ended. */
.hp .calc:not([data-hp-state="running"]) h1 .ell {
  display: none;
}

.hp .calc .knownline {
  margin-top: 10px;
  font-size: 12.5px;
  color: var(--hp-ink-4);
  font-variant-numeric: tabular-nums;
}
.hp .calc .knownline b {
  color: var(--hp-ink-3);
  font-weight: 600;
}
.hp .calc:not([data-hp-state="running"]) .knownline {
  display: none;
}

/* ── steps ── */
.hp .calc .steps {
  margin-top: 34px;
  border-top: 1px solid var(--hp-line-2);
  display: flex;
  flex-direction: column;
}
.hp .calc .step {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 19px 2px;
  border-bottom: 1px solid var(--hp-line);
  animation: hp-row-in 0.24s cubic-bezier(0.2, 0.7, 0.3, 1) both;
}
.hp .calc .step:nth-child(2) {
  animation-delay: 0.08s;
}
.hp .calc .step:nth-child(3) {
  animation-delay: 0.16s;
}
@keyframes hp-row-in {
  from {
    opacity: 0;
    translate: 0 7px;
  }
}
.hp .calc .step .sic {
  width: 24px;
  height: 24px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: -1px;
}
.hp .calc .step .sic .ic {
  display: none;
}
.hp .calc .step[data-step-state="wait"] .ic-wait,
.hp .calc .step[data-step-state="live"] .ic-live,
.hp .calc .step[data-step-state="done"] .ic-done,
.hp .calc .step[data-step-state="fail"] .ic-fail {
  display: flex;
  align-items: center;
  justify-content: center;
}
/* waiting: a plain 20px outline circle */
.hp .calc .step .ic-wait {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid var(--hp-line-2);
}
/* live: the ring spinner. Border colour is the design's own `--navy-line`
   (#cfe0f1) with a navy top — not one of hp.css's shared tokens, so it is a
   literal value scoped to this one spinner rather than a new shared custom
   property. */
.hp .calc .step .ic-live.ring {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2.5px solid #cfe0f1;
  border-top-color: var(--hp-navy);
  animation: hp-spin 0.9s linear infinite;
}
@keyframes hp-spin {
  to {
    transform: rotate(360deg);
  }
}
/* done: 22px filled green circle, white check, pop-in */
.hp .calc .step .ic-done {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--hp-green);
  color: #fff;
  animation: hp-pop-in 0.32s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
.hp .calc .step .ic-done svg {
  width: 11px;
  height: 11px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
@keyframes hp-pop-in {
  0% {
    transform: scale(0.4);
    opacity: 0;
  }
  70% {
    transform: scale(1.08);
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}
/* fail: amber soft circle + amber line border + warning glyph — NEVER the
   green check. A step that did not answer is not a step that finished. */
.hp .calc .step .ic-fail {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--hp-amber-soft);
  border: 1px solid #f0dcc0;
  color: var(--hp-amber);
}
.hp .calc .step .ic-fail svg {
  width: 12px;
  height: 12px;
}

.hp .calc .step .stx {
  flex: 1;
  min-width: 0;
  font-size: 16.5px;
  line-height: 1.4;
  color: var(--hp-ink-2);
}
.hp .calc .step .stx .num {
  font-weight: 600;
  color: var(--hp-ink);
  font-variant-numeric: tabular-nums;
  display: inline-block;
}
.hp .calc .step .stx .txt {
  display: none;
}
.hp .calc .step[data-step-state="wait"] .stx {
  color: var(--hp-ink-5);
}
/* Waiting AND fail both show the SAME plain sentence live uses — waiting as
   a muted preview of what this step is about to say, fail as the neutral
   statement of what it WAS trying to do (design's own frames: `.step--wait`
   and `.step--fail` both render the plain text, unshimmered, no dots;
   `.step--fail` additionally carries the small explanation below it). */
.hp .calc .step[data-step-state="wait"] .txt-live,
.hp .calc .step[data-step-state="live"] .txt-live,
.hp .calc .step[data-step-state="fail"] .txt-live,
.hp .calc .step[data-step-state="done"] .txt-done,
.hp .calc .step[data-step-state="fail"] .txt-fail {
  display: block;
}
.hp .calc .step[data-step-state="live"] .txt-live {
  font-weight: 600;
  color: var(--hp-ink-2);
}
/* Wait AND fail: no shimmer sweep, no trailing dots -- those are what "this
   is happening right now" looks like, and neither state is that (wait not
   yet, fail not any more). */
.hp .calc .step[data-step-state="wait"] .txt-live .shim,
.hp .calc .step[data-step-state="fail"] .txt-live .shim {
  background: none;
  color: inherit;
  -webkit-background-clip: initial;
  background-clip: initial;
  animation: none;
}
.hp .calc .step[data-step-state="wait"] .txt-live .ell,
.hp .calc .step[data-step-state="fail"] .txt-live .ell {
  display: none;
}
/* Wait only: the count itself is not bold either -- it is a preview of a
   number the fail state, by contrast, already genuinely knows (step 1's
   category count is resolved before any phase runs at all, independent of
   whether that phase's OWN round trip later fails). */
.hp .calc .step[data-step-state="wait"] .txt-live .num {
  color: inherit;
  font-weight: 400;
}
/* Shimmer: gradient sweep across the live text, background-clip:text. Design
   value: 100deg, ink -> navy -> ink, 1.6 s linear infinite. */
.hp .calc .step .shim {
  background: linear-gradient(100deg, var(--hp-ink) 42%, var(--hp-navy) 50%, var(--hp-ink) 58%);
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: hp-shimmer 1.6s linear infinite;
}
@keyframes hp-shimmer {
  from {
    background-position: 200% 0;
  }
  to {
    background-position: -20% 0;
  }
}
/* Three-dot ellipsis: opacity is a PLAIN property (not only a keyframe
   state) so `animation:none!important` under reduced motion still leaves it
   fully visible — a static "…", never invisible. */
.hp .calc .ell i {
  display: inline-block;
  font-style: normal;
  opacity: 1;
  animation: hp-blink 1.2s infinite;
}
.hp .calc .ell i:nth-child(2) {
  animation-delay: 0.2s;
}
.hp .calc .ell i:nth-child(3) {
  animation-delay: 0.4s;
}
@keyframes hp-blink {
  0%,
  80%,
  100% {
    opacity: 0.2;
  }
  40% {
    opacity: 1;
  }
}
.hp .calc .step .txt-fail {
  margin-top: 4px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--hp-ink-4);
}

.hp .calc .smeta {
  flex: none;
  margin-top: 3px;
  font-size: 12px;
  color: var(--hp-ink-5);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.hp .calc .step[data-step-state="live"] .smeta {
  color: var(--hp-navy);
  font-weight: 600;
}
.hp .calc .step[data-step-state="done"] .smeta {
  color: var(--hp-green);
  font-weight: 600;
}
.hp .calc .step[data-step-state="fail"] .smeta {
  color: var(--hp-amber);
  font-weight: 600;
}

/* ── phase-3 subline ("Skládáme výsledky pro 178 cm") + skeleton grid ── */
.hp .calc .tailsub {
  display: none;
  align-items: center;
  gap: 10px;
  margin: 34px 0 12px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--hp-ink-4);
}
.hp .calc.skel-visible .tailsub {
  display: flex;
}
.hp .calc[data-hp-state="tail"] .tailsub {
  display: none;
}
.hp .calc .tailsub .ring {
  width: 14px;
  height: 14px;
  border-width: 2px;
}
.hp .calc[data-hp-state="tail"] .chvost {
  display: flex;
}
.hp .calc .chvost {
  display: none;
  align-items: center;
  gap: 10px;
  margin: 34px 0 12px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--hp-ink-4);
  animation: hp-row-in 0.24s both;
}
.hp .calc .chvost .ring {
  width: 14px;
  height: 14px;
  border-width: 2px;
}

.hp .calc .skels {
  display: none;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}
.hp .calc.skel-visible .skels {
  display: grid;
}
.hp .calc .skels.skels--dim {
  opacity: 0.55;
}
.hp .calc[data-hp-state="tail"] .skels.skels--dim {
  opacity: 1;
}
.hp .calc .skel {
  background: var(--hp-card);
  border: 1px solid var(--hp-line);
  border-radius: var(--hp-r);
  box-shadow: var(--hp-sh);
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.hp .calc .skel .ph {
  border-radius: 6px;
  background: linear-gradient(100deg, var(--hp-bg) 40%, #f3f5f7 50%, var(--hp-bg) 60%);
  background-size: 200% 100%;
  animation: hp-wave 1.4s linear infinite;
}
@keyframes hp-wave {
  from {
    background-position: 200% 0;
  }
  to {
    background-position: -20% 0;
  }
}
.hp .calc .skel .ph--img {
  height: 74px;
  border-radius: 8px;
}
.hp .calc .skel .ph--t {
  height: 12px;
  width: 75%;
}
.hp .calc .skel .ph--m {
  height: 9px;
  width: 50%;
}
.hp .calc .skel .ph--p {
  height: 11px;
  width: 38%;
}

/* ── payoff ── */
.hp .calc .payoff {
  display: none;
  margin-top: 36px;
  animation: hp-payoff-in 0.28s cubic-bezier(0.2, 0.7, 0.3, 1) both;
}
.hp .calc[data-hp-state="payoff"] .payoff,
.hp .calc[data-hp-state="tail"] .payoff {
  display: block;
}
@keyframes hp-payoff-in {
  from {
    opacity: 0;
    translate: 0 8px;
  }
}
.hp .calc .bignum {
  font-family: var(--hp-disp);
  font-weight: 600;
  font-size: 58px;
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--hp-ink);
  font-variant-numeric: tabular-nums;
}
.hp .calc .bignum small {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--hp-ink-2);
}
.hp .calc .paysub {
  margin: 8px 0 0;
  font-size: 13.5px;
  color: var(--hp-ink-4);
}

/* Once the payoff/chvost states are reached, the running-state steps list
   stands down (design frame 5 drops it entirely). */
.hp .calc[data-hp-state="tail"] .steps {
  display: none;
}

/* ── timeout + error bars ── */
.hp .calc .contbar {
  margin-top: 30px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.hp .calc .contbar[hidden] {
  display: none;
}
.hp .calc.timeout-active [data-hp-timeoutbar] {
  animation: hp-row-in 0.24s both;
}
.hp .calc .warnbar {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  background: var(--hp-amber-soft);
  border: 1px solid #f0dcc0;
  color: var(--hp-ink-2);
  border-radius: var(--hp-rb);
  padding: 12px 14px;
  font-size: 13px;
  line-height: 1.55;
}
.hp .calc .warnbar svg {
  width: 15px;
  height: 15px;
  flex: none;
  color: var(--hp-amber);
  margin-top: 1px;
}
.hp .calc .warnbar b {
  color: var(--hp-amber);
}
.hp .calc .ctarow {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}
.hp .calc .btn.gh {
  background: transparent;
  border-color: transparent;
  color: var(--hp-ink-3);
}
.hp .calc .btn.gh:hover {
  background: var(--hp-bg);
  color: var(--hp-ink-2);
}

/* 2026-08-06 visual diff: the error/timeout bars bring their OWN primary
   "go to results" control (`.ctarow`'s `[data-hp-skip]`, promoted by the
   same promoteSkips() that promotes every escape hatch on the page), so
   above 640px the ALWAYS-present desktop escape hatch (`.skiprow`,
   already promoted to a matching `.btn.lg` by then) became a second,
   redundant "Pokračovat" sitting right under the first one -- confirmed
   in a real render (d6-desktop-error.png), not just read off the markup.
   Below 640px `.skiprow` is already display:none (see the mobile block),
   so this only has anything to hide above that width. */
.hp .calc[data-hp-state="error"] .skiprow,
.hp .calc.timeout-active .skiprow {
  display: none;
}

/* Reduced motion (POCITAME.md state 8): the finished state in one paint.
   hp.css's blanket `.hp *{animation:none!important}` already removes the
   spin/shimmer/blink/wave/rise — these three make sure "no animation" reads
   as the DESIGN's own reduced state (a solid dot, plain ink text) rather
   than a half-drawn one (an outline ring with no motion, a gradient frozen
   mid-sweep). */
@media (prefers-reduced-motion: reduce) {
  .hp .calc .step .ic-live.ring {
    background: var(--hp-navy);
    border-color: var(--hp-navy);
  }
  .hp .calc .step .shim {
    background: none;
    color: var(--hp-ink);
    -webkit-background-clip: initial;
    background-clip: initial;
  }
}

/* ── mobile (390) ── */
@media (max-width: 640px) {
  /* The other half of the desktop restoration above: on a phone the dock IS
     the primary action (audit A1 — the in-column button measured 54px below
     the fold at 360x780), so the column's copy stands down. Exactly one of the
     two is displayed at any width; the browser tests assert that by counting
     visible CTAs rather than by reading these rules. */
  .hp .qz .next,
  .hp .calc .skiprow {
    display: none;
  }
  /* The other direction of the SAME rule (2026-08-06 visual diff): below
     640px `.skiprow` is already gone, so the dock is the one doubling the
     error/timeout bar's own primary link -- hide THAT copy instead and
     keep the bar's secondary action (`Zkusit znova` / `Počkám ešte
     chvíľu`), which the dock has no equivalent for. */
  .hp .calc .contbar .ctarow [data-hp-skip] {
    display: none;
  }
  .hp .qz {
    /* 56px of bottom gutter used to sit above a sticky band that is no longer
       here. The dock reserves its own room on the `.hp` root (hp.css), so this
       is now just the last card's breathing space. */
    padding: 24px 16px 24px;
  }
  /* Screen 02/03/08 token: H1 28/700/1.15. The 800 weight and the inherited
     1.55 line height are the desktop's; on a phone they cost 11px of line box
     on every step and read heavier than the mockup. computing.html's own H1
     is sized separately below (POCITAME.md: 44/600 desktop, 32/600 mobile —
     a different screen with its own token, not this one). */
  .hp .qz h1.step-h {
    font-size: 28px;
    font-weight: 700;
    line-height: 1.15;
  }
  .hp .calc h1 {
    font-size: 32px;
  }
  /* Screens 02/03/08 all put 18px between the helper and the answers, and let
     the answer block own that gap rather than the paragraph. 30px of desktop
     margin plus the block's own top margin was reading as two separate gaps. */
  .hp .qz .helper {
    margin-bottom: 0;
  }
  .hp .qz .tiles,
  .hp .qz .pillrow {
    margin-top: 18px;
  }
  .hp .qz .top {
    flex-wrap: wrap;
    gap: 12px;
    /* 40px of desktop gutter under a two-row header pushed the H1 a third of
       the way down the fold. Screen 08 puts 22px here. */
    margin-bottom: 22px;
  }
  /* :not(.pnum) — the step label carries .prog too, and moving BOTH to their
     own row costs three rows for a two-row header. Back and the label share
     row one, the bar spans row two. */
  .hp .qz .top > .prog:not(.pnum) {
    order: 3;
    flex-basis: 100%;
  }
  .hp .qz .top > .pnum {
    margin-left: auto;
  }
  .hp .pillrow .pill {
    flex: 1 1 auto;
  }
  /* ── S2 selected state, PHONE ONLY ──────────────────────────────────────
     Navy-soft ground, a 2px navy inset ring, navy text. NEVER a solid navy
     fill: that belongs to the one primary CTA on the screen, and on step 3 the
     selected pill and the dock's submit button were the same dark blue 40px
     apart, so the selection read as the heavier of two buttons (audit C1).

     Phone only since 2026-08-02 (Codex final review finding 5). It first
     shipped unconditional, which repainted the DESKTOP budget pill from the
     baseline solid navy/white it has always had — measured at 1280, a checked
     pill went from rgb(16,72,132)/#fff to rgb(230,238,246)/rgb(16,72,132) with
     a 2px inset ring. Desktop is the frozen etalon; audit C1 was a phone
     finding and this is its phone fix. The unconditional rules above are the
     c84feffe declarations, verbatim. */
  .hp .tiles .tile:has(input:checked) {
    border-color: transparent;
    box-shadow: 0 0 0 2px var(--hp-navy) inset;
    background: var(--hp-navy-soft);
    color: var(--hp-navy);
  }
  .hp .pillrow .pill:has(input:checked) {
    background: var(--hp-navy-soft);
    border-color: transparent;
    box-shadow: 0 0 0 2px var(--hp-navy) inset;
    color: var(--hp-navy);
  }
  /* Selected wins the `box-shadow` on specificity, which would swallow the
     focus ring on the option a keyboard user is standing on. Only needed where
     the selected state DRAWS a box-shadow, i.e. here; the desktop's solid fill
     declares none, so `.pill:focus-within` reaches it unaided exactly as it
     did at c84feffe. */
  .hp .tiles .tile:has(input:checked):focus-within,
  .hp .pillrow .pill:has(input:checked):focus-within {
    box-shadow: 0 0 0 2px var(--hp-navy) inset, 0 0 0 3px var(--hp-navy-soft);
  }
  /* A 6px track is a 6px hit target on a touch screen. Padding rather than a
     taller track: `background-clip: content-box` keeps the drawn line at 6px
     while the element itself clears the 44px floor the QA checklist sets. */
  .hp .qz .hrange {
    height: 44px;
    padding: 19px 0;
    background-clip: content-box;
  }
  /* "Máme to." is now computing.html's H1 itself (`.hp .calc h1`, sized
     above), not a separate `.done` element — so it inherits the H1 mobile
     size (32px/600) rather than needing its own rule. */
  .hp .calc .bignum {
    font-size: 42px;
  }
  .hp .calc .bignum small {
    font-size: 17px;
  }
  .hp .calc .skels {
    grid-template-columns: 1fr 1fr;
    gap: 10px;
  }
  .hp .calc .skel:nth-child(3) {
    display: none;
  }
  /* B2: the screen used to be pinned 96px from the top and then stop, which
     left ~900px of nothing under three ticked lines and read as stalled. It is
     vertically centred at every width now, like the desktop always was — the
     content is short by design, so the fold is the layout. */
  .hp.hp-calc {
    padding-top: 24px;
    padding-inline: 20px;
  }
}

/* ── NOT SHIPPED: cross-document view transitions ──
   Owner feedback 2026-07-31 #2 also asked for the steps to stop reading as
   hard reloads, and `@view-transition { navigation: auto }` is exactly the
   platform feature for that: the browser cross-fades between two documents
   and the MPA shape (every step works with JavaScript off, as the prototype
   specified) stays untouched.

   It was built, and then pulled, because it could not be verified. With the
   at-rule present, the INCOMING document never renders: requestAnimationFrame
   stops firing and the page is frozen indefinitely. Reduced to a control
   experiment of two 20-line static pages whose only content is the at-rule
   and a link, served over plain http:

     with `@view-transition`      requestAnimationFrame never fires; frozen
     without it (same two files)  49 frames in 400 ms

   Chromium 149, reproduced headless AND in real Chrome, though both under
   Playwright/CDP. That points at the automation harness rather than at this
   codebase — a 20-line page cannot be at fault — but it means there is no way
   here to demonstrate that a real visitor does NOT see the freeze.

   The trade is one-sided. Upside: the funnel steps cross-fade. Downside if
   the freeze is real: the entry path of a paywall that went live yesterday is
   a blank page for everyone, and no test in this repo would catch it. So it
   waits for one manual check in an ordinary browser window.

   To enable, add this at the top level of BOTH hp-landing.css (O1) and
   hp-funnel.css (the steps) — a transition needs the rule on both documents —
   together with a reduced-motion block disabling `::view-transition-group(*)`,
   `::view-transition-old(*)` and `::view-transition-new(*)`, which hp.css's
   blanket `.hp *` reset cannot reach because those pseudo-elements hang off
   the root:

     @view-transition { navigation: auto; }
*/
