/* ==================================================================== */
/* Design tokens.                                                        */
/*                                                                       */
/* Three accents, and each of them MEANS something the domain already    */
/* asserts:                                                              */
/*                                                                       */
/*   brand blue  the measurement itself — the opening, its maatvoering   */
/*               and the kruismaten every other number is derived from   */
/*   hout        the stelkozijn                                          */
/*   glas        the ruiten                                              */
/*                                                                       */
/* Hout and glas are not decoration. AGENTS.md §7: the stelkozijn and    */
/* the glass are two SEPARATELY MANUFACTURED PRODUCTS with two separate  */
/* mediation budgets, and that split is the single most consequential    */
/* thing in this model. It is worth being able to see which product a    */
/* field belongs to without reading it, so the two colours mark the form */
/* steps AND the two halves of the mediation panel.                      */
/*                                                                       */
/* The neutrals are biased toward the accent rather than being pure      */
/* greys; on a screen held next to grey lead and old glass, a flat #888  */
/* reads as unfinished.                                                  */
/* ==================================================================== */
:root {
    --paper: #ffffff;
    --wash: #f2f6f8;
    --ink: #0f1b23;
    --ink-2: #46596a;
    --ink-3: #7b8fa0;
    --line: #dce4ea;
    --line-soft: #eaf0f4;

    --brand: #189DD8;
    --brand-deep: #0f6e9b;
    --brand-wash: #e7f5fc;
    --brand-ring: rgba(24, 157, 216, 0.22);

    --hout: #b4762a;
    --hout-ink: #8a5a17;
    --hout-wash: #fbf2e4;

    --glas: #14887f;
    --glas-ink: #0d6a63;
    --glas-wash: #e6f4f2;

    /* Beslag. The third PRODUCT on the sheet, beside the hout and the glas,  */
    /* and it gets a colour for the same reason those two have one: the panel */
    /* is read against a different trade. A near-neutral graphite rather than */
    /* another hue -- it has to sit next to --glas without competing with it, */
    /* and hinges are the one thing on this page that is actually metal.      */
    /* Deliberately darker and greyer than --step-1 (#6d8496), which is the   */
    /* blue-grey the unnumbered form steps already use.                       */
    --metaal: #4a5157;
    --metaal-ink: #363c41;
    --metaal-wash: #eef0f2;

    --danger: #b91c1c;

    /* The five form steps, in order. 1-3 run UP a blue ramp so the       */
    /* kruismaten are the brightest thing on the page; 4 and 5 hand over  */
    /* to the two products.                                               */
    --step-1: #6d8496;
    --step-1-ink: #52677a;
    --step-1-wash: #eef2f5;

    --radius: 10px;
    --radius-sm: 8px;

    --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text",
               "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    /* Every number in this app is a millimetre that gets cut. They are   */
    /* set in a mono face with tabular figures so a column of them lines  */
    /* up and a transposed digit is visible rather than merely present.   */
    --font-num: ui-monospace, "SF Mono", "Cascadia Mono", "Segoe UI Mono",
                "Roboto Mono", Consolas, monospace;
}

body {
    font-family: var(--font-ui);
    background: var(--wash);
    color: var(--ink);
}

/* Tailwind's .hidden is a single class in a <style> its JIT injects at
   RUNTIME. Measured: that style element lands AFTER this file's <link>, so
   today Tailwind wins a single-class tie and .hidden does hide a .stack.
   THAT IS NOT SOMETHING TO RELY ON — it is a property of when a vendored
   script happens to run, not of anything in this repo, and it is invisible
   in the source. Every rule below that sets `display` on an element some
   code hides is therefore paired with an explicit `.x.hidden` twin, which
   is two classes and wins whatever the order turns out to be. Same trick,
   and same reason, as .step-zoom.hidden further down. Do not add a display
   rule without its twin: the failure mode is a panel that refuses to hide,
   which on this form means fields for a stelkozijn that isn't there. */

@media (prefers-reduced-motion: reduce) {
    * {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }
}

.main-container {
    display: grid;
    grid-template-columns: 400px 1fr;
    gap: 20px;
    height: calc(100vh - 100px);
    max-width: 1920px;
    margin: 0 auto;
}

.left-panel {
    overflow-y: auto;
    padding-right: 10px;
}

.right-panel {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* New Results Layout */
.results-layout {
    display: grid;
    grid-template-columns: 1fr 260px;
    gap: 10px;
    flex: 1;
    min-height: 0;
}

/* Main Canvas Section */
.main-canvas-section {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.main-canvas {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.main-canvas .canvas-container {
    flex: 1;
    min-height: 0;
}

/* Steps Sidebar */
.steps-sidebar {
    display: flex;
    flex-direction: column;
    gap: 10px;
    overflow-y: auto;
    padding-right: 5px;
    padding-bottom: 10px;
}

/* No fixed width on desktop: the sidebar is a flex COLUMN, so the default
   align-items: stretch sizes these to the track. A hard 250px did not fit the
   260px track once its 5px padding and the vertical scrollbar were taken out,
   and the few pixels of overflow put a horizontal scrollbar under the column.
   The stacked layout, where the sidebar is a row, sets its own width. */
.step-canvas-section {
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    overflow: hidden;
    flex-shrink: 0;
}

/* A thumbnail's caption, at thumbnail scale. */
.step-canvas-section .canvas-title {
    font-size: 9.5px;
    letter-spacing: 0.06em;
    padding: 6px 8px;
}

/* Square by ratio rather than by a fixed pair of pixel values, so it follows
   whatever width the section ends up with. */
.step-canvas-section .canvas-container {
    aspect-ratio: 1;
    position: relative;
    padding: 5px;
}

.step-canvas-section .svg-host {
    top: 5px;
    left: 5px;
    right: 5px;
    bottom: 5px;
}

/* Every step opens enlarged on a click. Both classes, so the affordance is
   zoom-in rather than the plain pointer Tailwind's preflight puts on every
   [role="button"]. */
.step-canvas-section.step-zoomable {
    cursor: zoom-in;
    transition: border-color 0.15s, box-shadow 0.15s;
}

.step-zoomable:hover {
    border-color: #189DD8;
    box-shadow: 0 1px 4px rgba(24, 157, 216, 0.25);
}

.step-zoomable:focus-visible {
    outline: 2px solid #189DD8;
    outline-offset: 2px;
}

/* The buttons inside a step are not part of the zoom target. */
.step-zoomable button {
    cursor: pointer;
}

/* Pane navigation in step canvases */
.step-pane-nav {
    position: absolute;
    bottom: 3px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 5px;
    background: rgba(255,255,255,0.95);
    padding: 2px 6px;
    border-radius: 12px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    font-size: 10px;
}

.step-pane-nav button {
    padding: 2px;
}

.step-pane-nav svg {
    width: 14px;
    height: 14px;
}

/* Canvas Section Common Styles */
.canvas-section {
    background: var(--paper);
    position: relative;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(15, 27, 35, 0.04);
}

/* The same header treatment as a form step: a tint that fades out to the
   right, and a small uppercase title. A drawing's caption is a label on a
   technical document, not a headline. */
.canvas-title {
    padding: 9px 12px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-align: left;
    color: var(--ink-2);
    background-image: linear-gradient(to right, var(--brand-wash), transparent 72%);
    border-bottom: 1px solid var(--line-soft);
}

.canvas-container {
    flex: 1;
    position: relative;
    padding: 10px;
}

/* SVG drawing hosts. The svg viewBox does all scaling and centring; the host
   just fills its container. */
.svg-host {
    position: absolute;
    top: 10px;
    left: 10px;
    right: 10px;
    bottom: 10px;
}

.svg-host svg {
    display: block;
    width: 100%;
    height: 100%;
}

/* Hoekdetails: a 2x2 grid of zoomed corners, filling one step thumbnail in the
   steps sidebar. The grid fills its container absolutely rather than flowing,
   because .canvas-container is a fixed 240x240 box and a flowed grid would
   overflow it. Cells stay equal so the four share an aspect ratio as well as a
   scale — being comparable with each other is the whole point. */
.corner-detail-grid {
    position: absolute;
    top: 5px;
    left: 5px;
    right: 5px;
    bottom: 5px;
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: 4px;
}

.corner-detail {
    position: relative;
    min-width: 0;
    min-height: 0;
    border: 1px solid #e5e7eb;
    border-radius: 3px;
    background: white;
}

/* The host is absolutely positioned by .svg-host with a 10px inset; inside a
   cell this small that would leave almost nothing, so pull it tight. */
.corner-detail .svg-host {
    top: 2px;
    left: 2px;
    right: 2px;
    bottom: 2px;
}

/* The caption sits in the corner it names: Linksboven top-left, Rechtsonder
   bottom-right. These four drawings differ only in which corner they magnify,
   so a caption parked in the same place on all four made the reader match
   text to picture instead of just looking. */
.corner-detail-label {
    position: absolute;
    z-index: 1;
    font-size: 8.5px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--ink-3);
    pointer-events: none;
}

.corner-detail--tl .corner-detail-label { top: 3px; left: 5px; }
.corner-detail--tr .corner-detail-label { top: 3px; right: 5px; }
.corner-detail--br .corner-detail-label { bottom: 3px; right: 5px; }
.corner-detail--bl .corner-detail-label { bottom: 3px; left: 5px; }

/* Enlarged step view. The panel is a square capped at 900px so the drawing
   area is as large as the viewport allows on a phone and still a comfortable
   square on desktop; the svg viewBox does the fitting inside it, as everywhere
   else. */
.step-zoom {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
}

/* Two classes on purpose: Tailwind's own .hidden is a single class and is
   injected into the head at runtime, so it cannot be relied on to win the
   cascade against .step-zoom's display: flex. */
.step-zoom.hidden {
    display: none;
}

.step-zoom-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(17, 24, 39, 0.6);
}

.step-zoom-panel {
    position: relative;
    display: flex;
    flex-direction: column;
    width: min(900px, 100%);
    height: min(900px, 100%);
    background: var(--paper);
    border-radius: var(--radius);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
    overflow: hidden;
}

.step-zoom-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 8px 8px 8px 14px;
    background-image: linear-gradient(to right, var(--brand-wash), transparent 72%);
    border-bottom: 1px solid var(--line-soft);
}

.step-zoom-title {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-2);
}

.step-zoom-close {
    font-size: 22px;
    line-height: 1;
    padding: 0 8px;
    color: #374151;
}

/* The drawing area. position: relative because .svg-host and
   .corner-detail-grid both position themselves against it. */
.step-zoom-body {
    position: relative;
    flex: 1;
    min-height: 0;
    padding: 10px;
}

/* At this size the corner labels can afford to be read from arm's length. */
.step-zoom-body .corner-detail-label {
    font-size: 11px;
    letter-spacing: 0.07em;
    color: var(--ink-2);
}

.step-zoom-body .corner-detail .svg-host {
    top: 6px;
    left: 6px;
    right: 6px;
    bottom: 6px;
}

/* The indeling-editor (AGENTS.md §9.8): the window on the left, the controls
   for whatever is selected on the right. Overrides .step-zoom-body's single
   drawing area, which assumes it has the panel to itself. */
.division-editor {
    display: flex;
    gap: 12px;
    align-items: stretch;
}

.division-editor-canvas {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
    /* The tap target IS the drawing, so it must not be a sliver on a phone. */
    min-height: 240px;
    cursor: crosshair;
}

.division-editor-panel {
    flex: 0 0 240px;
    overflow-y: auto;
    padding-right: 4px;
}

