/*
  /product-cost-estimator — the lead engine.

  Same tokens, same top bar, same footer as the other three pages. The job here is
  different again: this page is not read, it is operated, and then it is read once
  at the end. So it is built as two surfaces rather than as a document —

    a working surface   one bordered panel that swaps its contents four times
    a reading surface   the estimate, laid out as a report and never as a form

  and there is nothing between them but four seconds of the machine working.

  ── The visibility contract ──────────────────────────────────────────────────

  All four stages ship in the document. Which of them is on screen is decided
  here, not in JavaScript, off two flags on <html>:

    data-js='true'        set inline in <head> before first paint
    data-estimator='open' set by the 1400ms failsafe if the module never arrived

  So the stepped form is stepped from the first frame — no flash of a long
  scrolling form — and if the module is blocked, 404s or throws, `data-estimator`
  turns every rule below off again and the page falls back to the one-page form
  that works with no script at all. Every rule that hides something a no-script
  visitor needs is written as:

    html[data-js='true']:not([data-estimator='open']) …

  Nothing in this file may hide part of the brief unconditionally. That is the
  whole of the rule; if a selector here does not carry both flags, it is a bug.

  ── Colour ───────────────────────────────────────────────────────────────────

  The palette keeps the meanings it has everywhere else on the site:

    blue   --info      software: the reading, the arithmetic, the analysis
    violet --network   the supply network being read — factories, materials
    orange --accent    human expertise. On this page that is one line of copy,
                       under the booking button, and nothing else.
    ink    --fg        physical fact and finished work

  Signal orange is 3.4:1 on white and never carries a sentence there — see the
  contrast note in tokens.css. Where an error has to be noticed it is an orange
  *rule* beside ink text, never orange text.

  Loaded after tokens.css and base.css; home.css is not loaded here, so anything
  this page needs is either in this file or in base.css.
*/

/* ═══ THE PAGE ════════════════════════════════════════════════════════════ */

.est {
  padding-top: calc(var(--topbar-h) + clamp(44px, 6vh, 76px));
  padding-bottom: clamp(64px, 7vw, 104px);
}

/*
  The working surface is narrower than the shell and the estimate is not. A brief
  is a column of controls a person reads down one at a time; the estimate is a
  report with three chains side by side. Same page, two measures.
*/
.est-intro {
  max-width: 40em;
  margin-bottom: clamp(30px, 3.4vw, 44px);
}

.est-intro .eyebrow {
  margin-bottom: clamp(16px, 2vw, 24px);
}

.est-intro h1 {
  margin-bottom: clamp(16px, 1.8vw, 22px);
}

.est-intro .lead {
  margin: 0;
}

/* ═══ PROGRESS ════════════════════════════════════════════════════════════ */

/*
  Four words and three hairlines. It exists to say "this is short", so it is set
  at the smallest mono size on the site and it never becomes a control — nothing
  here is clickable, because a step you have not filled in yet is not somewhere
  you can go.

  Shipped hidden: it describes a flow that only exists once the module runs.
*/
.est-progress {
  display: none;
  margin-bottom: clamp(26px, 2.8vw, 38px);
}

html[data-js='true']:not([data-estimator='open']) .est-progress {
  display: block;
}

.est-progress ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px clamp(10px, 1.4vw, 18px);
  list-style: none;
}

.est-progress li {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--font-mono);
  font-size: var(--t-mono-sm);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--fg-faint);
  transition: color var(--dur) var(--ease);
}

/* The em-dash of "Product — Requirements", drawn rather than typed. */
.est-progress li:not(:last-child)::after {
  content: '';
  width: clamp(14px, 3vw, 34px);
  height: 1px;
  margin-left: clamp(4px, 1vw, 10px);
  background: var(--line);
}

.est-progress-mark {
  width: 7px;
  height: 7px;
  border: 1px solid currentcolor;
  border-radius: 50%;
  transition:
    background-color var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease);
}

/* Behind you: filled. Where you are: a ring. Ahead: a hairline outline. */
.est-progress li[data-state='done'] {
  color: var(--fg-soft);
}

.est-progress li[data-state='done'] .est-progress-mark {
  background: var(--fg-soft);
}

.est-progress li[data-state='current'] {
  color: var(--fg);
}

.est-progress li[data-state='current'] .est-progress-mark {
  border-color: var(--fg);
  box-shadow: inset 0 0 0 2px var(--bg);
  background: var(--fg);
}

/* ═══ THE FORM ════════════════════════════════════════════════════════════ */

/*
  One bordered area of the page rather than a card floating on it — the light
  theme's raised surface is the same white as the background and the separation
  is the hairline. See the note on --bg-raised in tokens.css.

  It is one panel across all four stages on purpose: the frame stays still and
  only what is inside it changes, which is what makes the flow feel like four
  questions rather than four pages.
*/
.est-form {
  max-width: 62em;
  padding: clamp(26px, 3vw, 46px);
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--bg-raised);
}

/*
  What the page can and cannot do without JavaScript. Only ever seen on that
  path, so it is set as the first thing inside the panel rather than tucked away
  — a visitor who will not get an estimate should read that before the first
  field, not after the last one.
*/
.est-js-note {
  margin-bottom: clamp(26px, 2.8vw, 36px);
  padding-left: 16px;
  border-left: 2px solid var(--info);
  font-size: var(--t-small);
  line-height: 1.6;
  color: var(--fg-sub);
}

html[data-js='true']:not([data-estimator='open']) .est-js-note {
  display: none;
}

/* ── Stages ───────────────────────────────────────────────────────────── */

.est-stage {
  border: 0;
}

/* One page, four stages, with a rule between them. The no-script view. */
.est-stage + .est-stage {
  margin-top: clamp(40px, 4.4vw, 64px);
  padding-top: clamp(40px, 4.4vw, 64px);
  border-top: 1px solid var(--line);
}