/* Stacked below 700px: a 240px control column beside a drawing leaves neither
   usable on the device this is actually used on. The drawing keeps the larger
   share because it is what is being tapped. */
@media (max-width: 700px) {
    .division-editor {
        flex-direction: column;
    }

    .division-editor-canvas {
        flex: 1 1 60%;
    }

    .division-editor-panel {
        flex: 0 0 auto;
        max-height: 40%;
    }
}

.step-zoom-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 6px;
    background: var(--wash);
    border-top: 1px solid var(--line-soft);
}

.step-zoom-nav.hidden {
    display: none;
}

/* ==================================================================== */
/* "Hoe de beste stand wordt gekozen" — the rotation uitleg overlay.    */
/*                                                                      */
/* Same shell as the enlarged step, one difference: this panel holds    */
/* READING MATTER, not a drawing. The step panel is a square because a  */
/* viewBox fits itself into it; this one is a column that scrolls, so   */
/* it is capped on measure rather than made square, and the body gets   */
/* the padding and the overflow the drawing panel deliberately has not. */
/*                                                                      */
/* The three accents carry the same meanings here as everywhere else    */
/* (see the tokens at the top of this file): brand = what was measured, */
/* hout = the stelkozijn, glas = the ruiten. That is not decoration in  */
/* this overlay of all places — "two products, two budgets" is the      */
/* thing it exists to explain, so the two halves of the flow wear their */
/* product's colour.                                                    */
/* ==================================================================== */

/* The checkbox and its info button. The button is a SIBLING of the      */
/* label, never a child: inside it, tapping it would also toggle the     */
/* checkbox it is explaining.                                            */
.check-with-info {
    display: flex;
    align-items: stretch;
    gap: 8px;
}

.check-with-info .check {
    flex: 1 1 auto;
    min-width: 0;
}

.info-btn {
    flex: 0 0 auto;
    width: 38px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--paper);
    color: var(--brand-deep);
    font-family: var(--font-num);
    font-size: 15px;
    font-weight: 700;
    font-style: italic;
    line-height: 1;
    cursor: pointer;
    transition: border-color 0.15s, background-color 0.15s;
}

.info-btn:hover {
    border-color: var(--brand);
    background: var(--brand-wash);
}

.info-btn:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
}

/* Taller than the step panel is allowed to be, and narrower: this is a  */
/* column of text and figures, and a 900px measure is unreadable.        */
.rotation-info-panel {
    width: min(720px, 100%);
    height: min(880px, 100%);
}

.rotation-info {
    padding: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--ink-2);
}

.rotation-info p {
    margin: 0 16px 4px;
}

.rotation-info b {
    color: var(--ink);
    font-weight: 600;
}

.ri-lead {
    margin: 14px 16px 4px !important;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--line-soft);
    color: var(--ink);
}

/* One numbered step. The rule between steps does the grouping, so the   */
/* headings can stay small — on a phone held one-handed a run of large   */
/* headings is most of the screen.                                       */
.ri-step {
    padding: 14px 0 16px;
    border-bottom: 1px solid var(--line-soft);
}

.ri-step:last-child {
    border-bottom: 0;
}

.ri-step h3 {
    display: flex;
    align-items: center;
    gap: 9px;
    margin: 0 16px 10px;
    font-size: 14px;
    font-weight: 700;
    color: var(--ink);
}

.ri-num {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--brand);
    color: #fff;
    font-family: var(--font-num);
    font-size: 12px;
    font-weight: 700;
}

/* The last section is not a step in the process — it is the set of      */
/* switches the fitter turns. No number, and a rule of its own.          */
.ri-h-optional {
    padding-top: 2px;
    border-top: 2px solid var(--brand-wash);
}

.ri-fig {
    display: block;
    width: calc(100% - 32px);
    max-width: 560px;
    height: auto;
    margin: 0 auto 12px;
}

/* Every label inside a figure, in one place: the figures are generated  */
/* markup, so their type must not be re-specified per <text>.            */
.ri-fig text {
    font-family: var(--font-ui);
    fill: var(--ink-2);
}

.ri-fig .ri-note { font-size: 9px; }
.ri-fig .ri-tick { font-size: 8.5px; font-family: var(--font-num); fill: var(--ink-3); }
.ri-fig .ri-row-label { font-size: 9.5px; font-weight: 600; }
.ri-fig .ri-cell-title { font-size: 10px; font-weight: 700; fill: var(--ink); }
.ri-fig .ri-cell-count { font-size: 9.5px; font-weight: 600; }
.ri-fig .ri-in-bar { font-size: 11px; font-family: var(--font-num); font-weight: 700; fill: var(--ink); }
.ri-fig .ri-formula { font-size: 11px; font-family: var(--font-num); font-weight: 700; fill: var(--ink); }

/* The two band tables under the budget figure, in words. HTML rather than */
/* SVG <text> because they are generated from CONFIG.MEDIATION — their     */
/* length is not known when the figure is laid out, and SVG text does not  */
/* wrap.                                                                   */
.ri-legend {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 3px 10px;
    margin: 0 16px;
    font-size: 12.5px;
}

.ri-legend dt {
    font-weight: 700;
    white-space: nowrap;
}

.ri-legend dd {
    margin: 0;
    color: var(--ink-3);
}

.ri-legend--hout { color: var(--hout-ink); }
.ri-legend--glas { color: var(--glas-ink); }

/* The hatched stretch past the maximum, named here rather than inside the  */
/* bar: the stelkozijn's zone is narrower than the words for it.            */
.ri-swatch-hatch {
    display: inline-block;
    width: 30px;
    height: 12px;
    border: 1px dashed var(--danger);
    background: repeating-linear-gradient(
        45deg,
        transparent 0 3px,
        rgba(185, 28, 28, 0.45) 3px 4.6px
    );
}

@media (max-width: 480px) {
    .ri-legend {
        grid-template-columns: 1fr;
        gap: 0;
    }

    .ri-legend dd {
        margin-bottom: 6px;
    }
}

/* An aside: true, but not part of the line of the step. Indented rather */
/* than boxed, so a run of them does not read as a second column.        */
.ri-note-block {
    margin-top: 8px !important;
    padding-left: 10px;
    border-left: 3px solid var(--line);
    font-size: 12.5px;
    color: var(--ink-3);
}

/* The objective ladder. Numbered by CSS rather than by <ol> markers so  */
/* the number can sit in the same disc the step headings use — the whole */
/* point of the figure is that these are ranked, not listed.             */
.ri-ladder {
    counter-reset: ri-rule;
    margin: 0 16px;
    padding: 0;
    list-style: none;
}

.ri-ladder li {
    counter-increment: ri-rule;
    position: relative;
    padding: 7px 0 7px 34px;
    border-top: 1px solid var(--line-soft);
}

.ri-ladder li:first-child {
    border-top: 0;
}

.ri-ladder li::before {
    content: counter(ri-rule);
    position: absolute;
    left: 0;
    top: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--wash);
    border: 1px solid var(--line);
    color: var(--ink-2);
    font-family: var(--font-num);
    font-size: 11px;
    font-weight: 700;
}

/* The first rule is the only one that is exact — every rule below it is */
/* a tiebreak. Worth being able to see that without reading.            */
.ri-ladder li:first-child::before {
    background: var(--brand);
    border-color: var(--brand);
    color: #fff;
}

.ri-ladder-main {
    display: block;
    color: var(--ink);
    font-weight: 600;
}

.ri-ladder-sub {
    display: block;
    margin-top: 2px;
    font-size: 12.5px;
    color: var(--ink-3);
}

/* The pipeline. Boxes and arrows in flow rather than one SVG: the text  */
/* is Dutch and has to wrap, and wrapping is the one thing an SVG        */
/* diagram cannot do.                                                    */
.ri-flow {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    margin: 0 16px;
}

.ri-flow-box {
    padding: 9px 11px;
    border: 1px solid var(--line);
    border-left-width: 4px;
    border-radius: var(--radius-sm);
    background: var(--paper);
}

/* Direct child only. The body text of a box contains its own inline <b>   */
/* ("het hout-budget"), and a descendant selector here turned every one of */
/* those into its own line.                                                */
.ri-flow-box > b {
    display: block;
    font-size: 13px;
}

.ri-flow-box > span {
    display: block;
    margin-top: 2px;
    font-size: 12.5px;
    color: var(--ink-3);
}

.ri-flow-box--brand { border-left-color: var(--brand); background: var(--brand-wash); }
.ri-flow-box--hout  { border-left-color: var(--hout);  background: var(--hout-wash); }
.ri-flow-box--glas  { border-left-color: var(--glas);  background: var(--glas-wash); }
.ri-flow-box--muted { border-left-color: var(--ink-3); background: var(--wash); }

.ri-flow-arrow {
    text-align: center;
    font-size: 15px;
    line-height: 1;
    color: var(--ink-3);
}

/* The two branches of the stelkozijn question, side by side because     */
/* they are one question with two answers — the same reason .check-row   */
/* pairs the two kleppen.                                                */
.ri-flow-split {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
}

@media (max-width: 560px) {
    .ri-flow-split {
        grid-template-columns: 1fr;
    }
}

.ri-options {
    margin: 0 16px;
    padding: 0;
    list-style: none;
}

.ri-options li {
    display: flex;
    gap: 10px;
    padding: 9px 0;
    border-top: 1px solid var(--line-soft);
}

.ri-options li:first-child {
    border-top: 0;
}

.ri-opt-glyph {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: var(--radius-sm);
    background: var(--brand-wash);
    color: var(--brand-deep);
    font-size: 14px;
    line-height: 1;
}

/* Direct child only, same reason as .ri-flow-box > b above. */
.ri-opt-text > b {
    display: block;
    font-size: 13px;
}

.ri-opt-text > span {
    display: block;
    margin-top: 2px;
    font-size: 12.5px;
    color: var(--ink-3);
}

/* Pane Navigation */
.pane-nav {
    position: absolute;
    bottom: 15px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 10px;
    background: rgba(255,255,255,0.95);
    padding: 5px 10px;
    border-radius: 20px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

/* The three brand-colour utilities that used to live here are gone with the
   markup that used them: .brand-color styled the old wordmark and the
   mediation/mullion link-buttons, .brand-bg-light the old analysis panels.
   The wordmark, the panels and the buttons all take their blue from the
   tokens now, so a second way to say #189DD8 is one more place for it to
   drift. */

/* ------------------------------------------------------------------ */
/* Buttons. ONE system, four weights.                                   */
/*                                                                      */
/* This replaces four unrelated looks that had grown up side by side:   */
/* a filled export button, an outlined one, a "draw the division"       */
/* button wearing the input style (so it read as a field), and a dozen  */
/* underlined-text links for the expand/collapse controls. The weights  */
/* now say what the control is worth:                                   */
/*                                                                      */
/*   --primary  the one action that produces the deliverable            */
/*   --outline  a narrower hand-off of the same deliverable             */
/*   --soft     a real action inside a step; tinted with THAT step's    */
/*              accent, so it belongs to the section it sits in         */
/*   --quiet / --tiny  changes the level of detail a field is shown at, */
/*              never the data (except where §9.6/§9.8 say so). These   */
/*              are the ones that used to be underlined text.           */
/*                                                                      */
/* --soft and --quiet read --step-accent-* off the section they are in, */
/* which is inherited, so no button carries its own colour.             */
/* ------------------------------------------------------------------ */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    line-height: 1.2;
    min-height: 34px;
    padding: 8px 14px;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--ink-2);
    cursor: pointer;
    white-space: nowrap;
    transition: background-color 0.15s, border-color 0.15s, color 0.15s, box-shadow 0.15s;
}

/* The paired twin: .btn sets display, so Tailwind's .hidden cannot.
   #mullionTopExpand and its three siblings are toggled exactly this way. */
.btn.hidden {
    display: none;
}

.btn--primary {
    background-color: var(--brand);
    border-color: var(--brand);
    color: #fff;
    padding: 8px 18px;
}

/* NO translateY ON HOVER. These buttons sit flush against the top edge of
   #results (.right-panel is overflow:hidden — it has to be, it fills a fixed
   grid track), so a 1px lift pushed the top of the button past the clip edge
   and sliced it off mid-hover. Colour and shadow carry the affordance instead;
   a control that moves under the pointer is a worse trade than a static one
   even where there is room for it. */
.btn--primary:hover {
    background-color: var(--brand-deep);
    border-color: var(--brand-deep);
    box-shadow: 0 2px 8px rgba(15, 110, 155, 0.28);
}

/* The two partial exports. Outlined rather than filled: they are narrower
   hand-offs than the full document, which stays the primary action. */
.btn--outline {
    background-color: var(--paper);
    border-color: var(--brand);
    color: var(--brand-deep);
}

/* Static on hover for the same reason as .btn--primary above. */
.btn--outline:hover {
    background-color: var(--brand-wash);
    box-shadow: 0 2px 8px rgba(15, 110, 155, 0.14);
}

.btn--soft {
    background-color: var(--step-accent-wash, var(--brand-wash));
    color: var(--step-accent-ink, var(--brand-deep));
}

.btn--soft:hover {
    background-color: var(--step-accent-tint, var(--brand-wash));
    border-color: var(--step-accent, var(--brand));
}

.btn--quiet {
    min-height: 28px;
    padding: 5px 9px;
    font-size: 12px;
    color: var(--step-accent-ink, var(--brand-deep));
}

.btn--quiet:hover {
    background-color: var(--step-accent-tint, var(--brand-wash));
}

.btn--tiny {
    min-height: 24px;
    padding: 3px 7px;
    font-size: 11px;
    color: var(--ink-3);
}

.btn--tiny:hover {
    color: var(--step-accent-ink, var(--brand-deep));
    background-color: var(--step-accent-tint, var(--line-soft));
}

.btn--block {
    width: 100%;
}

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

/* Sized in em so the glyph tracks the label rather than the root font. */
.btn-icon {
    width: 1.15em;
    height: 1.15em;
    flex: none;
}

/* A row of level controls under the field they act on. */
.btn-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* The export buttons wrap onto their own line before they shrink: a truncated
   "Stelkozijnen" and a truncated "Glas" are not distinguishable. */
.export-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: flex-end;
}

/* ------------------------------------------------------------------ */
/* Position tabs. One job holds several positions (AGENTS.md §9.2).    */
/*                                                                      */
/* Underline tabs in a white card. Two modes, set as a class by         */
/* js/tabs.js: .is-scroll (desktop — the list scrolls sideways) and     */
/* .is-overflow (stacked layout — as many tabs as fit, rest under Meer). */
/* ------------------------------------------------------------------ */

.position-bar {
    display: flex;
    align-items: stretch;
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: 0 1px 2px rgba(15, 27, 35, 0.04);
    margin-bottom: 12px;
    padding-left: 4px;
    position: relative;
    flex: 0 0 auto;
}

/* Tabs + the Meer button. A structural row, not a wrapped flex line — see
   the comment in tabs.renderInto(). */
.position-row {
    display: flex;
    align-items: stretch;
    flex: 1;
    min-width: 0;
    /* Anchors .position-menu, so the dropdown hangs under the tabs rather
       than under the action row once the bar stacks. */
    position: relative;
}

/* The second bar, above the form. Only useful once the layout stacks and
   the results panel is a long scroll away. */
.position-bar--mobile {
    display: none;
    margin-top: 12px;
    margin-bottom: 0;
}

.position-tablist {
    display: flex;
    align-items: stretch;
    flex: 1;
    min-width: 0;
}

.position-bar.is-scroll .position-tablist {
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
}

.position-bar.is-overflow .position-tablist {
    overflow: hidden;
}

.position-tab {
    flex: 0 0 auto;
    max-width: 180px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: 12px 14px 10px;
    border: 0;
    border-bottom: 3px solid transparent;
    background: none;
    color: var(--ink-3);
    font-family: inherit;
    font-size: 13px;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s;
}

.position-tab:hover {
    color: var(--ink);
}

.position-tab.is-active {
    color: var(--ink);
    font-weight: 600;
    border-bottom-color: var(--brand);
}

.position-tab:focus-visible,
.position-more-btn:focus-visible,
.position-menu-item:focus-visible,
.position-action:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: -2px;
}

/* A position that cannot be drawn cannot be exported either, so it is
   marked here rather than discovered at export time. */
.position-tab.is-incomplete::after,
.position-menu-item.is-incomplete::after {
    content: '';
    display: inline-block;
    width: 6px;
    height: 6px;
    margin-left: 6px;
    border: 1px dashed #c8d4dc;
    border-radius: 50%;
    vertical-align: middle;
}

/* --- overflow menu ------------------------------------------------- */

.position-more {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
}

.position-more-btn {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 12px 10px 10px;
    border: 0;
    background: none;
    color: var(--ink-3);
    font-family: inherit;
    font-size: 13px;
    cursor: pointer;
    white-space: nowrap;
}

.position-more-btn:hover {
    color: var(--ink);
}

.position-more-count:not(:empty) {
    background: var(--brand);
    color: #fff;
    border-radius: 999px;
    font-family: var(--font-num);
    font-size: 10.5px;
    font-weight: 700;
    padding: 1px 6px;
}

.position-menu {
    position: absolute;
    top: calc(100% + 4px);
    right: 4px;
    z-index: 20;
    min-width: 180px;
    max-height: 60vh;
    overflow-y: auto;
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    box-shadow: 0 8px 20px rgba(15, 27, 35, 0.14);
    padding: 4px;
}

.position-menu-item {
    display: block;
    width: 100%;
    text-align: left;
    padding: 9px 10px;
    border: 0;
    border-radius: 6px;
    background: none;
    color: var(--ink-2);
    font-family: inherit;
    font-size: 13px;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.position-menu-item:hover {
    background: var(--brand-wash);
    color: var(--ink);
}

/* --- action buttons ------------------------------------------------ */

.position-actions {
    display: flex;
    align-items: center;
    gap: 2px;
    flex: 0 0 auto;
    padding: 0 4px;
    border-left: 1px solid var(--line-soft);
    margin-left: 4px;
}

.position-action {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--ink-3);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.position-action:hover {
    background: var(--brand-wash);
    color: var(--brand-deep);
}

.position-action.is-danger:hover {
    background: #fdf0f0;
    color: var(--danger);
}

/* The standalone copy next to "Raamgegevens" (index.html). It isn't
   adjacent to a tab list there, so drop the dividers the in-bar version
   uses to separate itself from the tabs -- including the stacked-layout
   border-top further down, which assumes the actions sit below a tab row. */
[data-position-actions] .position-actions {
    border-left: 0;
    border-top: 0;
    margin-left: 0;
    padding: 0;
    justify-content: flex-end;
}

/* Shown in place of the drawings until a position has been analysed. The
   results panel is no longer hidden wholesale — it carries the tab bar. */
.results-placeholder {
    color: var(--ink-3);
    font-size: 13px;
    line-height: 1.5;
    background: var(--paper);
    border: 1px dashed #c8d4dc;
    border-radius: var(--radius);
    padding: 24px;
    text-align: center;
}

/* ==================================================================== */
/* THE MEASURING FORM                                                    */
/*                                                                       */
/* Five numbered steps in the order the fitter works in (AGENTS.md       */
/* §1.1): merk, maatvoering, kruismaten, stelkozijn, glas. The numbers   */
/* are not ornament — this is a real sequence, worked top to bottom on   */
/* a tablet held in one hand, and the form used to present all of it as  */
/* one undifferentiated column of controls.                              */
/*                                                                       */
/* Each step owns an accent (see the tokens at the top of this file) and */
/* publishes it to its descendants as --step-accent*, so buttons,        */
/* checked rows and focus rings inside a step take that step's colour    */
/* without any of them naming it.                                        */
/* ==================================================================== */

/* ------------------------------------------------------------ masthead */

.masthead {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px 20px;
    padding-bottom: 12px;
    margin-bottom: 16px;
    border-bottom: 1px solid var(--line);
}

/* Serif on purpose and alone on this page: it is the wordmark, and the one
   place a typeface is allowed to be about identity rather than about
   reading a millimetre. */
.wordmark {
    font-family: Georgia, "Times New Roman", serif;
    font-size: 26px;
    font-weight: 700;
    letter-spacing: 0.055em;
    line-height: 1;
    color: var(--brand);
}

.wordmark-by {
    font-size: 0.6em;
    font-weight: 400;
    font-style: italic;
    letter-spacing: 0.02em;
    color: var(--ink-3);
}

.masthead-meta {
    display: flex;
    align-items: baseline;
    gap: 10px;
    font-size: 11px;
    color: var(--ink-3);
}

/* A version is data, so it is set like one. */
.masthead-version {
    font-family: var(--font-num);
    font-size: 10.5px;
    letter-spacing: 0.02em;
    color: var(--ink-3);
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 2px 8px;
}

/* ------------------------------------------------------ section heads */

.form-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 10px;
    margin: 0 2px 10px;
    min-height: 34px;
}

.form-head-title {
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--ink-3);
}

/* The same head over the results column, so the two halves of the app read
   as one page rather than as a form and a viewer that met by accident. */
.form-head--results {
    flex: 0 0 auto;
    margin-bottom: 12px;
}

.step-flow {
    display: grid;
    gap: 12px;
}

/* Five of these are open at once, and until now all five carried the same
   white surface and the same lift — five equal figures, no ground. They are
   the ground now: paper and a shadow are spent on stap 3 alone (below),
   because the kruismaten are what the fitter is actually here to type and
   everything else on this form exists to qualify them. */
.step {
    position: relative;
    background: #fbfcfd;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
    transition: opacity 0.2s ease;

    --step-accent: var(--step-1);
    --step-accent-ink: var(--step-1-ink);
    --step-accent-wash: var(--step-1-wash);
    --step-accent-tint: #e3e9ee;
}

/* The step you are NOT in recedes. Twelve kruismaten plus four other steps
   is more than can be held at once, and the answer is not to hide anything
   — every step has to stay readable and reachable — but to let whichever one
   has the caret sit forward of the rest.

   GUARDED by #windowForm:focus-within, so this is a state and not a default:
   with nothing focused all five cards are at full contrast, which is what a
   fitter sees when they arrive at a position and when they put the tablet
   down. .step is a <details>, so its <summary> is focusable too and tapping a
   step head brings that card forward as well.

   0.8 is a floor found by eye: --ink over #fbfcfd still clears 7:1 there, and
   much below it a dimmed step starts to read as DISABLED rather than as not
   right now. */
#windowForm:focus-within .step:not(:focus-within) {
    opacity: 0.8;
}

@media (prefers-reduced-motion: reduce) {
    .step {
        transition: none;
    }
}