/*
  Stepped. Everything but the live stage leaves the layout entirely: a collapsed
  fieldset that still occupies a row would push the buttons down the page, and a
  `required` control inside a `visibility: hidden` box still blocks submission.
*/
html[data-js='true']:not([data-estimator='open']) .est-stage:not([data-active]) {
  display: none;
}

html[data-js='true']:not([data-estimator='open']) .est-stage + .est-stage {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

/*
  The stage arriving. Short and small — the panel is not moving, so this only has
  to say "different question", and anything longer becomes a wait. Keyed off a
  class the flow adds, so it plays on the way in and never on the way out.
*/
@media (prefers-reduced-motion: no-preference) {
  .est-stage[data-enter] {
    animation: est-stage-in var(--dur) var(--ease) both;
  }
}

@keyframes est-stage-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
}

.est-stage-head {
  margin-bottom: clamp(24px, 2.6vw, 34px);
}

.est-stage-head h2 {
  font-size: clamp(26px, 2.6vw, 38px);
  max-width: 20em;
  margin-bottom: 12px;
}

.est-stage-head .lead {
  font-size: var(--t-body);
  max-width: 36em;
  margin: 0;
}

/* ── The field grid ───────────────────────────────────────────────────── */

/*
  Two columns for the short answers, one for anything that is a paragraph or a
  set of choices. `data-span` comes from the field definition in
  content/estimator.js, so a new field lands in the grid correctly without
  anyone deciding where it goes.
*/
.est-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(20px, 2.2vw, 28px) clamp(18px, 2vw, 26px);
  align-items: start;
}

.est-field {
  min-width: 0;
  margin: 0;
  border: 0;
}

.est-field[data-span='full'] {
  grid-column: 1 / -1;
}

/*
  Conditional fields. They ship visible so the no-script brief is complete, and
  collapse the moment the flag is set; js/estimator-flow.js opens the ones whose
  condition is met. Height is not animated — the answer that opens them is a
  radio, and a box that unrolls under a click you have already made lands after
  you have moved on. The fade is enough to say it appeared.
*/
html[data-js='true']:not([data-estimator='open'])
  .est-field[data-when-field]:not([data-open]) {
  display: none;
}

@media (prefers-reduced-motion: no-preference) {
  html[data-js='true'] .est-field[data-when-field][data-open] {
    animation: est-field-in var(--dur) var(--ease) both;
  }
}

@keyframes est-field-in {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
}

/* ── Labels, help, errors ─────────────────────────────────────────────── */

/*
  Mono and uppercase, the design system's voice for a control label — the same
  voice as the buttons and the section eyebrows. Small, because the answer
  matters more than the question.
*/
.est-label {
  display: block;
  width: 100%;
  margin-bottom: 8px;
  padding: 0;
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  line-height: 1.4;
  color: var(--fg-sub);
}

/*
  The two fields that move the estimate most: the description and what the buyer
  pays today. Nothing but a darker label — they are already the fields a buyer
  wants to fill in, and a highlighted box around one of six controls reads as a
  warning rather than as an invitation.
*/
.est-field[data-feature] .est-label {
  color: var(--fg);
}

/* Decorative. `required` on the control carries the fact. */
.est-req {
  color: var(--accent);
}

.est-help {
  margin-top: 8px;
  font-size: var(--t-small);
  line-height: 1.5;
  color: var(--fg-soft);
}

/*
  An orange rule and ink copy. Orange fails contrast at this size on white, so it
  marks the field and the words stay readable — see the contrast note in
  tokens.css.
*/
.est-error {
  margin-top: 10px;
  padding-left: 12px;
  border-left: 2px solid var(--signal);
  font-size: var(--t-small);
  line-height: 1.5;
  color: var(--fg);
}

.est-form-error {
  margin-top: clamp(20px, 2.2vw, 28px);
  padding-left: 14px;
  border-left: 2px solid var(--signal);
  font-size: var(--t-body);
  line-height: 1.55;
  color: var(--fg);
}

.est-note {
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--fg-soft);
}

.est-required {
  margin-top: clamp(22px, 2.4vw, 30px);
  font-family: var(--font-mono);
  font-size: var(--t-mono-sm);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg-faint);
}

/* ── Inputs ───────────────────────────────────────────────────────────── */

/*
  The border belongs to the wrapper, not to the input, because half of these
  controls have something beside them inside the same box: a currency selector, a
  € sign, a "units" suffix. One frame around the whole control reads as one
  control.

  A textarea has nothing beside it, so it is its own frame and carries the same
  border directly.
*/
.est-input-wrap,
.est-textarea {
  display: flex;
  align-items: center;
  width: 100%;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--bg);
  transition: border-color var(--dur-fast) var(--ease);
}

.est-input {
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  padding: 13px 14px;
  border-radius: inherit;
  background: none;
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: 1.45;
}

/*
  Scoped to the wrapper on purpose. Every control here is an `.est-input` for its
  type and its padding, and all but one of them sits inside a frame that is
  already drawn — so the border comes off inside a wrapper and nowhere else. Left
  unscoped it also strips the textarea, which is its own frame and has no wrapper
  to fall back on.
*/
.est-input-wrap .est-input {
  border: 0;
}

.est-textarea {
  display: block;
  /* Vertical only: a resizable width would break out of the grid column. */
  resize: vertical;
  min-height: 104px;
}

.est-input::placeholder {
  color: var(--fg-faint);
}

/*
  The site never removes a focus outline. It moves it: the outline is drawn
  around the frame rather than around the input inside it, so a control with a
  currency selector in the same box does not get two rings. The border tracks
  focus as well — the outline sits outside the control and the border is the
  control, so on a panel of eight of them the border is what tells you which one
  you are in.
*/
.est-input-wrap:focus-within {
  outline: 2px solid var(--fg);
  outline-offset: 3px;
  border-radius: var(--r-sm);
  border-color: var(--fg);
}