/* The rail. It is the only thing that reads at a glance from across a
   room, so it carries the step's colour at full strength.

   z-index, because "at full strength" has to survive card content that runs
   to the card's inner edge. .kruis is a full-bleed plinth AND position:
   relative, so at z-index auto the later tree order won and the grey ground
   painted straight over 260px of the blue rail — on stap 3, the one card the
   rail matters most on. Nothing in a step should ever be above this. */
.step::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 4px;
    z-index: 1;
    background: var(--step-accent);
}

/* The head is the <summary> of a <details>: every step folds away, so a fitter
   working on the kruismaten can put the four they are done with out of sight.
   Collapsing changes NO DATA — <details> keeps its whole subtree in the DOM, so
   getFormData still reads every field of a closed step, which is exactly why
   this one may be a <details> where the LEVEL controls (§9.10) may not. */
.step-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 14px 11px 18px;
    /* Fades out rather than filling: a solid tinted bar on five stacked
       cards turns the column into stripes. */
    background: linear-gradient(to right, var(--step-accent-wash), transparent 72%);
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.step-head::-webkit-details-marker {
    display: none;
}

/* Only while open, or a collapsed card carries a rule under nothing. */
.step[open] > .step-head {
    border-bottom: 1px solid var(--line-soft);
}

.step-head:hover {
    background-color: var(--line-soft);
}

.step-head:focus-visible {
    outline: 2px solid var(--brand-ring);
    outline-offset: -2px;
}

/* The marker sits last in the row, after the step-unit where there is one.
   ITS OWN summary's marker: a step contains folds of its own (§9.10), and a
   descendant selector would turn their arrows with it. */
.step-head .advanced-marker {
    margin-left: auto;
}

.step-unit + .advanced-marker {
    margin-left: 8px;
}

.step[open] > .step-head .advanced-marker {
    transform: rotate(45deg);
}

/* A step with nothing behind the fold — stap 4 without a stelkozijn, whose
   whole body is [data-frame-only] and hidden. The head stays, because the
   number and the title are how the fitter knows the step exists at all, but the
   arrow goes and the bar stops responding: an arrow that opens an empty card is
   a control that promises something and then does nothing.
   uiHandlers.updateFrameInputs() owns the class. */
.step--inert > .step-head {
    cursor: default;
    pointer-events: none;
}

.step--inert > .step-head .advanced-marker {
    display: none;
}

.step-num {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 21px;
    height: 21px;
    flex: none;
    border-radius: 50%;
    background: var(--step-accent);
    color: #fff;
    font-family: var(--font-num);
    font-size: 11px;
    font-weight: 700;
}

.step-title {
    font-size: 15px;
    font-weight: 650;
    letter-spacing: -0.01em;
    color: var(--ink);
}

.step-unit {
    margin-left: auto;
    font-family: var(--font-num);
    font-size: 11px;
    letter-spacing: 0.03em;
    color: var(--ink-3);
}

.step-body {
    display: grid;
    gap: 14px;
    padding: 14px 14px 16px 18px;
}

/* Paired twin — stap 4's body is the whole of what a stelkozijn adds, so
   without one the card is its header and nothing else. */
.step-body.hidden {
    display: none;
}

/* A rebuilt host that came out empty (#paneHingeInputs on an eendeling)
   must not leave a gap behind it. */
.step-body > div:empty {
    display: none;
}

.step--merk {
    --step-accent: var(--step-1);
    --step-accent-ink: var(--step-1-ink);
    --step-accent-wash: var(--step-1-wash);
    --step-accent-tint: #e3e9ee;
}

.step--maat {
    --step-accent: var(--brand-deep);
    --step-accent-ink: var(--brand-deep);
    --step-accent-wash: #e8f1f7;
    --step-accent-tint: #dbeaf3;
}

/* The one card that gets paper and a lift, for the reason in .step above.
   The rail, the accent and every other step's borders are deliberately
   untouched: this is a figure/ground move, not a colour one. */
.step--kruis {
    background: var(--paper);
    box-shadow: 0 1px 3px rgba(15, 27, 35, 0.07);

    --step-accent: var(--brand);
    --step-accent-ink: var(--brand-deep);
    --step-accent-wash: var(--brand-wash);
    --step-accent-tint: #d6ecf8;
}

.step--kozijn {
    --step-accent: var(--hout);
    --step-accent-ink: var(--hout-ink);
    --step-accent-wash: var(--hout-wash);
    --step-accent-tint: #f5e7d0;
}

.step--glas {
    --step-accent: var(--glas);
    --step-accent-ink: var(--glas-ink);
    --step-accent-wash: var(--glas-wash);
    --step-accent-tint: #d5eae7;
}

/* A position without a stelkozijn keeps a grey rail: the card is present
   because the question has to be asked, not because it has been answered
   yes. Ticking "Inclusief stelkozijn" turns the whole card amber, which
   is a state you can read from the corner of your eye while typing
   kruismaten. Without :has() support the card simply stays amber
   throughout — the fallback is a colour, never a broken layout.

   Scoped to #windowForm rather than to the card, because the checkbox
   lives in stap 1 and :has() only ever looks DOWNWARD. */
#windowForm:not(:has(#frameEnabled:checked)) .step--kozijn {
    --step-accent: #c3ced6;
    --step-accent-ink: var(--ink-2);
    --step-accent-wash: #f0f3f5;
    --step-accent-tint: #e6ebee;
}

/* The stelkozijn question is asked in stap 1 but is about the product of
   stap 4, so it borrows that colour: one product, one colour, in both
   places it appears. */
.check--hout {
    --step-accent: var(--hout);
    --step-accent-ink: var(--hout-ink);
    --step-accent-wash: var(--hout-wash);
}

/* ------------------------------------------------------------ fields */

.field-label {
    display: block;
    margin-bottom: 5px;
    font-size: 12px;
    font-weight: 600;
    color: var(--ink-2);
}

.field-hint {
    margin-top: 6px;
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--ink-3);
}

.field-hint--lead {
    margin-top: 0;
}

.field-hint strong {
    font-family: var(--font-num);
    font-weight: 700;
    color: var(--ink-2);
}

/* Blocked out so a <label class="field"> wrapping its own control lays
   out the same as a <div class="field"> — which is how the per-ruit
   scharnier selects associate without an id, since a drawn division's
   leaves cannot be enumerated in advance (§10). Paired twin: several
   .field blocks are revealed conditionally. */
.field {
    display: block;
}

.field.hidden {
    display: none;
}

/* A control that follows the field above it — shown or hidden on its own,
   which is why it is a wrapper rather than a margin on the button. */
.field-action {
    margin-top: 8px;
}

/* Two mutually-relevant checkboxes side by side (bovenklep / onderklep)
   rather than stacked: they are one question with two answers. */
.check-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

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

/* A vertical run of controls that is revealed or hidden as one, e.g. the
   whole stelkozijn block or one mullion's taper. Paired twin, see the
   note near the top of this file. */
.stack {
    display: grid;
    gap: 8px;
}

.stack.hidden {
    display: none;
}

/* Same reason as .step-body > div:empty: #klepHeights is empty until a
   klep is ticked, and an empty grid child still collects a gap. */
.stack > div:empty {
    display: none;
}

.stack--tight {
    gap: 5px;
}

/* ----------------------------------------------------------- controls */

.compact-input {
    width: 100%;
    padding: 8px 10px;
    font-family: inherit;
    font-size: 14px;
    color: var(--ink);
    background-color: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    transition: border-color 0.15s, box-shadow 0.15s;
}

.compact-input:hover {
    border-color: #c3d0d9;
}

.compact-input:focus {
    outline: none;
    border-color: var(--step-accent, var(--brand));
    box-shadow: 0 0 0 3px var(--brand-ring);
}

.compact-input--lead {
    font-size: 16px;
    font-weight: 600;
    padding: 10px 12px;
}

.compact-input--sm {
    padding: 6px 8px;
    font-size: 13px;
}

/* Opmerkingen (AGENTS.md §9.10). Free text, so it grows DOWNWARD only: a
   textarea that can be dragged wider escapes the form column and, on the
   stacked layout, pushes a horizontal scrollbar under the whole panel. */
textarea.compact-input {
    resize: vertical;
    min-height: 46px;
    line-height: 1.45;
}

/* ------------------------------------------------------------------
   Ingeklapte velden (AGENTS.md §9.10)

   One field behind a disclosure arrow, for the questions whose answer is
   the same on almost every position. Two mechanisms, one appearance:

     details.field-fold      aantal, kleur, opmerking — closing changes no
                             data, so the browser may own the state.
     button.field-fold-toggle  the stijl/dorpelbreedtes and dikte LEVELS —
                             closing DROPS overrides, so a function owns it
                             and the arrow only reports aria-expanded.

   THE CARD IS DELIBERATELY .advanced--nested's, to the pixel: same border,
   same wash, same summary padding. Those two blocks in stap 5 were the first
   folds in this form and are the ones a fitter learns to press, so anything
   else that folds has to look like them or it reads as a heading with a
   decoration in front of it. Both also reuse .advanced-marker, so the arrow
   that means "there is more here" is one shape everywhere.
   ------------------------------------------------------------------ */
.field-fold {
    border: 1px solid var(--line-soft);
    border-radius: var(--radius);
    background: #fbfcfd;
    overflow: hidden;
}

.field-fold-summary {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    /* .advanced--nested's own padding, and it grows on the stacked layout
       through the same touch-target rule further down — a fold that is a
       comfortable tap in stap 5 and a thin strip in stap 4 would be two
       different controls. */
    padding: 8px 11px;
    list-style: none;
    cursor: pointer;
    text-align: left;
    background: none;
    border: 0;
    font: inherit;
    font-size: 12px;
    font-weight: 600;
    color: var(--ink-2);
}

.field-fold-summary::-webkit-details-marker {
    display: none;
}

.field-fold-summary:hover {
    background-color: #f4f8fa;
}

.field-fold-summary:focus-visible {
    outline: 2px solid var(--step-accent, var(--brand));
    outline-offset: -2px;
}

/* The marker points right when closed and down when open — same rotation the
   .advanced panels use, driven by [open] there and by aria-expanded here. */
.field-fold[open] .advanced-marker,
.field-fold-toggle[aria-expanded="true"] .advanced-marker {
    transform: rotate(45deg);
}

.field-fold-summary .field-label,
.field-fold-toggle .field-label {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    color: inherit;
    cursor: pointer;
}

/* The value of a folded field, on the summary row itself. A collapsed "Aantal"
   that does not say "4×" hides something that changes what gets made. */
.field-fold-value {
    margin-left: auto;
    max-width: 55%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: var(--font-num);
    font-size: 12px;
    font-weight: 400;
    color: var(--ink-3);
}

.field-fold-body {
    display: grid;
    gap: 10px;
    padding: 0 11px 11px;
}

.compact-input--xs {
    padding: 5px 7px;
    font-size: 12px;
}

select.compact-input {
    appearance: none;
    -webkit-appearance: none;
    padding-right: 30px;
    background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%237b8fa0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 8l4 4 4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 9px center;
    background-size: 15px 15px;
}

/* Every number on this form is a millimetre a workshop will cut to. */
#windowForm input[type="number"] {
    font-family: var(--font-num);
    font-variant-numeric: tabular-nums;
}

/* Spinners off. They are useless for a value typed off a tape measure,
   and in the mullion and mediation grids they eat the width the digits
   need — the same narrow-track problem the .mediation-grid note below
   already records. */
#windowForm input[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}

#windowForm input[type="number"]::-webkit-outer-spin-button,
#windowForm input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* A checkbox that answers a yes/no question about the position, shown as
   a row rather than a bare box: it becomes a target you can hit without
   aiming, and a ticked row fills with its step's colour, so which
   options are on is legible without reading any of them. */
.check {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 11px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background-color: var(--paper);
    font-size: 13px;
    color: var(--ink-2);
    cursor: pointer;
    transition: border-color 0.15s, background-color 0.15s, color 0.15s;
}

.check input[type="checkbox"] {
    width: 18px;
    height: 18px;
    flex: none;
    margin: 0;
    accent-color: var(--step-accent, var(--brand));
    cursor: pointer;
}

.check:hover {
    border-color: #c3d0d9;
}

.check:has(input:checked) {
    border-color: var(--step-accent, var(--brand));
    background-color: var(--step-accent-wash, var(--brand-wash));
    color: var(--ink);
    font-weight: 600;
}

.check:focus-within {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
}

/* ---------------------------------------------------- 3. kruismaten */
/*                                                                     */
/* Eight numbers describing one crooked quadrilateral (§5), plus the    */
/* four outer maten that are their sums — so every field sits where its */
/* measurement sits, around a drawn measuring cross, the instrument the */
/* numbers come off. Reading down: the width across the top, the two    */
/* top corners, the axis with the height down each side, the two bottom */
/* corners, the width across the bottom.                                */

.kruis {
    position: relative;
    display: grid;
    /* Tight, because this is the gap WITHIN the stack of three heights down
       each side: HLB, HL and HLO are one side of one window and read as one
       column. The two width rows push themselves clear of it below. */
    gap: 3px;
    /* Every field holds one four-digit millimetre and its key, and is sized
       for exactly that -- which comes out at the width the fields had in the
       original 2x2, where four of them fitted across the card. */
    --measure-w: 74px;

    /* THE PLINTH. The card is white, so a white box cannot rise above it --
       and "this one has a number in it" is the single thing a fitter has to
       be able to see across twelve fields (see the state ladder below). The
       twelve therefore sit on a tinted ground, and lightness does the
       ranking: measured is the only white, raised box in the group.

       FULL BLEED, by a negative margin that exactly cancels .step-body's
       padding, rather than an inset panel with its own padding. An inset
       panel would eat 20px of row width, and §9.11 is explicit that the
       inset left at both edges of a width row -- the room the heights sit
       out into -- is what the whole arrangement rests on. Padding 16px
       against margins of 18/14 puts the CONTENT box back in the middle of
       the element (-18+16 = 14-16), which is what keeps the arm below at a
       plain 50%.

       It also groups: twelve controls become one instrument, which takes a
       little weight off the form without hiding anything. */
    margin: 0 -14px 0 -18px;
    padding: 12px 16px;
    background-color: #eef3f7;
    border-top: 1px solid #e2eaf0;
    border-bottom: 1px solid #e2eaf0;

    /* The vertical arm, straight down the middle. A background, so it costs */
    /* no elements and cannot get between a finger and an input.            */
    /* background-image paints ABOVE background-color on the same element, so */
    /* the plinth above does not bury it; it is only held off the two        */
    /* hairlines by the padding.                                            */
    background-image:
        linear-gradient(rgba(24, 157, 216, 0.4), rgba(24, 157, 216, 0.4));
    background-size: 1px calc(100% - 24px);
    background-position: 50% 12px;
    background-repeat: no-repeat;
}

/* An edge's two halves with its total between them: BLB · BB · BRB reads
   left-half, whole, right-half, which is the order they sit in on the
   window. Inset from the card, so the heights have somewhere further out to
   be -- that inset is the whole reason the two families never line up. */
.kruis-row--widths {
    display: flex;
    justify-content: center;
    gap: 6px;
    margin: 12px 0;
}

/* The plinth's own padding is the clearance at the two ends. */
.kruis-row--widths:first-child { margin-top: 0; }
.kruis-row--widths:last-child { margin-bottom: 0; }

/* Every height goes out to the edges, further out than the widths reach:
   the four corner hoogtes stack under and over the two totals they belong
   to. So the heights read as two columns down the sides and the widths as
   two rows across, and which axis a field measures is answerable from where
   it sits before its key is read at all. It also leaves the horizontal arm
   a span to be drawn across. */
.kruis-row--heights {
    display: grid;
    grid-template-columns: 1fr 1fr;
}

.kruis-row > .measure {
    width: var(--measure-w);
}

.kruis-row--heights > .measure {
    justify-self: start;
}

.kruis-row--heights > .measure:last-child {
    justify-self: end;
}

/* The horizontal arm and the hub at the origin.

   These used to be backgrounds on .kruis at 50% of its height, which was
   the gutter back when the grid had two rows of corner cells. It is nothing
   in particular now, so they belong to the row that IS the axis — and since
   a row holds nothing but .measure boxes, 50% of it is exactly the middle of
   those inputs.

   The arm runs the full width and the .measure boxes' own --paper
   background occludes its ends, so what shows is the segment between them,
   through the hub. */
.kruis-row--axis {
    background-image:
        linear-gradient(rgba(24, 157, 216, 0.4), rgba(24, 157, 216, 0.4)),
        radial-gradient(circle at 50% 50%, var(--brand) 0 3px, transparent 3.5px);
    background-size: 100% 1px, 100% 100%;
    background-position: 0 50%, 0 0;
    background-repeat: no-repeat;
}

/* What the app worked out, and what it still needs. An unfinished form is
   the normal state while typing, so it is stated in the same register as a
   hint; only a contradiction is red. */
.kruis-problem,
.kruis-note {
    margin-top: 10px;
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--ink-3);
}

.kruis-problem--error {
    color: var(--danger);
    font-weight: 600;
}

/* The key lives inside the field border rather than above it: a
   placeholder disappears the moment a digit is typed, and on this form
   "which box was the width?" is exactly the question that produces a
   pane 3 mm too small. It names the whole measurement (BLB, HRO) rather
   than only breedte or hoogte, which is what let the caption row above
   every pair go — see the note in index.html.

   ----------------------------------------------------------------------
   THE STATE LADDER, AND WHY ITS ORDER IS NOT NEGOTIABLE.

   Every state below weighs (0,2,0). A :has() and a :not() each take the
   specificity of their argument, so `.measure:has(:placeholder-shown)`,
   `.measure:not(:has(:placeholder-shown))`, `.measure.measure--total` and
   `.measure:focus-within` are all worth exactly the same, and SOURCE ORDER
   is the only thing deciding between them. §9.11 records that getting this
   wrong shipped twice. Write a new state INTO this sequence, never after it:

     1  .measure                                  base
     2  .measure:has(:placeholder-shown)          nothing measured yet
     3  .measure.is-derived, .measure.is-assumed  take the border back
     4  .measure.is-assumed / .is-derived         guessed / locked
     5  .measure.measure--total (+ .is-derived)   which KIND of number
     6  .measure:not(:has(:placeholder-shown))    MEASURED
     7  .measure:focus-within                     always last

   Read as lightness, that is a depth ladder on the plinth .kruis draws:
   a measured number is the only white, raised box in the group, an empty
   field is a hole the colour of the plinth, and a derived one is sunk
   below it — it is the only field here that is inert. */
.measure {
    display: grid;
    grid-template-columns: 22px minmax(0, 1fr);
    align-items: center;
    padding-left: 6px;
    background-color: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    transition: border-color 0.15s, box-shadow 0.15s, background-color 0.15s;
}

/* Not measured yet. Same "incomplete" convention the position tabs use,
   so what is still missing reads at a glance instead of on inspection.
   A genuine 0 is a real reading (§8) and fills the field like any
   other value.

   It takes the plinth's own colour: an unmeasured field is a hole in the
   instrument, not an object on it. OPAQUE, not transparent — BB and BO sit
   at 50% of .kruis and the vertical arm would otherwise run straight
   through them.

   `:has(:placeholder-shown)` rather than `:has(.measure-input:placeholder-shown)`
   ON PURPOSE, and it is not a shortening. A :has() takes the specificity of
   its most specific argument, so naming the class inside made this rule
   (0,3,0) — more specific than every state below that has to override it,
   which meant a computed maat kept the dashed border that says "nothing here
   yet" while showing a number. The only placeholder in a .measure is the
   input's, so dropping the class costs nothing and puts this back at (0,2,0),
   where a later single-class state wins. */
.measure:has(:placeholder-shown) {
    border-style: dashed;
    border-color: #cbd8e1;
    background-color: #eef3f7;
}

/* A maat the app worked out for itself. It is SHOWN as placeholder text and
   never written into the field (js/kruismaten.js), so the rule above fires on
   it and would paint it "not measured yet" — the opposite of what it means.
   Both states have to take the border back.

   is-derived  follows from the other maten and could not be anything else, so
               the field is locked: two measurements of one thing may not
               disagree.
   is-assumed  is the straight-window fallback, and stays editable — typing
               over it is how the fitter says the window is crooked. */
.measure.is-derived,
.measure.is-assumed {
    border-style: solid;
    border-color: var(--line);
}

/* Editable, so it sits just above the plinth rather than in it. */
.measure.is-assumed {
    background-color: #f7fafc;
}

/* Locked, so it is the one thing here that sits BELOW the plinth. */
.measure.is-derived {
    background-color: #e7eef3;
    border-color: #d3dee5;
}

.measure.is-derived .measure-input,
.measure.is-assumed .measure-input {
    cursor: default;
}

.measure-input::placeholder {
    color: #b3c2cc;
    opacity: 1;
}

/* The ghost is a number, not the "—" that means nothing is here, so it reads
   in the ink of a real value rather than of a placeholder. */
.measure.is-derived .measure-input::placeholder,
.measure.is-assumed .measure-input::placeholder {
    color: var(--ink-3);
    font-style: italic;
}

/* The four outer maten are a different KIND of number: the whole of one
   side, not one corner's offset from the cross. They sit in the same rows
   as the kruismaten, so position alone cannot say so — hence the brand tint
   and the brand key.

   The tint is on the HOLE, not on the number. It used to fill the box at
   every state, which made the four fields most often left for the app to
   work out the most emphatic things on the card — tint reading as emphasis
   when it only ever meant kind. Rule 6 below sits after these and takes a
   measured total to white along with everything else, so once there IS a
   number the brand-deep key carries "whole side" on its own and fill is
   free to mean state.

   Border STYLE is left alone, so the dashed "not measured yet" and the solid
   derived/assumed states still read on a total exactly as they do on a
   kruismaat; these rules come after all three so the tint survives them, and
   they are written `.measure.measure--total` for the same specificity reason
   as the note above — one class would lose the border colour to the dashed
   rule. */
.measure.measure--total {
    background-color: #e8f1f7;
    border-color: #b9d4e6;
}

.measure.measure--total.is-derived {
    background-color: #dfeaf2;
}

/* MEASURED — the state this form did not have.

   Everything else here is subtractive: a dash for nothing yet, a wash for
   locked, an italic ghost for a guess. A number the fitter actually typed
   was therefore rendered in the DEFAULT style — the quietest thing on the
   card, and the only one that has to be checked before glass is cut. It is
   now the only white, raised box in the group, and the only one whose value
   carries weight.

   Disjoint from is-derived / is-assumed BY CONSTRUCTION, so it cannot fight
   them: refreshKruismaten() never writes .value (js/ui-handlers.js), so a
   computed maat always has an empty field, always shows its placeholder, and
   can never match this rule. */
.measure:not(:has(:placeholder-shown)) {
    background-color: var(--paper);
    border-style: solid;
    border-color: #b9c8d3;
    box-shadow: 0 1px 3px rgba(15, 27, 35, 0.10);
}