.est-input-wrap .est-input:focus-visible {
  outline: none;
}

.est-textarea:focus,
.est-input-wrap:focus-within {
  border-color: var(--fg);
}

/* The field the browser or the flow rejected. Marked, never recoloured. */
.est-field[data-invalid] .est-input-wrap,
.est-field[data-invalid] .est-textarea {
  border-color: var(--signal);
}

/*
  Native select chrome varies by platform enough that one of eight controls would
  look like it came from somewhere else. The arrow is drawn instead — inline SVG
  so it needs no request, and padding on the right keeps a long country name
  clear of it.
*/
.est-select {
  appearance: none;
  padding-right: 40px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='7' viewBox='0 0 11 7'%3E%3Cpath d='M1 1l4.5 4.5L10 1' fill='none' stroke='%236d6d6d' stroke-width='1.4' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 15px center;
  cursor: pointer;
}

/* A select that has not been answered yet reads as a placeholder, like one. */
.est-select:has(option[value='']:checked) {
  color: var(--fg-faint);
}

.est-select option {
  color: var(--fg);
}

/* ── Affixes: €, units, per unit ──────────────────────────────────────── */

.est-affix {
  flex: 0 0 auto;
  padding-left: 14px;
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  color: var(--fg-soft);
  pointer-events: none;
}

.est-affix--end {
  padding: 0 14px 0 0;
  text-transform: uppercase;
  letter-spacing: 0.07em;
}

/* With a prefix in the box the number sits next to it, not 14px away from it. */
.est-input-wrap:has(.est-affix:first-child) .est-input,
.est-input-wrap[data-currency] .est-input {
  padding-left: 6px;
}

/*
  The currency selector for the one figure the buyer supplies rather than reads.
  Inside the field's own frame with a hairline between them, so it reads as part
  of the amount and not as a seventh question.
*/
.est-currency {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  align-self: stretch;
  border-right: 1px solid var(--line);
}

.est-currency-select {
  appearance: none;
  height: 100%;
  padding: 0 26px 0 13px;
  border: 0;
  border-radius: var(--r-sm) 0 0 var(--r-sm);
  background: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6' viewBox='0 0 11 7'%3E%3Cpath d='M1 1l4.5 4.5L10 1' fill='none' stroke='%236d6d6d' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  color: var(--fg-sub);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.04em;
  cursor: pointer;
}

/* ── Cards ────────────────────────────────────────────────────────────── */

/*
  A choice you make by pressing the answer rather than by opening a list. The
  radio itself is off screen — not `display: none`, which takes it out of the
  accessibility tree and out of the keyboard's reach — and the label is the
  control.
*/
.est-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 168px), 1fr));
  gap: 12px;
}

/* Two wide cards, not three narrow ones: the sourcing question has two answers. */
.est-cards[data-wide='true'] {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}

.est-card,
.est-chip {
  position: relative;
  cursor: pointer;
}

.est-card input,
.est-chip input {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.est-card .est-choice-face {
  display: block;
  height: 100%;
  padding: 16px 18px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--bg);
  transition:
    border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}

.est-choice-label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg);
}

.est-choice-note {
  display: block;
  margin-top: 7px;
  font-size: var(--t-small);
  line-height: 1.45;
  color: var(--fg-soft);
}

.est-card:hover .est-choice-face {
  border-color: var(--fg-soft);
}

/*
  Selected is a second hairline drawn inside the first rather than an inversion.
  Three of these sit side by side and one of them is chosen at all times, so a
  black card would be the loudest thing in the panel from the first frame.
*/
.est-card:has(input:checked) .est-choice-face {
  border-color: var(--fg);
  box-shadow: inset 0 0 0 1px var(--fg);
}

/* The outline goes on the visible face, since the radio it belongs to is not. */
.est-card:has(input:focus-visible) .est-choice-face,
.est-chip:has(input:focus-visible) .est-choice-face {
  outline: 2px solid var(--fg);
  outline-offset: 3px;
}

/* ── Chips ────────────────────────────────────────────────────────────── */

/*
  The same choice at a quarter of the weight, for the four priorities — which are
  one word each and are already answered by default. Selected inverts here,
  because a chip is the size of a button and inverting is what a button does on
  this site.
*/
.est-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.est-chip .est-choice-face {
  display: inline-flex;
  align-items: center;
  min-height: 42px;
  padding: 0 18px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-xs);
  background: var(--bg);
  transition:
    background-color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}

.est-chip:hover .est-choice-face {
  border-color: var(--fg-soft);
}

.est-chip:has(input:checked) .est-choice-face {
  border-color: var(--fg);
  background: var(--fg);
}

.est-chip:has(input:checked) .est-choice-label {
  color: var(--bg);
}

/* ── Advanced ─────────────────────────────────────────────────────────── */

/*
  A native <details>, so the disclosure works on the no-script page too. The
  marker is drawn as a + that becomes a − — the default triangle is the one piece
  of platform chrome on the page that cannot be restyled consistently.
*/
.est-advanced {
  margin-top: clamp(26px, 2.8vw, 36px);
  padding-top: clamp(22px, 2.4vw, 28px);
  border-top: 1px solid var(--line);
}

.est-advanced-toggle {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg-sub);
  cursor: pointer;
  list-style: none;
  transition: color var(--dur-fast) var(--ease);
}

.est-advanced-toggle::-webkit-details-marker {
  display: none;
}

.est-advanced-toggle::before {
  content: '+';
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-xs);
  /* Turn the tracking off inside the box or the glyph sits off centre. */
  letter-spacing: 0;
  font-size: 13px;
  line-height: 1;
}

.est-advanced[open] .est-advanced-toggle::before {
  content: '−';
}

.est-advanced-toggle:hover {
  color: var(--fg);
}

.est-advanced-body {
  padding-top: clamp(20px, 2.2vw, 26px);
}

.est-advanced-body > .est-note {
  margin-bottom: clamp(18px, 2vw, 24px);
}