.measure:not(:has(:placeholder-shown)) .measure-input {
    font-size: 15px;
    font-weight: 600;
}

/* The key confirms the box, it does not carry the reading — so on a field
   that HAS one it steps back a further notch and leaves the digits alone. */
.measure:not(:has(:placeholder-shown)) .measure-key {
    color: #a8b8c4;
}

/* ...EXCEPT on a total, where the key is the only thing left saying which
   KIND of number this is: the fill went white with every other measured
   field, and BB sits inside the width row between its own two halves, so
   position cannot say it either (§9.11).

   It has to live HERE, after the rule above and written with the extra class
   to match its (0,3,0), rather than up with the other .measure--total rules
   where it started — at (0,2,0) it lost, and a measured total went grey. */
.measure.measure--total .measure-key {
    color: var(--brand-deep);
}

.measure:focus-within {
    border-style: solid;
    border-color: var(--brand);
    box-shadow: 0 0 0 3px var(--brand-ring);
}

/* Weight 600 and a lighter grey rather than the 700 this was: three bold
   caps repeated twelve times is a texture, and it was competing with the
   twelve numbers it exists to label. Size is deliberately NOT the knob — at
   10px this is already the smallest safety-critical text on the form, and it
   is what stops "which box was the width?" from producing a pane 3 mm too
   small (§9.11), on a tablet held at arm's length in daylight. */
.measure-key {
    font-family: var(--font-num);
    font-size: 10px;
    font-weight: 600;
    color: #9fb0bd;
}

.measure:focus-within .measure-key {
    color: var(--brand-deep);
}

.measure-input {
    width: 100%;
    min-width: 0;
    /* A FIXED line-height, not `normal`: the measured state above grows this
       text to 15px, and a box that grew with its content would shift the
       drawn cross by a pixel or two on the fourth digit — while the fitter is
       looking straight at it. Height is padding + line-height + border and
       nothing else, so it is identical across all seven states. The trimmed
       right padding pays for the wider digits. */
    padding: 9px 6px 9px 0;
    line-height: 20px;
    border: 0;
    background: none;
    font-family: var(--font-num);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    color: var(--ink);
    text-align: right;
}

.measure-input:focus {
    outline: none;
}

/* ---------------------------------------------- 4. stijl- en dorpel- */
/*                                             breedtes, per zijde     */

.side-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

.side-block {
    display: grid;
    gap: 4px;
    align-content: start;
    padding: 8px;
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    background-color: #fcfdfd;
}

.side-name {
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ink-3);
}

.side-block .btn--tiny {
    justify-self: start;
}

/* Two numbers that belong to one named thing — a klep's hoogte and speling —
   side by side under its name, rather than each dragging the name along. */
.duo {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 8px;
}

.duo-field {
    display: grid;
    gap: 3px;
    min-width: 0;
}

.duo-label {
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.03em;
    color: var(--ink-3);
}

/* A bordered numeric field that stands on its own.
 *
 * NOT .end-input, which these used to borrow: that one is deliberately
 * border-less because .end draws the border around it and its L/R key. Reusing
 * it here left the klep heights with no box at all the moment that changed.
 * A field whose appearance depends on a wrapper it does not have is a
 * regression waiting to happen, so this one carries its own. */
.duo-input {
    width: 100%;
    min-width: 0;
    padding: 7px 8px;
    background-color: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    font-family: var(--font-num);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    color: var(--ink);
    text-align: right;
    transition: border-color 0.15s, box-shadow 0.15s;
}

.duo-input:hover {
    border-color: #c3d0d9;
}

.duo-input:focus {
    outline: none;
    border-color: var(--step-accent, var(--brand));
    box-shadow: 0 0 0 3px var(--brand-ring);
}

/* One side, as its two ends.
 *
 * A side that tapers has two widths, so the input for it is two boxes — the
 * corner dropdown that used to sit here asked the fitter to name a corner in
 * order to say something the two numbers already say. The SECOND box is
 * recessive until it is touched: most sides are one width, and two equal-
 * weight boxes per side would read as twice the work.
 *
 * The collapsed box is still a real, focusable input. Tapping it is what
 * opens the side (its own `focus` calls expandMullionTaper), so the
 * affordance and the control are the same object rather than a button that
 * reveals a field somewhere else. */
.end-pair {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 24px;
    gap: 4px;
    align-items: stretch;
    transition: grid-template-columns 0.15s;
}

.end-pair.is-split {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

/* One end box. The border lives HERE rather than on the input, so the letter
   naming the end can sit inside it — exactly the kruismaat fields' B/H, and
   for the same reason: a label above the box would make the row taller the
   moment a side is split, and four sides jumping in height as you open one is
   the row you were aiming at moving out from under the cursor. */
.end {
    display: grid;
    grid-template-columns: 0 minmax(0, 1fr);
    align-items: center;
    min-width: 0;
    background-color: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    transition: border-color 0.15s, box-shadow 0.15s,
                background-color 0.15s, grid-template-columns 0.15s;
}

/* Naming an end only means something once there are two of them: collapsed,
   the track is 0 wide and the letter is clipped rather than hidden, so the
   box keeps its height either way. */
.end-pair.is-split .end {
    grid-template-columns: 20px minmax(0, 1fr);
}

/* Padding only once the track has room for it: at 0 wide the letter is
   clipped, but padding is not, and 7px of it would sit over the number. */
.end-key {
    overflow: hidden;
    font-family: var(--font-num);
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
    color: var(--ink-3);
}

.end-pair.is-split .end-key {
    padding-left: 7px;
}

.end:focus-within {
    border-color: var(--step-accent, var(--brand));
    box-shadow: 0 0 0 3px var(--brand-ring);
}

.end:focus-within .end-key {
    color: var(--step-accent-ink, var(--brand-deep));
}

.end-input {
    width: 100%;
    min-width: 0;
    padding: 7px 8px;
    border: 0;
    background: none;
    font-family: var(--font-num);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    color: var(--ink);
    text-align: right;
}

.end-input:focus {
    outline: none;
}

/* Collapsed: a dashed sliver carrying a + rather than a number it has no
   room for. Dashed is the same "nothing here yet" mark the unmeasured
   kruismaat fields use. */
.end-pair:not(.is-split) .end--second {
    position: relative;
    border-style: dashed;
    border-color: #c8d4dc;
    background-color: #fbfcfd;
    cursor: pointer;
}

.end-pair:not(.is-split) .end--second .end-input {
    padding-left: 0;
    padding-right: 0;
    color: transparent;
    cursor: pointer;
}

.end-pair:not(.is-split) .end--second .end-input::placeholder {
    color: transparent;
}

.end-pair:not(.is-split) .end--second::after {
    content: '+';
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font-size: 15px;
    line-height: 1;
    color: var(--ink-3);
    /* The label is the click target; the cue must not intercept it. */
    pointer-events: none;
}

.end-pair:not(.is-split) .end--second:hover {
    border-color: var(--step-accent, var(--brand));
    background-color: var(--step-accent-wash, var(--brand-wash));
}

/* ==================================================================== */
/* THE ANALYSIS PANELS                                                   */
/*                                                                       */
/* Glas afmetingen / Stelkozijn / Toegepaste correcties, under the form. */
/* Same card, rail and header treatment as a form step, and the same two */
/* product colours: this panel is the ANSWER to what the form asked, and */
/* looking like a different application was most of why it read as an    */
/* afterthought bolted under the inputs.                                 */
/*                                                                       */
/* It is read, not operated, so the information design differs where it  */
/* should: name left, value right in the numeric face, so a column of    */
/* millimetres lines up and a transposed digit shows.                    */
/* ==================================================================== */

.panel {
    position: relative;
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: 0 1px 2px rgba(15, 27, 35, 0.04);
    overflow: hidden;
    margin-top: 12px;

    --step-accent: var(--brand);
    --step-accent-ink: var(--brand-deep);
    --step-accent-wash: var(--brand-wash);
}

.panel::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 4px;
    background: var(--step-accent);
}

.panel--glas {
    --step-accent: var(--glas);
    --step-accent-ink: var(--glas-ink);
    --step-accent-wash: var(--glas-wash);
}

.panel--hout {
    --step-accent: var(--hout);
    --step-accent-ink: var(--hout-ink);
    --step-accent-wash: var(--hout-wash);
}

.panel--metaal {
    --step-accent: var(--metaal);
    --step-accent-ink: var(--metaal-ink);
    --step-accent-wash: var(--metaal-wash);
}

/* The head is the <summary> of a <details> (js/window-geometry._panel): every
   results panel folds. Collapsing one changes no data — unlike the form's level
   controls (§9.10) — so the browser may own the state, and the panel keeps its
   own bottom border only while it is open, or a closed card carries a rule
   under nothing. */
.panel-head {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 10px 14px 10px 18px;
    background-image: linear-gradient(to right, var(--step-accent-wash), transparent 72%);
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.panel-head::-webkit-details-marker {
    display: none;
}

.panel[open] > .panel-head {
    border-bottom: 1px solid var(--line-soft);
}

.panel-head:hover {
    background-color: var(--line-soft);
}

.panel-head:focus-visible {
    outline: 2px solid var(--brand-ring);
    outline-offset: -2px;
}

/* Rotates with [open], like every other disclosure in this app. Its OWN
   summary's marker: the panels now contain nested folds of their own, and a
   descendant selector would turn those arrows too (§9.10). */
.panel[open] > .panel-head .advanced-marker {
    transform: rotate(45deg);
}

.panel-title {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--step-accent-ink);
}

.panel-body {
    display: grid;
    gap: 12px;
    padding: 12px 14px 14px 18px;
}

.panel-note {
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--ink-3);
}

/* The two hoekscharnier controls (boven / onder) side by side under one
   caption, because they are one decision with two ends rather than two
   unrelated fields. Wraps on a narrow phone rather than squeezing the selects
   below their own option text. */
/* The two dubbel-scharnier toggles, side by side. They sit beside the
   Aantal-scharnieren field rather than under it: with several ruiten a stacked
   pair per ruit was most of the block's height, and these are two words and two
   boxes.

   WRAP IS THE FALLBACK, not clipping. The chips never shrink and never
   truncate — "boven" and "onder" are the entire label, so half of one says
   nothing — so in a column too narrow for both they stack instead. That costs a
   line in a place that has run out of width anyway, where an ellipsis would
   cost the meaning. */
.hinge-corner-pair {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* The red flag on a hinge still over its limit after the dubbel scharnier
   (AGENTS.md 9.1). It sits in the Fres cell and in the corrections list, and it
   is the one mark in a panel of settled-looking numbers that asks somebody to
   do something. */
.hinge-alarm {
    color: var(--danger);
    margin-left: 5px;
    font-size: 12px;
    line-height: 1;
    cursor: help;
}

/* The info button in a panel header. Sits inside the <summary>, so its click
   handler stops the details from toggling — see _panel(). Pushed to the far end
   so it does not read as part of the title.

   The vertical padding is taken out rather than the button made wider: at the
   shared .info-btn padding the glyph sat in the middle of a tall pill and read
   as smaller than the one beside the rotation checkbox, which is the same
   control. Same box, more of it is the "i". */
/* The same flag as in the table, raised to the collapsed header. margin-left
   auto here and NOT on the button, so the two travel to the right end together
   with the flag leading. */
.hinge-alarm--head {
    margin-left: auto;
    margin-right: 0;
    font-size: 14px;
}

.hinge-alarm--head + .panel-info-btn {
    margin-left: 8px;
}

.panel-info-btn {
    margin-left: auto;
    flex: none;
    width: 30px;
    height: 22px;
    padding: 0;
    font-size: 15px;
}

/* The worked example in the scharnieren overlay. Same muted, tabular treatment
   the mediation legend beside it uses. */
.hinge-info-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 11.5px;
    font-variant-numeric: tabular-nums;
}