/* ── Stage navigation ─────────────────────────────────────────────────── */

.est-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 16px;
  margin-top: clamp(28px, 3vw, 40px);
}

/*
  The primary is pushed to the trailing edge on every stage, including the first
  one where it is alone. The panel does not move between stages and neither does
  the button that leaves it.
*/
.est-go {
  order: 2;
  min-height: 52px;
  margin-left: auto;
  padding: 0 30px;
}

.est-nav [data-back] {
  order: 1;
}

.est-nav-note {
  order: 3;
  flex: 0 0 100%;
  font-size: var(--t-small);
  color: var(--fg-soft);
}

/*
  The step buttons only exist in a stepped flow. On the no-script page the last
  stage's real submit is the only button, which is what sends the brief.
*/
.est-nav [data-step-nav] {
  display: none;
}

html[data-js='true']:not([data-estimator='open']) .est-nav [data-step-nav] {
  display: inline-flex;
}

/* ═══ ANALYSIS ════════════════════════════════════════════════════════════ */

/*
  The one brand moment in the flow, and the argument for the whole page: the
  machine doing the work the homepage describes. Four to seven seconds, eight
  stages, and every line of it is something the cost model actually does.

  Laid out as a diagram over a list rather than as a spinner over a percentage.
  Nothing here pulses, bounces or glows; the only thing that moves is state
  arriving, one step at a time.
*/
.est-analysis {
  max-width: 68em;
  padding: clamp(30px, 3.4vw, 50px);
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--bg-raised);
}

.est-analysis-head {
  margin-bottom: clamp(30px, 3.4vw, 46px);
}

.est-analysis-head h2 {
  font-size: clamp(26px, 2.8vw, 40px);
  max-width: 22em;
  margin-bottom: 12px;
}

.est-analysis-head .lead {
  font-size: var(--t-body);
  margin: 0;
}

/*
  The rail is read first and comes second in the document — it is decorative and
  hidden from assistive technology, so a screen reader gets the eight named
  stages without a five-node diagram in front of them.
*/
.est-analysis-body {
  display: grid;
  grid-template-areas:
    'rail'
    'steps';
  gap: clamp(30px, 3.4vw, 44px);
}

.est-rail {
  grid-area: rail;
}

.est-steps {
  grid-area: steps;
}

/* ── The rail ─────────────────────────────────────────────────────────── */

.est-rail-cols {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  list-style: none;
}

.est-rail-cols li {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  text-align: center;
}

/*
  The segment between this node and the next. It grows from the left as the beam
  passes, which is the whole of the animation — one hairline extending, five
  times, in the order the model reaches the columns.
*/
.est-rail-cols li:not(:last-child)::after {
  content: '';
  position: absolute;
  top: 4px;
  left: 50%;
  width: 100%;
  height: 1px;
  background: var(--line);
}

.est-rail-cols li:not(:last-child)::before {
  content: '';
  position: absolute;
  top: 4px;
  left: 50%;
  width: 100%;
  height: 1px;
  background: currentcolor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-slow) var(--ease-inout);
}

.est-rail-cols li[data-state='done']::before,
.est-rail-cols li[data-state='active']::before {
  transform: scaleX(1);
}

.est-rail-dot {
  position: relative;
  z-index: 1;
  width: 9px;
  height: 9px;
  border: 1px solid currentcolor;
  border-radius: 50%;
  background: var(--bg-raised);
  transition:
    background-color var(--dur) var(--ease),
    transform var(--dur) var(--ease);
}

.est-rail-label {
  font-family: var(--font-mono);
  font-size: var(--t-mono-sm);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  line-height: 1.3;
  transition: color var(--dur) var(--ease);
}

/*
  Tone is the palette's own meaning, not decoration — see the note on
  ANALYSIS.columns in content/estimator.js. Blue is the software, violet is the
  network it is reading, ink is physical fact. Nothing here is orange: orange is
  human expertise, and no person has looked at the brief yet.
*/
.est-rail-cols li {
  color: var(--fg-faint);
}

.est-rail-cols li[data-state='active'],
.est-rail-cols li[data-state='done'] {
  color: var(--fg-soft);
}

.est-rail-cols li[data-tone='info'][data-state='active'],
.est-rail-cols li[data-tone='info'][data-state='done'] {
  color: var(--info);
}

.est-rail-cols li[data-tone='network'][data-state='active'],
.est-rail-cols li[data-tone='network'][data-state='done'] {
  color: var(--network);
}

.est-rail-cols li[data-tone='plain'][data-state='active'],
.est-rail-cols li[data-tone='plain'][data-state='done'] {
  color: var(--fg);
}

.est-rail-cols li[data-state='active'] .est-rail-dot,
.est-rail-cols li[data-state='done'] .est-rail-dot {
  background: currentcolor;
}

.est-rail-cols li[data-state='active'] .est-rail-dot {
  transform: scale(1.25);
}

/* ── The eight stages ─────────────────────────────────────────────────── */

/*
  Two columns, filled down and then across, so 01–04 read as a column rather
  than as alternating rows. `grid-auto-flow: column` with four explicit rows is
  what does that, and it means the source order is still 01 to 08.
*/
.est-steps {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(4, auto);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px clamp(24px, 3vw, 48px);
  list-style: none;
}

.est-step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: 14px;
  padding: 13px 0;
  border-bottom: 1px solid var(--line);
  transition: opacity var(--dur) var(--ease);
}

.est-step-num {
  font-family: var(--font-mono);
  font-size: var(--t-mono-sm);
  letter-spacing: 0.06em;
  line-height: 1.7;
  color: var(--fg-faint);
  font-variant-numeric: tabular-nums;
  transition: color var(--dur) var(--ease);
}

.est-step-title {
  display: block;
  font-size: var(--t-small);
  line-height: 1.45;
  color: var(--fg-sub);
  transition: color var(--dur) var(--ease);
}