.hinge-info-table th,
.hinge-info-table td {
    text-align: left;
    padding: 3px 8px 3px 0;
    border-bottom: 1px solid var(--line-soft);
}

.hinge-info-table th {
    font-weight: 600;
    color: var(--ink-3);
}

/* Trimmed to the width of its own word, and NEVER below it. The default .check
   padding is sized for a full-width row like "Rotatie per paneel optimaliseren";
   here it is two chips sharing a line with a number field.

   flex: 0 0 auto and nowrap, not 0 1 auto with an ellipsis: "boven" and "onder"
   are the whole label, so a chip that shrinks is a chip that says "bove...".
   The row carries a min-width to match, which is what makes the grid hand it
   enough space instead. */
.hinge-corner-pair .check--compact {
    flex: 0 0 auto;
    gap: 6px;
    padding: 6px 9px;
    font-size: 12px;
    white-space: nowrap;
}

/* THE FULL WIDTH OF THE RUIT, and that is the cheaper answer here. The left
   panel is ~340px, so this grid runs two 126px columns — and two chips need
   158px. Sharing a column with the Aantal-scharnieren field therefore wrapped
   them one above the other, which is the one thing the row must not do.

   Spanning both columns costs ONE row, once per hinge axis rather than per ruit
   (only the first ruit on an axis carries this control), and buys the chips
   room at any panel width. An earlier attempt forced the point with a 172px
   min-width instead; in a narrower track that just overflowed the column and
   pushed the whole block sideways. */
[data-hinge-corner-row] {
    grid-column: 1 / -1;
    min-width: 0;
}


.spec {
    display: grid;
    gap: 5px;
}

.spec-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 12px;
    align-items: baseline;
    font-size: 12.5px;
}

.spec-row dt {
    color: var(--ink-2);
    min-width: 0;
}

.spec-row dd {
    font-family: var(--font-num);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--ink);
    text-align: right;
    white-space: nowrap;
}

/* A qualifier that belongs to the name, not to the number — a pane's hinge
   side, a tapered end's corner. Never set in the numeric face: it is not a
   measurement and must not read as one. */
.spec-note {
    display: block;
    font-family: var(--font-ui);
    font-size: 10.5px;
    font-weight: 500;
    color: var(--ink-3);
}

.spec-group {
    display: grid;
    gap: 5px;
    padding-top: 10px;
    border-top: 1px solid var(--line-soft);
}

.spec-group-name {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--ink-3);
}

/* Scharniergaten en -belasting (AGENTS.md §9.1). A spec-row holds ONE value,
   and a hinge has five — two coordinates and three forces — so cramming them
   into a single nowrap cell would spill off a phone. A row per hinge instead,
   with the columns lining up, which is also the only way the bottom hinge is
   visibly the maatgevende one.

   The wrapper scrolls rather than the page: css elsewhere in this file assumes
   the left panel never scrolls sideways. */
.hinge-scroll {
    overflow-x: auto;
}

/* table-layout: fixed so the five value columns are EQUAL — with automatic
   layout each one sized itself to its own longest number, which put a 2 mm
   column beside a 4 mm one and made the table read as if the wide columns
   mattered more. The min-width is what the widest realistic row needs
   ("2350 mm", "-1234 N"); below it .hinge-scroll scrolls rather than the
   numbers being clipped. */
.hinge-table {
    width: 100%;
    min-width: 285px;
    table-layout: fixed;
    border-collapse: collapse;
    font-size: 11.5px;
}

.hinge-table th,
.hinge-table td {
    padding: 3px 4px;
    text-align: right;
    white-space: nowrap;
    overflow: hidden;
}

/* The row number is a label, not one of the five measurements, so it does not
   take an equal share. Everything else splits what is left. */
.hinge-table .hinge-nr {
    /* Wide enough for a row number AND the "dubbel" marker beside it. At the
       old 22px that word wrapped to "du." and read as a truncated unit rather
       than as a label; at 1% the browser squeezed the numbers themselves to
       slivers, because the value columns beside it are not fixed either. */
    width: 62px;
    white-space: nowrap;
}

/* Two coordinates and three forces: different quantities in different units,
   and reading across the row they ran together. A rule rather than a gap, so
   the split survives the columns being equal. */
.hinge-table .hinge-split {
    border-left: 1px solid var(--line);
    padding-left: 8px;
}

/* The unit rides with the number but must not compete with it: a column of
   values should read as a column of figures with the unit trailing. */
.hinge-table .unit {
    font-size: 9.5px;
    font-weight: 400;
    color: var(--ink-3);
}

.hinge-table thead th {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink-3);
    border-bottom: 1px solid var(--line-soft);
}

/* The row header is the hinge's number, which is a label and not a
   measurement — left in the UI face, like .spec-note. */
.hinge-table tbody th {
    font-family: var(--font-ui);
    font-weight: 600;
    color: var(--ink-3);
    text-align: left;
}

.hinge-table tbody td {
    font-family: var(--font-num);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--ink);
}

/* Corrections are sentences, not measurements, so they stay in the UI face
   and hang off a marker rather than lining up in a column. */
.note-list {
    display: grid;
    gap: 4px;
    font-size: 12px;
    line-height: 1.45;
    color: var(--ink-2);
}

.note-list li {
    position: relative;
    padding-left: 14px;
}

.note-list li::before {
    content: '';
    position: absolute;
    left: 2px;
    top: 0.55em;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--step-accent);
}

/* ------------------------------------------------------- geavanceerd */

.advanced {
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: 0 1px 2px rgba(15, 27, 35, 0.04);
    overflow: hidden;
}

.advanced-summary {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 14px;
    list-style: none;
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink-2);
}

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

.advanced-summary:hover {
    background-color: #fafcfd;
}

.advanced-summary:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: -2px;
}

.advanced-marker {
    width: 7px;
    height: 7px;
    flex: none;
    margin-left: 3px;
    border-right: 1.5px solid var(--ink-3);
    border-bottom: 1.5px solid var(--ink-3);
    transform: rotate(-45deg);
    transition: transform 0.15s;
}

/* ITS OWN summary's marker, not every marker inside it. A descendant selector
   here reaches the folds NESTED in this panel — the two Middelen contexts
   (§9.8) — and turned their arrows down whenever the panel itself was open,
   whatever those folds were actually doing. */
.advanced[open] > .advanced-summary .advanced-marker {
    transform: rotate(45deg);
}

.advanced-note {
    /* Hard right, not a gap after the title. "Middelen" names the block and
       GEAVANCEERD warns about it — two different kinds of word, and beside each
       other they read as one phrase however much air is between them. The badge
       keeps its own margin-left:auto and simply follows this one. */
    margin-left: auto;
    font-size: 10.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--ink-3);
}

/* "n aangepast". The panel is collapsed by default and its kozijn half
   hides again without a stelkozijn, so an override that survived a
   duplicatePosition would otherwise move manufacturing numbers from
   somewhere nobody can see. */
.badge-count {
    margin-left: auto;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--brand);
    color: #fff;
    font-family: var(--font-num);
    font-size: 10.5px;
    font-weight: 700;
}

.badge-count.hidden {
    display: none;
}

/* Two auto margins in one flex row SPLIT the free space between them, which
   parked GEAVANCEERD in the middle of the bar. The note owns the push to the
   right; a badge behind it just follows. */
.advanced-note + .badge-count {
    margin-left: 8px;
}

.advanced-body {
    display: grid;
    gap: 12px;
    padding: 0 14px 14px;
}

/* Scharnieren per ruit (AGENTS.md §9.1): one block per ruit, its NAME in bold,
   and its two fields captioned by what they are. The other way round — a column
   headed "Scharnierzijde" over rows captioned "Linkerruit" — put the shared word
   in bold and the ruit, which is what a fitter is actually looking for, in the
   small print under it. */
.hinge-fields {
    display: grid;
    gap: 12px;
}

/* The two fields of one ruit, side by side.

   auto-fit, so the block is right with one field as well as two — an eendeling
   has no per-ruit scharnierzijde, and a ruit that follows another's count has no
   count field. The floor is 112 px because the FORM COLUMN IS NARROW: 340 px on
   a 1280 px desktop, of which this block sees 266. Anything higher silently
   collapses the pair back into a stack; below ~240 px of container they stack
   anyway, which is right — two selects sharing 110 px is not a column, it is a
   clipped word. */
.hinge-ruit-fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
    gap: 10px 14px;
    align-items: end;
}

.hinge-ruit-fields > * {
    min-width: 0;
}

/* An empty slot must not claim a grid track, or a ruit with one field centres
   it over half the row. */
/* NOT A BOX. The slot exists so refreshHingeCounts() has somewhere to move the
   count row and the toggles to; `display: contents` hands both straight to
   .hinge-ruit-fields' grid, so they land SIDE BY SIDE instead of stacked. Two
   rows per ruit was the single biggest source of height in this block once the
   toggles arrived. */
.hinge-count-slot {
    display: contents;
}

.hinge-count-slot:empty {
    display: none;
}

/* Parked count fields (see refreshHingeCounts): in the DOM so setFormData can
   still write them, out of sight because no ruit is on that axis. */
.hinge-count-store {
    display: none;
}

/* The heading over a group of fields — here the ruit's own name. Louder than
   the .field-label under it, and set like .spec-group-name in the results
   panel, which does the same job there. */
.field-group-title {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--ink-3);
    padding-bottom: 5px;
    margin-bottom: 8px;
    border-bottom: 1px solid var(--line-soft);
}

/* A qualifier on a field's own caption — which other ruiten a count covers.
   Not bold and not in the label's colour: it modifies the caption, it is not
   part of the name. */
.field-note {
    font-weight: 400;
    color: var(--ink-3);
}

/* Said in place of a field, for the ruit that follows another's count. */
.field-note--follow {
    display: block;
    font-size: 11px;
    line-height: 1.4;
    align-self: end;
    padding-bottom: 7px;
}

/* A disclosure INSIDE a step, rather than a card of its own: the per-ruit
   scharnierzijden are one optional detail of the uitvoering above them, so
   they sit lighter than the mediation panel at the bottom of the form. */
.advanced--nested {
    border-color: var(--line-soft);
    box-shadow: none;
    background: #fbfcfd;
}

.advanced--nested .advanced-summary {
    padding: 8px 11px;
    font-size: 12px;
}

.advanced--nested .advanced-body {
    gap: 10px;
    padding: 0 11px 11px;
}

.advanced--nested .badge-count {
    background: var(--step-accent, var(--brand));
}

/* One block per product, in that product's colour — the same pair the
   form steps use, because it is the same distinction (§7). */
/* A context is a FOLD CARD, built like the ones in stap 4 (.field-fold) and
   keeping only its own colour: the wash and the accent are what say which
   PRODUCT's policy this is (§7 — mediation is spent per product), and that is
   the one thing about it that may not look like everything else. The padding
   therefore moves off this block and onto the header and the body, so the
   header bar spans the card the way a .field-fold-summary does. */