.est-step-note {
  display: block;
  margin-top: 3px;
  font-size: var(--t-mono);
  line-height: 1.45;
  color: var(--fg-faint);
}

/*
  The mark. A hairline box that is empty while the step is waiting, blue while it
  is being done, and ink with a check once it is — working and finished are two
  different things, and the palette already says which is which.
*/
.est-step-mark {
  position: relative;
  width: 14px;
  height: 14px;
  margin-top: 3px;
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  transition:
    border-color var(--dur) var(--ease),
    background-color var(--dur) var(--ease);
}

.est-step[data-state='idle'] {
  opacity: 0.62;
}

.est-step[data-state='active'] .est-step-num {
  color: var(--info);
}

.est-step[data-state='active'] .est-step-title {
  color: var(--fg);
}

.est-step[data-state='active'] .est-step-mark {
  border-color: var(--info);
}

/*
  The one thing on the page that moves on its own, and it moves as little as it
  can: a dot at 3px inside a 14px box, fading. It marks which of eight lines is
  live, which a static page of eight identical rows cannot say.
*/
.est-step[data-state='active'] .est-step-mark::after {
  content: '';
  position: absolute;
  inset: 3px;
  border-radius: 1px;
  background: var(--info);
  animation: est-working 1.1s var(--ease-inout) infinite alternate;
}

@keyframes est-working {
  from {
    opacity: 0.25;
  }
  to {
    opacity: 1;
  }
}

.est-step[data-state='done'] .est-step-title {
  color: var(--fg-sub);
}

.est-step[data-state='done'] .est-step-mark {
  border-color: var(--fg);
  background: var(--fg);
}

.est-step[data-state='done'] .est-step-mark::after {
  content: '✓';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--bg-raised);
  font-size: 9px;
  line-height: 1;
}

/* ── Ready ────────────────────────────────────────────────────────────── */

.est-ready {
  display: flex;
  align-items: center;
  gap: 11px;
  margin-top: clamp(28px, 3vw, 40px);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--fg);
}

.est-ready::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--info);
}

/* ═══ RESULTS ═════════════════════════════════════════════════════════════ */

/*
  The reading surface. No panel and no border around the whole thing: this is a
  document, and the blocks are separated by rules the way the rest of the site
  separates anything editorial.
*/
.est-results {
  max-width: var(--shell);
}

@media (prefers-reduced-motion: no-preference) {
  .est-results[data-enter] {
    animation: est-results-in var(--dur-slow) var(--ease) both;
  }
}

@keyframes est-results-in {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
}

/* ── The headline figure ──────────────────────────────────────────────── */

.est-result-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 20px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--line);
}

.est-ref {
  font-family: var(--font-mono);
  font-size: var(--t-mono-sm);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--fg-faint);
}

.est-result-head h2 {
  font-size: clamp(30px, 3.6vw, 50px);
  max-width: 22em;
  margin: clamp(22px, 2.4vw, 30px) 0 clamp(26px, 2.8vw, 38px);
}

.est-cost-label {
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--fg-sub);
  margin-bottom: 10px;
}

/*
  The number the visitor came for. Display face, tabular figures, and the range
  set as one string — "€6.40 – €7.80" is the estimate and neither end of it is
  the answer on its own.
*/
.est-cost {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 12px;
  font-family: var(--font-display);
  font-size: clamp(32px, 4.6vw, 58px);
  font-weight: 800;
  letter-spacing: -0.022em;
  line-height: 1.02;
  font-variant-numeric: tabular-nums;
}

.est-cost-unit {
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-soft);
}

.est-basis {
  margin-top: 14px;
  font-size: var(--t-body);
  color: var(--fg-sub);
}

/*
  The sentence that does most of the honesty work on this page, and it sits
  directly under the figure it qualifies rather than in small print at the
  bottom. Quiet, never hidden.
*/
.est-disclaimer {
  max-width: 44em;
  margin-top: 12px;
  padding-left: 14px;
  border-left: 2px solid var(--line-strong);
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--fg-soft);
}

.est-result-head [data-slot='converted'] {
  margin-top: 12px;
}

/* ── Blocks ───────────────────────────────────────────────────────────── */

.est-block {
  margin-top: clamp(44px, 5vw, 76px);
  padding-top: clamp(30px, 3.2vw, 44px);
  border-top: 1px solid var(--line);
}

.est-block > h3,
.est-block-head h3 {
  margin-bottom: 10px;
}

.est-block-head {
  margin-bottom: clamp(24px, 2.6vw, 34px);
}

.est-block-head .est-note {
  max-width: 46em;
  margin: 0;
}

.est-block > h3 + .est-compare {
  margin-top: clamp(22px, 2.4vw, 30px);
}

.est-block > .est-note:last-child {
  margin-top: clamp(18px, 2vw, 24px);
  max-width: 46em;
}

/* ── Compare and budget cells ─────────────────────────────────────────── */

/*
  A hairline grid rather than three bordered cards: the gap *is* the rule, so the
  cells sit against each other and read as one figure being resolved into
  another. The background of the container is what shows through the 1px gaps.
*/
.est-compare {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  overflow: hidden;
}

.est-cell {
  padding: clamp(20px, 2.2vw, 28px);
  background: var(--bg);
}

.est-cell-label {
  font-family: var(--font-mono);
  font-size: var(--t-mono-sm);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--fg-soft);
  margin-bottom: 12px;
}

.est-cell-value {
  font-family: var(--font-display);
  font-size: clamp(22px, 2.2vw, 30px);
  font-weight: 800;
  letter-spacing: -0.018em;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}

.est-cell-note {
  margin-top: 10px;
  font-size: var(--t-small);
  line-height: 1.45;
  color: var(--fg-soft);
}

/*
  The one figure a buyer came for, so the one cell that inverts. `data-theme` on
  the cell rather than hard-coded ink: the tokens rebind and the label, the value
  and the note all follow.
*/
.est-cell--hero {
  background: var(--bg);
  color: var(--fg);
}

.est-annual {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 20px;
  margin-top: 18px;
  padding: 18px 0 0;
  border-top: 1px solid var(--line);
}

.est-annual .est-cell-label {
  margin-bottom: 0;
}

.est-annual-value {
  font-family: var(--font-display);
  font-size: clamp(20px, 1.9vw, 26px);
  font-weight: 800;
  letter-spacing: -0.016em;
  font-variant-numeric: tabular-nums;
}

.est-annual .est-cell-note {
  margin-top: 0;
}

/* ── Breakdown ────────────────────────────────────────────────────────── */

/*
  Eight rows: what it is, how much of the landed cost it is, and what that comes
  to. The bar is the middle column rather than a chart beside the table, so the
  proportions are read in the same pass as the numbers.
*/
.est-breakdown {
  list-style: none;
  margin: 0;
  padding: 0;
}

.est-breakdown-row {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.5fr) auto;
  align-items: center;
  gap: clamp(14px, 1.8vw, 26px);
  padding: 14px 0;
  border-top: 1px solid var(--line);
}

.est-breakdown-label {
  font-size: var(--t-small);
  line-height: 1.4;
  color: var(--fg);
}

.est-breakdown-bar {
  position: relative;
  height: 6px;
  border-radius: 1px;
  background: var(--line);
  overflow: hidden;
}

/*
  One hue, stepped in opacity down the eight components — materials is the
  darkest and "other landed costs" the lightest. Eight distinct colours would
  make a legend necessary; a single ramp says "these are parts of one number".
  `--i` and `--v` are set per row by js/estimator-results.js.
*/
.est-breakdown-bar span {
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--v, 0) * 100%);
  background: var(--fg);
  opacity: calc(1 - (var(--i, 0) * 0.085));
  border-radius: 1px;
  transform-origin: left center;
}

@media (prefers-reduced-motion: no-preference) {
  .est-breakdown-bar span {
    animation: est-bar-in var(--dur-slow) var(--ease-inout) both;
    animation-delay: calc(var(--i, 0) * 55ms);
  }
}

@keyframes est-bar-in {
  from {
    transform: scaleX(0);
  }
}

.est-breakdown-value {
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.02em;
  color: var(--fg);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.est-breakdown-share {
  display: block;
  margin-top: 3px;
  font-size: var(--t-mono-sm);
  color: var(--fg-faint);
}

.est-breakdown-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding: 16px 0 0;
  border-top: 1px solid var(--fg);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.est-breakdown-total span:last-child {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}

/* ── Scenarios ────────────────────────────────────────────────────────── */

.est-scenarios {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: clamp(14px, 1.6vw, 20px);
}

.est-scenario {
  display: flex;
  flex-direction: column;
  padding: clamp(22px, 2.4vw, 30px);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-raised);
}

/* The recommendation, marked with a second hairline rather than a colour. */
.est-scenario[data-recommended='true'] {
  border-color: var(--fg);
  box-shadow: inset 0 0 0 1px var(--fg);
}

.est-scenario-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 12px;
  margin-bottom: 18px;
}

.est-scenario-tag {
  font-family: var(--font-mono);
  font-size: var(--t-mono-sm);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--accent);
}

/*
  Only one of the three cards carries a tag, and all three render the element so
  that the names line up across the row. A collapsed empty line would put "Best
  overall" one line above "Lowest cost", which reads as a layout fault rather
  than as emphasis.
*/
.est-scenario-tag:empty::before {
  content: '\00a0';
}

.est-scenario-name {
  font-family: var(--font-display);
  font-size: var(--t-h3);
  font-weight: 800;
  letter-spacing: -0.014em;
  line-height: 1.1;
  flex: 0 0 100%;
}

/* The same label, tighter: inside a card there is less room above the figure. */
.est-scenario .est-cell-label {
  margin-bottom: 8px;
}

.est-scenario-cost {
  font-family: var(--font-display);
  font-size: clamp(24px, 2.4vw, 32px);
  font-weight: 800;
  letter-spacing: -0.018em;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}

.est-scenario-range {
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: var(--t-mono-sm);
  letter-spacing: 0.04em;
  color: var(--fg-soft);
  font-variant-numeric: tabular-nums;
}

.est-scenario-rows {
  margin: clamp(20px, 2.2vw, 26px) 0 0;
  display: grid;
  gap: 0;
}

.est-scenario-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 11px 0;
  border-top: 1px solid var(--line);
}

.est-scenario-row dt {
  font-family: var(--font-mono);
  font-size: var(--t-mono-sm);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg-soft);
}

.est-scenario-row dd {
  font-size: var(--t-small);
  text-align: right;
  color: var(--fg);
}

/* ── The chain ────────────────────────────────────────────────────────── */

/*
  Five stops in the order the goods move, with an arrow between them — the same
  left-to-right reading as the route diagrams on the homepage. It wraps rather
  than scrolls: five boxes on one line at 390px would be five columns of one word.
*/
.est-chain {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 10px clamp(10px, 1.4vw, 16px);
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: chain;
}

.est-chain-step {
  flex: 1 1 170px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 18px 18px 20px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-raised);
}

.est-chain-label {
  font-family: var(--font-mono);
  font-size: var(--t-mono-sm);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--fg-soft);
}

.est-chain-place {
  font-size: var(--t-small);
  line-height: 1.4;
  color: var(--fg);
}

/*
  The arrow sits between two cards and belongs to neither, so it is drawn on the
  gap. Orange, because this is physical movement — the one place on this page the
  brand accent is used as anything other than a label.
*/
.est-chain-step:not(:last-child) {
  position: relative;
}

.est-chain-step:not(:last-child)::after {
  content: '→';
  position: absolute;
  top: 50%;
  right: calc(clamp(10px, 1.4vw, 16px) * -1);
  width: clamp(10px, 1.4vw, 16px);
  text-align: center;
  transform: translate(0, -50%);
  color: var(--signal);
  font-size: 13px;
  line-height: 1;
}