.mediation-context {
    display: grid;
    gap: 0;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.mediation-context.hidden {
    display: none;
}

.mediation-context--glas {
    background-color: var(--glas-wash);
    border-color: rgba(20, 136, 127, 0.18);
    --step-accent: var(--glas);
    --step-accent-ink: var(--glas-ink);
    --step-accent-tint: #d5eae7;
}

.mediation-context--hout {
    background-color: var(--hout-wash);
    border-color: rgba(180, 118, 42, 0.2);
    --step-accent: var(--hout);
    --step-accent-ink: var(--hout-ink);
    --step-accent-tint: #f5e7d0;
}

/* The header bar is a .field-fold-summary; these three lines are everything
   that differs from one in stap 4. The hover lift is white rather than the
   grey wash, because it has to read on both the glas and the hout background,
   and the marker takes the product's own ink so the arrow belongs to the block
   it opens. */
.mediation-context .field-fold-summary:hover {
    background-color: rgba(255, 255, 255, 0.5);
}

.mediation-context .advanced-marker {
    border-right-color: var(--step-accent-ink);
    border-bottom-color: var(--step-accent-ink);
}

.mediation-body {
    padding: 0 11px 11px;
}

.mediation-context-title {
    font-size: 12px;
    font-weight: 700;
    color: var(--step-accent-ink);
}

/* Mediatie-overrides (AGENTS.md §9.8).
 *
 * One grid per context, four columns: side name, max., binnen, buiten. TWO
 * CLASSES ON THE GRID DECIDE WHAT IS VISIBLE — `no-sides` hides the four
 * per-side rows and `no-split` hides the binnen/buiten columns — so the two
 * levels of specificity are one class toggle each rather than ten elements to
 * keep in step. A cell carrying both classes is hidden by either rule, which
 * is what makes the two axes independent.
 *
 * The template shrinks with the columns; leaving it at four would keep two
 * empty tracks and squeeze the inputs for nothing.
 */
.mediation-grid {
    display: grid;
    grid-template-columns: 3.75rem repeat(3, minmax(0, 1fr));
    gap: 4px 6px;
    align-items: center;
}

.mediation-grid.no-split {
    grid-template-columns: 3.75rem minmax(0, 1fr);
}

.mediation-grid.no-split .mediation-split,
.mediation-grid.no-sides .mediation-side {
    display: none;
}

/* ONE AXIS, TWO STATES (AGENTS.md §9.8). Collapsed is one row — *Alle*, max.
   only. Open is the four zijde rows with binnen/buiten, and the *Alle* row goes
   away: a value for "all sides" and a value per side are two answers to the
   same question, and showing both invites a fitter to give both.
   `no-sides` and `no-split` therefore always move together, and this is the
   rule that retires the Alle row when they do. ui-handlers.js carries the
   Alle value INTO the four rows on the way open, so nothing is lost by it
   disappearing. */
.mediation-grid:not(.no-sides) .mediation-all {
    display: none;
}

.mediation-grid input {
    /* Number inputs default to a font-size-derived width that overflows a
       narrow grid track on iOS; the track decides the width here. */
    width: 100%;
    min-width: 0;
    padding: 5px 7px;
    font-family: var(--font-num);
    font-variant-numeric: tabular-nums;
    font-size: 12px;
    color: var(--ink);
    background-color: var(--paper);
    border: 1px solid var(--line);
    border-radius: 6px;
    text-align: right;
    transition: border-color 0.15s, box-shadow 0.15s;
}

.mediation-grid input:focus {
    outline: none;
    border-color: var(--step-accent);
    box-shadow: 0 0 0 3px var(--brand-ring);
}

.mediation-head,
.mediation-row-label {
    font-size: 10.5px;
    font-weight: 600;
    color: var(--step-accent-ink, var(--ink-3));
    text-align: right;
}

.mediation-row-label {
    text-align: left;
}

.section-card {
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 14px;
    margin-bottom: 14px;
    box-shadow: 0 1px 2px rgba(15, 27, 35, 0.04);
}

/* Custom scrollbar */
.left-panel::-webkit-scrollbar,
.steps-sidebar::-webkit-scrollbar {
    width: 6px;
}

.left-panel::-webkit-scrollbar-track,
.steps-sidebar::-webkit-scrollbar-track {
    background: #f1f1f1;
    border-radius: 3px;
}

.left-panel::-webkit-scrollbar-thumb,
.steps-sidebar::-webkit-scrollbar-thumb {
    background: #888;
    border-radius: 3px;
}

.left-panel::-webkit-scrollbar-thumb:hover,
.steps-sidebar::-webkit-scrollbar-thumb:hover {
    background: #555;
}

/* ------------------------------------------------------------------ */
/* Responsive layout. The app is used on site (AGENTS.md §1.1): iPad   */
/* and desktop are both first-class targets.                           */
/* ------------------------------------------------------------------ */

/* Narrower desktops / iPad landscape: shrink the form column. */
@media (max-width: 1280px) {
    .main-container {
        grid-template-columns: 340px 1fr;
    }
}

/* iPad portrait and phones: stack everything vertically. The steps
   sidebar becomes a horizontal strip under the main drawing. */
@media (max-width: 1100px) {
    .main-container {
        display: block;
        height: auto;
    }

    .left-panel {
        padding-right: 0;
        overflow-y: visible;
        margin-bottom: 16px;
    }

    .right-panel {
        overflow: visible;
    }

    .results-layout {
        display: flex;
        flex-direction: column;
    }

    /* Parent height is auto when stacked, so the drawing needs its own. */
    .main-canvas .canvas-container {
        flex: none;
        height: min(58vh, 540px);
    }

    .steps-sidebar {
        flex-direction: row;
        overflow-x: auto;
        overflow-y: hidden;
        padding-right: 0;
        padding-bottom: 8px;
        -webkit-overflow-scrolling: touch;
    }

    /* A row of thumbnails that scrolls sideways, so these DO need a width —
       stretch has nothing to stretch to along the main axis. */
    .step-canvas-section {
        flex: 0 0 auto;
        width: 250px;
    }

    /* Second tab bar appears above the form. Once the layout stacks, the
       results panel is a full screen of scrolling away, and having to travel
       there to change position while measuring is the wrong trade. */
    .position-bar--mobile {
        display: flex;
    }

    /* Touch targets: comfortably tappable navigation. */
    .pane-nav button,
    .step-pane-nav button,
    #mainPaneNavigation button {
        padding: 10px;
    }

    /* The form on the device it is actually used on. 16px is not a taste
       call: below it, iOS Safari zooms the page on focus, and a fitter
       who has to pinch back out between two kruismaten will stop using
       the app. The extra padding buys the same fields a 44px target. */
    .compact-input,
    .measure-input,
    .end-input,
    .duo-input,
    .mediation-grid input {
        font-size: 16px;
    }

    .compact-input {
        padding: 11px 12px;
    }

    .compact-input--lead {
        font-size: 17px;
    }

    .compact-input--sm,
    .compact-input--xs,
    .end-input,
    .duo-input {
        padding: 9px 10px;
    }

    /* A 24px sliver is a fingernail. */
    .end-pair {
        grid-template-columns: minmax(0, 1fr) 34px;
    }

    .end-pair.is-split .end {
        grid-template-columns: 24px minmax(0, 1fr);
    }

    .end-key {
        font-size: 11px;
    }

    .end-pair.is-split .end-key {
        padding-left: 9px;
    }

    .end-pair:not(.is-split) .end--second .end-input {
        padding-left: 0;
        padding-right: 0;
    }

    .measure {
        grid-template-columns: 25px minmax(0, 1fr);
        padding-left: 8px;
    }

    .measure-input {
        padding: 12px 6px 12px 0;
        line-height: 22px;
    }

    /* TWIN of the measured-state size bump, and it is not optional. That rule
       is .measure:not(:has(...)) .measure-input, which weighs (0,3,0) and so
       beats the plain .measure-input 16px above at (0,1,0) — a filled field
       would fall back to its 15px desktop size and iOS would zoom the page the
       moment it was focused. §9.11's second trap, seen from the other side.
       Going UP from 16px is always safe; going down never is. */
    .measure:not(:has(:placeholder-shown)) .measure-input {
        font-size: 17px;
    }

    /* Padding + line-height + border = 12 + 22 + 12 + 2 = 48px, so the field
       still clears the 44px touch target this whole block exists to buy, and
       does so identically in all seven states. */

    /* Four digits at the 16 px this font has to keep to stop iOS zooming,
       plus the three-letter key. Three of these plus their gaps still have
       to leave the card room at both edges for the heights to sit outside
       them, which is what sets the ceiling here. */
    .kruis {
        --measure-w: 84px;
    }

    .mediation-grid input {
        padding: 9px 8px;
    }

    .check {
        padding: 12px;
    }

    /* A disclosure is tapped, so it gets a tappable row like everything else.
       .field-fold-summary is here for the same reason it copies the nested
       card's padding: the folds in stap 1 and 4 have to be the same control as
       the ones in stap 5, on every layout. */
    .advanced-summary,
    .advanced--nested .advanced-summary,
    .field-fold-summary {
        padding-top: 13px;
        padding-bottom: 13px;
    }

    .btn {
        min-height: 42px;
    }

    .btn--quiet {
        min-height: 36px;
        padding: 8px 12px;
    }

    .btn--tiny {
        min-height: 32px;
        padding: 7px 10px;
    }

    .step-body {
        gap: 16px;
    }

    .position-action {
        width: 40px;
        height: 40px;
    }

    .position-tab,
    .position-more-btn {
        padding-top: 14px;
        padding-bottom: 12px;
    }

    /* The three action buttons take ~133 px. On a 375 px phone that left a
       budget of 110 px for tabs — one tab, and everything else under Meer.
       Stacking the bar hands the full width back to the tabs. Column
       direction, not flex-wrap: wrapping let the actions share a line with
       the zero-basis tab list and collapse it to nothing. */
    .position-bar {
        flex-direction: column;
        align-items: stretch;
        padding-left: 0;
    }

    .position-row {
        min-height: 0;
        padding-left: 4px;
    }

    .position-actions {
        justify-content: flex-end;
        border-left: 0;
        border-top: 1px solid var(--line-soft);
        margin-left: 0;
        padding: 2px 4px;
    }

    /* Narrower tabs so more than one survives the overflow budget; long
       merken ellipsis rather than crowding everything else under Meer. */
    .position-tab {
        max-width: 116px;
        padding-left: 10px;
        padding-right: 10px;
    }

    /* "Meer" keeps its word — a bare badge and chevron reads as a status
       marker, not as something you can open. */
    .position-more-btn {
        padding-left: 8px;
        padding-right: 8px;
        gap: 3px;
    }

    .step-pane-nav button {
        padding: 6px;
    }
}

/* Narrow phones. There used to be a rule here stacking B over H inside each
   corner cell: at 375 px the old 2x2 grid left every field about 40 px of
   usable width, not enough for a four-digit millimetre at the 16 px the field
   has to keep to stop iOS zooming. That arrangement is gone, and so is the
   problem — a field is now sized to its content rather than to a share of the
   card, so it is the same width at 375 px as at 1400 px and there is nothing
   left to stack. What narrows instead is the space AROUND them. */
@media (max-width: 560px) {
    .kruis-row--widths {
        gap: 4px;
    }
}

/* Small phones: tighter steps strip. */
@media (max-width: 480px) {
    .main-canvas .canvas-container {
        height: 55vh;
    }

    /* Only the section width: the container is square by aspect-ratio and
       follows it, so there is one place that decides thumbnail size. */
    .step-canvas-section {
        width: 190px;
    }
}