/* ── Trade-offs ───────────────────────────────────────────────────────── */

/*
  Two by two, not auto-fit. There are exactly four axes, and packing them by
  available width puts three on the first row and leaves Reliability on its own
  underneath — which reads as an axis that arrived late rather than as one of a
  set. A square block reads as a set.
*/
.est-tradeoff {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(24px, 2.8vw, 40px) clamp(30px, 3.4vw, 56px);
}

.est-axis-label {
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--fg);
}

.est-axis-bars {
  list-style: none;
  margin: 0;
  padding: 0;
}

.est-axis-bars li {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 6px;
  padding: 12px 0 0;
}

.est-axis-name {
  font-size: var(--t-mono);
  color: var(--fg-sub);
}

.est-axis-bar {
  position: relative;
  height: 5px;
  border-radius: 1px;
  background: var(--line);
  overflow: hidden;
}

/*
  The recommended chain's bars are ink; the other two are a grey. Not three
  colours — the question the chart answers is "what does the recommendation give
  up", and a legend of three hues turns that into a puzzle.
*/
.est-axis-bar span {
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--v, 0) * 100%);
  border-radius: 1px;
  background: var(--fg-faint);
  transform-origin: left center;
}

.est-axis-bars li[data-recommended='true'] .est-axis-bar span {
  background: var(--fg);
}

.est-axis-bars li[data-recommended='true'] .est-axis-name {
  color: var(--fg);
}

@media (prefers-reduced-motion: no-preference) {
  .est-axis-bar span {
    animation: est-bar-in var(--dur-slow) var(--ease-inout) both;
    animation-delay: calc(var(--i, 0) * 60ms);
  }
}

/* ── Drivers ──────────────────────────────────────────────────────────── */

/*
  Seven assumptions, each one a question for the call. Label left, sentence
  right, hairline between — a definition list, set as one.
*/
.est-drivers {
  margin: 0;
}

.est-driver {
  display: grid;
  grid-template-columns: minmax(0, 15em) minmax(0, 1fr);
  gap: 8px clamp(20px, 2.6vw, 44px);
  padding: 16px 0;
  border-top: 1px solid var(--line);
}

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

.est-driver dt {
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg-soft);
}

.est-driver dd {
  margin: 0;
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--fg);
  max-width: 44em;
}

/* ── The commercial step ──────────────────────────────────────────────── */

/*
  Dark, because it is the one block on this page that is an offer rather than a
  finding, and the site's own language for "this is us talking" is the ink
  ground. `data-theme` rather than hard-coded colours, so the buttons, the rules
  and the copy inside it all rebind on their own.
*/
.est-conversion {
  margin-top: clamp(48px, 5.4vw, 84px);
  padding: clamp(30px, 3.6vw, 56px);
  border-radius: var(--r);
  background: var(--bg);
  color: var(--fg);
}

.est-conversion h3 {
  font-size: clamp(24px, 2.6vw, 36px);
  max-width: 22em;
  margin-bottom: 14px;
}

.est-conversion .lead {
  max-width: 40em;
  margin-bottom: clamp(26px, 2.8vw, 36px);
}

.est-conversion-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

.est-conversion-actions .btn {
  min-height: 52px;
  padding: 0 30px;
}

/*
  The managed-service reminder, said once. Orange is human expertise everywhere
  on this site, and on the ink ground it reads at 6.0:1 — the one place on this
  page the accent is allowed to carry a sentence.
*/
.est-cue {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: clamp(24px, 2.6vw, 32px);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  line-height: 1.5;
  color: var(--accent);
}

.est-cue::before {
  content: '';
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
}

/* What the button actually does today, said before it is pressed. */
.est-handoff {
  max-width: 44em;
  margin-top: 18px;
}

.est-restart {
  margin-top: clamp(34px, 3.6vw, 48px);
}

/* ═══ THE PRINTABLE ESTIMATE ══════════════════════════════════════════════ */

/*
  "Download estimate" is the browser's own print-to-PDF against real markup
  rather than a service this site does not have. The report is written into the
  page by js/estimator-report.js and never shown on screen — so it is display:
  none here even after the `hidden` attribute comes off, and the print sheet
  below is the only place it exists.
*/
.est-report {
  display: none;
}

@media print {
  /*
    Everything that is chrome goes: the bar, the footer, the working surface, the
    analysis and the on-screen results. What prints is one document, laid out for
    paper, with the disclaimer on it — a printed estimate travels further than a
    page does, so the sentence that says it is not a quotation has to travel with
    it.
  */
  .topbar,
  .footer,
  .skip-link,
  .est {
    display: none !important;
  }

  .est-report {
    display: block;
    padding: 0;
    color: #000;
    background: #fff;
    font-size: 10.5pt;
    line-height: 1.5;
  }

  .rep-page {
    /* One page break between the estimate and the appendix, never inside a row. */
    break-after: page;
  }

  .rep-page:last-child {
    break-after: auto;
  }

  .rep-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 24pt;
    padding-bottom: 10pt;
    border-bottom: 1.5pt solid #000;
  }

  .rep-brand {
    font-family: var(--font-display);
    font-size: 13pt;
    font-weight: 800;
    letter-spacing: -0.02em;
  }

  .rep-kicker {
    font-family: var(--font-mono);
    font-size: 7.5pt;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: #444;
    margin-top: 3pt;
  }

  .rep-ref {
    font-family: var(--font-mono);
    font-size: 7.5pt;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: #444;
    text-align: right;
  }

  .rep-title {
    font-family: var(--font-display);
    font-size: 22pt;
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.05;
    margin: 16pt 0 4pt;
  }

  .rep-meta {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(90pt, 1fr));
    gap: 8pt 18pt;
    margin: 14pt 0 0;
    padding: 10pt 0;
    border-top: 0.5pt solid #bbb;
    border-bottom: 0.5pt solid #bbb;
  }

  .rep-meta dt {
    font-family: var(--font-mono);
    font-size: 7pt;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: #666;
  }

  .rep-meta dd {
    margin: 2pt 0 0;
    font-size: 9.5pt;
  }

  .rep-section {
    margin-top: 16pt;
    break-inside: avoid;
  }

  .rep-section-title {
    font-family: var(--font-mono);
    font-size: 7.5pt;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.11em;
    color: #666;
    padding-bottom: 5pt;
    border-bottom: 0.5pt solid #bbb;
    margin-bottom: 8pt;
  }

  .rep-figure {
    font-family: var(--font-display);
    font-size: 20pt;
    font-weight: 800;
    letter-spacing: -0.02em;
  }

  .rep-figure-note {
    font-size: 9pt;
    color: #444;
    margin-top: 3pt;
  }

  .rep-table {
    width: 100%;
    border-collapse: collapse;
  }

  .rep-table th,
  .rep-table td {
    padding: 4.5pt 0;
    border-bottom: 0.5pt solid #ddd;
    font-size: 9.5pt;
    text-align: left;
    vertical-align: top;
  }

  .rep-table th {
    font-family: var(--font-mono);
    font-size: 7pt;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: #666;
  }

  .rep-table td:last-child,
  .rep-table th:last-child {
    text-align: right;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
  }

  .rep-table tfoot td {
    border-top: 1pt solid #000;
    border-bottom: 0;
    font-weight: 700;
  }

  .rep-list {
    margin: 0;
    padding-left: 12pt;
  }

  .rep-list li {
    margin-bottom: 3.5pt;
    font-size: 9.5pt;
  }

  .rep-body {
    font-size: 9.5pt;
    max-width: 34em;
  }

  .rep-chain {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 6pt 14pt;
  }

  .rep-chain li {
    font-size: 9.5pt;
  }

  .rep-chain-label {
    font-family: var(--font-mono);
    font-size: 7pt;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: #666;
    display: block;
  }

  /* The one thing on the document that must survive being skimmed. */
  .rep-disclaimer {
    margin-top: 18pt;
    padding: 9pt 11pt;
    border: 0.5pt solid #999;
    font-size: 8.5pt;
    line-height: 1.45;
    color: #222;
    break-inside: avoid;
  }

  /* Its own heading: no rule under it, because the box is already a frame. */
  .rep-disclaimer .rep-section-title {
    border-bottom: 0;
    padding-bottom: 0;
    color: #222;
    margin-bottom: 4pt;
  }

  .rep-footer {
    margin-top: 14pt;
    padding-top: 7pt;
    border-top: 0.5pt solid #bbb;
    font-size: 8.5pt;
    color: #444;
  }

  @page {
    margin: 16mm 15mm;
  }
}

/* ═══ NARROW ══════════════════════════════════════════════════════════════ */

/*
  The estimate loses its second column before the brief does — three scenario
  cards need more room than two short fields, and the fields are what a visitor
  is looking at for most of the time they are here.
*/
@media (max-width: 980px) {
  .est-driver {
    grid-template-columns: minmax(0, 1fr);
    gap: 5px;
  }

  .est-breakdown-row {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  /* The bar moves under the row rather than being squeezed between two columns. */
  .est-breakdown-bar {
    grid-column: 1 / -1;
    order: 3;
    margin-top: 2px;
  }
}

@media (max-width: 900px) {
  .est-analysis-body {
    gap: clamp(26px, 3vw, 36px);
  }

  .est-steps {
    grid-auto-flow: row;
    grid-template-rows: none;
    grid-template-columns: minmax(0, 1fr);
  }

  .est-rail-label {
    font-size: 9px;
    letter-spacing: 0.05em;
  }

  /* Half of 2×2 is three bars in a column half as wide as the labels above them. */
  .est-tradeoff {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 720px) {
  .est-result-head h2 {
    font-size: clamp(27px, 7vw, 36px);
  }

  /*
    The chain becomes a column, so the arrow between two cards turns a quarter
    turn with it.
  */
  .est-chain-step {
    flex: 1 1 100%;
  }

  .est-chain-step:not(:last-child)::after {
    content: '↓';
    top: auto;
    right: auto;
    bottom: calc(10px * -1);
    left: 50%;
    width: 10px;
    height: 10px;
    transform: translate(-50%, 0);
  }
}

@media (max-width: 560px) {
  /*
    One column of fields. Below this, two short controls side by side leave
    neither of them room to be read.
  */
  .est-fields {
    grid-template-columns: minmax(0, 1fr);
  }

  /*
    Four words will not sit on one line at this width, and once the row wraps the
    connector after the last word on it points at nothing. Without them it reads
    as four labelled dots — which is all it ever claimed to be.
  */
  .est-progress li:not(:last-child)::after {
    display: none;
  }

  /*
    The working panel loses its border and its padding and becomes the page. At
    22px of gutter a bordered box inside it spends 26px a side on a frame nobody
    needs when there is nothing beside it to be separated from.
  */
  .est-form,
  .est-analysis {
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
  }

  /* The rail's own dot background has to follow the panel's. */
  .est-rail-dot {
    background: var(--bg);
  }

  .est-step[data-state='done'] .est-step-mark::after {
    color: var(--bg);
  }

  .est-conversion {
    margin-left: calc(var(--gutter) * -1);
    margin-right: calc(var(--gutter) * -1);
    border-radius: 0;
  }

  /*
    The primary action goes full width and the secondary follows it. Two
    half-width mono buttons at 390px break their own labels over two lines.
  */
  .est-conversion-actions .btn,
  .est-nav .btn {
    width: 100%;
  }

  .est-go {
    margin-left: 0;
    order: 1;
  }

  .est-nav [data-back] {
    order: 2;
  }

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