/* ===========================================================================
   EmbedSheets v2 - Calculator frontend
   ---------------------------------------------------------------------------
   This is the visitor-facing calculator, NOT the SaaS admin interface. Every
   color and shape comes from a --calc-* variable so the theme system (Light,
   Dark, Black, Custom, Transparent) can restyle it without touching markup.

   Responsiveness is a core requirement: the calculator adapts to the width of
   whatever container or iframe it is embedded in, never to a fixed desktop
   width.
   =========================================================================== */

.calc {
    /* Light preset defaults. Theme presets and custom settings override these. */
    --calc-primary:             #5b5bd6;
    --calc-accent:              #7c6cf0;
    --calc-bg:                  #ffffff;
    --calc-surface:             #f7f8fa;
    --calc-text:                #1a2231;
    --calc-muted-text:          #5c6b82;
    --calc-heading:             #0f172a;
    --calc-label:               #1a2231;
    --calc-border:              #dfe4ec;
    --calc-input-bg:            #ffffff;
    --calc-input-text:          #1a2231;
    --calc-input-border:        #cdd5e0;
    --calc-input-focus-border:  #5b5bd6;
    --calc-placeholder:         #8695ab;
    --calc-primary-button-bg:   #5b5bd6;
    --calc-primary-button-text: #ffffff;
    --calc-result-bg:           #f2f3fd;
    --calc-result-text:         #1a2231;
    --calc-result-label:        #3f3f8a;
    --calc-result-border:       #dcdffb;

    --calc-disabled-bg:         #f2f4f7;
    --calc-disabled-text:       #98a2b3;
    --calc-error:               #cf3b3b;

    --calc-button-border:       #5b5bd6;
    --calc-button-hover-bg:     #4f4fc4;
    --calc-button-hover-text:   #ffffff;

    --calc-input-border-width:  1px;
    --calc-border-radius:       8px;
    --calc-button-radius:       8px;
    --calc-result-radius:       8px;
    --calc-gap:                 16px;

    /* Shape. See the "layout" group in theme_schema(). */
    --calc-max-width:           1200px;
    --calc-card-radius:         12px;
    --calc-card-border:         #dfe4ec;

    box-sizing: border-box;
    /*
     * A column of rows, and flex rather than block for one reason: adjacent
     * block margins COLLAPSE. With "space above" and "space below" both set to
     * 14 the gap between two fields was 14 and not 28, so setting one of them
     * to the value the other already had appeared to do nothing at all. Flex
     * items do not collapse, so both settings are additive and each one
     * visibly does what its label says.
     */
    display: flex;
    flex-direction: column;
    background: var(--calc-bg);
    color: var(--calc-text);
    /* The reader's own system font unless the theme names one. See
       theme_font_stack() - the variable carries its own fallbacks. */
    font-family: var(--calc-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
    font-size: 15px;
    line-height: 1.55;
    /* Named so a flush row can pull out by exactly this much. */
    --calc-padding: 20px;
    padding: var(--calc-padding);
    /* Its own corners, not the text boxes' - see the layout group in theme_schema(). */
    border-radius: var(--calc-outer-radius, var(--calc-border-radius));
    container-type: inline-size;

    /*
     * A ceiling, not a width. On anything narrower than the limit this does
     * nothing at all, which is why it needs no media query to be "desktop
     * only" - a phone never reaches 1200px to be constrained by it.
     */
    max-width: var(--calc-max-width);
    margin-left: auto;
    margin-right: auto;
}

.calc--transparent { background: transparent; --calc-padding: 0px; padding: 0; }

/*
 * The calculator as a card.
 *
 * Off by default: embedded in somebody's page, a calculator usually wants to
 * look like part of it rather than a panel dropped on top. Turned on, it gets
 * the edge and lift that separate it from whatever it is sitting on.
 *
 * Never combined with transparent - a card is a border around a fill, and
 * transparent is the absence of one. theme_sanitize() refuses the pair.
 */
.calc--card {
    border: 1px solid var(--calc-card-border);
    border-radius: var(--calc-card-radius);
    box-shadow: 0 1px 3px rgba(16, 24, 40, .06), 0 10px 28px rgba(16, 24, 40, .07);
    --calc-padding: 28px;
    padding: var(--calc-padding);
}

@media (max-width: 560px) {
    /* A card's generous padding costs too much of a narrow screen. */
    .calc--card { --calc-padding: 18px; padding: var(--calc-padding); }
}

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

/* ---------------------------------------------------------------------------
   Rows and columns
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   Where the spare room in a row goes

   Only means anything once the columns are narrower than the row: two at 40%
   leave a fifth of it over. Filling the row is what it has always done, so
   that is the case with no class at all and every row already built keeps the
   layout it has.
   --------------------------------------------------------------------------- */

.calc-row--justify-start   { justify-content: flex-start; }
.calc-row--justify-center  { justify-content: center; }
.calc-row--justify-end     { justify-content: flex-end; }
.calc-row--justify-between { justify-content: space-between; }
.calc-row--justify-around  { justify-content: space-around; }
/* Equal gaps, the ends included - which is what "equally spaced" means to
   somebody looking at it, and what space-around does not do. */
.calc-row--justify-evenly  { justify-content: space-evenly; }

/*
 * A column only stops filling the row when it has been given a width of its
 * own - see calc_column_style(). Without this the columns grow to take the
 * space up and there is none left to distribute, so the setting above would
 * describe nothing.
 */
.calc-row[class*="calc-row--justify-"] > .calc-col { flex-grow: var(--calc-col-grow, 1); }

.calc-row {
    display: flex;
    flex-wrap: wrap;
    /* Between COLUMNS, side to side. The vertical rhythm is the margins. */
    gap: var(--calc-gap);
    /*
     * The space above and below a field block.
     *
     * On the row rather than on .calc-el, because almost every field sits
     * alone in its own column - where .calc-el is both :first-child and
     * :last-child and therefore has both margins zeroed. The setting was
     * real, the variable was read, and the rule it landed on could never
     * apply. What actually separated two fields was this margin.
     */
    margin-top: var(--calc-el-space-top, 0);
    margin-bottom: var(--calc-el-space-bottom, var(--calc-gap));
    background: var(--calc-row-bg, transparent);
    /*
     * How much of the calculator the row takes, and where it sits in what is
     * left. Both default to what a row has always been - the whole width, so
     * the alignment has nothing to do - and the property is written only when
     * a row was actually narrowed, so no rule here changes an existing page.
     *
     * Centred is the default because a band narrower than its container almost
     * always wants to be; left and right are classes, so the common case
     * carries no class at all.
     */
    width: var(--calc-row-width, 100%);
    margin-left: auto;
    margin-right: auto;
}

.calc-row--left  { margin-right: auto; margin-left: 0; }
.calc-row--right { margin-left: auto; margin-right: 0; }

/*
 * A row that runs to the edges.
 *
 * The calculator has padding so its fields do not touch the border; a banner
 * across the top wants the opposite. Pulled out by the padding with negative
 * margins rather than by removing the padding, because the padding belongs to
 * the calculator and every other row still needs it.
 *
 * The padding is read from a variable rather than repeated, so the two cannot
 * drift apart - and a card's larger padding is pulled out by exactly its own
 * amount. The first such row also loses the gap beneath the top edge, which
 * is what makes it sit flush rather than nearly flush.
 */
.calc-row--flush {
    margin-left: calc(var(--calc-padding, 20px) * -1);
    margin-right: calc(var(--calc-padding, 20px) * -1);
    width: calc(100% + (var(--calc-padding, 20px) * 2));
}

/*
 * Running to the edges and taking part of the width are contradictory asks.
 * The edges win, because a banner is the more specific thing to have wanted -
 * and the builder says so where the two settings are offered.
 */
.calc-row--flush.calc-row--left,
.calc-row--flush.calc-row--right {
    margin-left: calc(var(--calc-padding, 20px) * -1);
    margin-right: calc(var(--calc-padding, 20px) * -1);
}

.calc-row--flush:first-child {
    margin-top: calc(var(--calc-padding, 20px) * -1);
}

.calc-row--flush:last-child {
    margin-bottom: calc(var(--calc-padding, 20px) * -1);
}

/*
 * Nothing inside it keeps a rounded corner of its own except at the very top,
 * where the picture has to follow the calculator's own corners or it will
 * square them off from the inside.
 */
.calc-row--flush .calc-col { padding: 0; }

.calc-row--flush:first-child .calc-image-header,
.calc-row--flush:first-child .calc-image-header img,
.calc-row--flush:first-child > .calc-col > .calc-el:first-child img {
    border-top-left-radius: inherit;
    border-top-right-radius: inherit;
}

/* It stays in the DOM so its controls and results remain available to the
   calculation runtime, while the complete row stays invisible to visitors. */
.calc-row--hidden { display: none !important; }

/*
 * Hidden by a conditional rule.
 *
 * One class for all three things a rule can govern - a row, a step, an
 * element - because the rule does not care which it is pointing at, and one
 * selector is what lets the runtime toggle them all the same way.
 *
 * !important because it has to beat whatever the thing would otherwise be:
 * a step carries display from .calc-step, an element from its own type. The
 * element stays in the document either way, so its value still reaches the
 * formulas - the same bargain .calc-row--hidden above already makes.
 */
.calc-cond-hidden { display: none !important; }

/* ---------------------------------------------------------------------------
   Showing and hiding by screen size

   The three widths here must stay in step with ES_BREAKPOINT_TABLET and
   ES_BREAKPOINT_MOBILE in inc/elements.php, which is what the settings panel
   quotes when it explains what "tablet" means. CSS cannot read a PHP constant,
   so this is the one place the numbers are repeated on purpose.

   Container queries, so the question is how wide the CALCULATOR is: an embed
   in a 400px sidebar is a phone layout whatever the screen around it is doing.
   Anything hidden here is still in the page and still answers formulas -
   display:none, exactly as a hidden row and a hidden field already work.
   --------------------------------------------------------------------------- */

@container (min-width: 769px) {
    .calc-hide-desktop { display: none !important; }
}

@container (min-width: 481px) and (max-width: 768px) {
    .calc-hide-tablet { display: none !important; }
}

@container (max-width: 480px) {
    .calc-hide-mobile { display: none !important; }
}

/* Without container queries the viewport is the only width available. Wrong
   for a narrow embed on a wide page, but an element the owner marked
   "not on phones" still disappears on a phone, which is the point of it. */
@supports not (container-type: inline-size) {
    @media (min-width: 769px) {
        .calc-hide-desktop { display: none !important; }
    }

    @media (min-width: 481px) and (max-width: 768px) {
        .calc-hide-tablet { display: none !important; }
    }

    @media (max-width: 480px) {
        .calc-hide-mobile { display: none !important; }
    }
}

/*
 * The space is BETWEEN blocks; the calculator's padding handles the ends.
 *
 * A flush row is excluded because it pulls itself out past the padding with a
 * negative margin, and zeroing that would leave a banner sitting a padding's
 * width inside the edge it is supposed to run to.
 */
.calc-row:first-child:not(.calc-row--flush) { margin-top: 0; }
.calc-row:last-child { margin-bottom: 0; }

/*
 * A column takes a share of the row by default. Give it a custom width and
 * --calc-col-grow drops to 0 and --calc-col-basis holds the width, so the
 * column stops sharing and takes exactly what it was told to. Shrink stays at
 * 1 either way: a fixed column that cannot shrink pushes the row wider than
 * the embed it is in.
 */
.calc-col {
    flex: var(--calc-col-grow, var(--calc-col-span, 12)) 1 var(--calc-col-basis, 0);
    min-width: 0;
    /* Same reason as .calc - so two elements stacked in one column are
       separated by both settings rather than by the larger of them. */
    display: flex;
    flex-direction: column;
    /* Transparent unless the row settings gave it one, so a column with no
       background of its own shows whatever is behind it. */
    background: var(--calc-col-bg, transparent);
    /* None unless the column asked for one, so a column nobody styled is drawn
       exactly as it always was. */
    border: var(--calc-col-border, 0);
    border-radius: var(--calc-col-radius, 0);
    box-shadow: var(--calc-col-shadow, none);
    padding:
        var(--calc-col-pad-top, 0)
        var(--calc-col-pad-right, 0)
        var(--calc-col-pad-bottom, 0)
        var(--calc-col-pad-left, 0);
}

/* ---------------------------------------------------------------------------
   A column lifted off the page, and where its content sits

   A shadow is four lengths and a colour, which is not a thing anybody wants to
   be asked for - three depths cover what people mean by "lift it a bit" and
   none of them can be written wrong.
   --------------------------------------------------------------------------- */

.calc-col--shadow-soft   { --calc-col-shadow: 0 1px 2px rgba(16, 24, 40, .06); }
.calc-col--shadow-medium { --calc-col-shadow: 0 2px 6px rgba(16, 24, 40, .09),
                                              0 8px 20px rgba(16, 24, 40, .07); }
.calc-col--shadow-strong { --calc-col-shadow: 0 4px 10px rgba(16, 24, 40, .12),
                                              0 18px 40px rgba(16, 24, 40, .12); }

/*
 * Where the content sits, once there is room to sit anywhere - a short column
 * beside a tall one, or one given height by what is next to it.
 *
 * A column is a flex column, so the two axes are the two properties the wrong
 * way round: down the column is justify-content, across it is align-items.
 * Writing them the intuitive way round is the mistake this comment exists to
 * stop somebody making.
 */
.calc-col--y-top    { justify-content: flex-start; }
.calc-col--y-middle { justify-content: center; }
.calc-col--y-bottom { justify-content: flex-end; }

/*
 * And the three that share the room out rather than pushing everything one
 * way. They only mean something with more than one element in the column, and
 * only when it is taller than its contents - which is what a column beside a
 * taller one is.
 */
.calc-col--y-between { justify-content: space-between; }
.calc-col--y-around  { justify-content: space-around; }
.calc-col--y-evenly  { justify-content: space-evenly; }

.calc-col--x-left   { align-items: flex-start; }
.calc-col--x-center { align-items: center; }
.calc-col--x-right  { align-items: flex-end; }

/*
 * An element inside an aligned column stops filling the width, or "centred"
 * would centre a block that is already as wide as the column and nothing would
 * move. Only where an alignment was asked for: everything else keeps the full
 * width it has always had.
 */
.calc-col--x-left  > .calc-el,
.calc-col--x-center > .calc-el,
.calc-col--x-right > .calc-el { width: auto; max-width: 100%; }

.calc-google-map-wrap { width: 100%; }
.calc-google-map {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    max-width: 100%;
    overflow: hidden;
    border: 1px solid var(--calc-border);
    border-radius: var(--calc-border-radius);
    background: var(--calc-surface);
    color: var(--calc-muted-text);
}
.calc-google-map iframe { width: 100%; height: 100%; border: 0; }

/* Markers mode is a still image from the Static Maps API - the only way to
   pin an arbitrary set of addresses. It fills the box the owner sized. */
.calc-google-map__image { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Rating --------------------------------------------------------------------
   Five stars, each optionally worth a half. A star is a POSITION; the radios
   inside it are laid over its two sides, so ten values still show five stars.

   Written left to right, unlike the old reversed-row trick: that only worked
   because "every star after the checked one" could be coloured with a sibling
   selector, which cannot express "half of this star". :has() lets each star
   ask what the whole group is worth, which is the real question. */

.calc-rating {
    display: flex;
    gap: 2px;
    width: max-content;
    font-size: 30px;
    line-height: 1;
}

.calc-rating__star { position: relative; display: inline-block; width: 1em; height: 1em; }

/* Out of sight, not out of the page: a hidden input is not focusable, and
   these are what the keyboard and the screen reader operate. */
.calc-rating__pick { position: absolute; width: 1px; height: 1px; opacity: 0; }

/* The click targets, laid over the star. Half on the left, whole on the
   right; with halves off, one target covers the whole star. */
.calc-rating__hit { position: absolute; top: 0; bottom: 0; z-index: 2; cursor: pointer; }
.calc-rating__hit--half  { left: 0; width: 50%; }
.calc-rating__hit--whole { right: 0; width: 50%; }
.calc-rating:not(.calc-rating--half) .calc-rating__hit--whole { left: 0; width: 100%; }

.calc-rating__glyph { color: #d9dee7; }

/* The figure beside the question. Tabular digits so 3.0 and 4.5 are the same
   width and the label does not shuffle as stars are clicked. */
.calc-rating__value {
    margin-left: 6px;
    color: var(--calc-muted-text);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.calc-rating__value[hidden] { display: none; }

/* The gold star, clipped to how much of THIS star is earned. Absolute over
   the grey one, so the two line up exactly whatever the font does. */
.calc-rating__fill {
    position: absolute;
    inset: 0;
    color: #f5b301;
    overflow: hidden;
    width: 0;
    pointer-events: none;
}

/*
 * How much of each star is filled.
 *
 * A star is full when itself or any star after it is chosen, and half when its
 * own half is the choice. :has() on the row is what makes "any star after"
 * expressible without reversing the markup.
 */
.calc-rating__star:has(~ .calc-rating__star .calc-rating__pick:checked) .calc-rating__fill,
.calc-rating__star:has(.calc-rating__pick--whole:checked) .calc-rating__fill { width: 100%; }

.calc-rating__star:has(.calc-rating__pick--half:checked) .calc-rating__fill { width: 50%; }

/* Hover previews the same way, so what you are about to choose is what you
   see. Pointer-only: a touch has no hover and would leave a star stuck gold. */
@media (hover: hover) {
    .calc-rating__star:has(~ .calc-rating__star .calc-rating__hit:hover) .calc-rating__fill,
    .calc-rating__star:has(.calc-rating__hit--whole:hover) .calc-rating__fill { width: 100%; }

    .calc-rating__star:has(.calc-rating__hit--half:hover) .calc-rating__fill { width: 50%; }
}

.calc-rating__pick:focus-visible ~ .calc-rating__hit {
    outline: 2px solid var(--calc-input-focus-border);
    outline-offset: 2px;
}

/*
 * Without :has() the fill cannot be worked out in CSS at all, so the stars
 * would all sit grey. The runtime sets --calc-rating-fill on each star as a
 * fallback; where :has() works the rules above win, and where it does not this
 * is the whole mechanism.
 */
@supports not selector(:has(*)) {
    .calc-rating__fill { width: var(--calc-rating-fill, 0); }
}

/*
 * Container queries, so stacking follows the width the calculator actually
 * has - not the width of the browser window. This is what makes an embed in a
 * narrow sidebar behave correctly on a wide desktop screen.
 */
@container (max-width: 520px) {
    .calc-col { flex: 1 1 100%; }
}

/* Fallback for browsers without container query support. */
@supports not (container-type: inline-size) {
    @media (max-width: 560px) {
        .calc-col { flex: 1 1 100%; }
    }
}

.calc-el {
    margin-top: var(--calc-el-space-top, 0);
    margin-bottom: var(--calc-el-space-bottom, var(--calc-gap));
}

/* The space is BETWEEN fields, so the ends of a column keep their own edge
   rather than gaining a gap the calculator's padding already provides. */
.calc-el:first-child { margin-top: 0; }
.calc-el:last-child { margin-bottom: 0; }
.calc-el--hidden { display: none; }

/*
 * ...unless it has something to say.
 *
 * A drive-distance field is hidden by default - it is a number feeding a
 * formula, not something a visitor reads - so a message written into it would
 * have been invisible in exactly the case that needs reporting: two addresses
 * with no route between them, or a lookup that failed. The container comes
 * back for the message; the value inside it is an <input type="hidden"> and
 * stays unseen either way.
 */
.calc-el--hidden:has(.calc-google-distance__error) { display: block; }
.calc-google-distance__error { color: var(--calc-error); }

/* ---------------------------------------------------------------------------
   Content
   --------------------------------------------------------------------------- */

.calc-heading { color: var(--calc-heading); margin: 0 0 8px; line-height: 1.3; font-weight: 640; }

/*
 * Sizes for the heading levels. Without these a heading inside an embed
 * inherits whatever the host page decided h2 and h3 look like, so the Size
 * setting appeared to do nothing on some sites and something on others.
 */
h2.calc-heading { font-size: 24px; }
h3.calc-heading { font-size: 19px; }
h4.calc-heading { font-size: 16px; }
h1.calc-heading { font-size: 30px; }
h5.calc-heading { font-size: 14px; }
h6.calc-heading { font-size: 13px; }

/* ---------------------------------------------------------------------------
   Image header
   A banner for the top of a calculator: picture, wash, words.
   --------------------------------------------------------------------------- */

/*
 * A stacking context of its own, so the three layers order themselves here and
 * cannot be reached by z-index from anywhere else on the host page.
 */
.calc-imghdr {
    position: relative;
    isolation: isolate;
    display: flex;
    overflow: hidden;
    height: var(--calc-hdr-h, auto);
    /*
     * Corners per pair, so a banner can be round at the top and square at
     * the bottom where it meets the fields. Both default to the
     * calculator's own corners when the element says nothing.
     */
    border-radius:
        var(--calc-hdr-radius-top, var(--calc-outer-radius, var(--calc-border-radius)))
        var(--calc-hdr-radius-top, var(--calc-outer-radius, var(--calc-border-radius)))
        var(--calc-hdr-radius-bottom, var(--calc-outer-radius, var(--calc-border-radius)))
        var(--calc-hdr-radius-bottom, var(--calc-outer-radius, var(--calc-border-radius)));
}

/*
 * With no height set, height: 100% resolves against an indefinite parent and
 * comes out as the picture's own height - so the banner is as tall as the
 * picture, and the same declaration covers both cases without a rule asking
 * whether a height was given.
 */
.calc-imghdr__image {
    width: 100%;
    height: 100%;
    object-fit: var(--calc-hdr-fit, cover);
    object-position: center var(--calc-hdr-focal, center);
    display: block;
}

/* Standing in for the picture until there is one. Muted rather than branded:
   it is a gap waiting to be filled, not a design decision. */
.calc-imghdr--noimage {
    min-height: 140px;
    background: linear-gradient(135deg, #334155 0%, #1e293b 100%);
}

.calc-imghdr__wash { position: absolute; inset: 0; pointer-events: none; }

/*
 * The words sit over the picture. Absolute rather than a flex child, so the
 * height of the text can never push the banner taller than it was asked to be
 * - a long description scrolls out of a fixed header rather than resizing it.
 */
.calc-imghdr__text {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: 6px;
    padding: clamp(16px, 4cqi, 32px);
}

.calc-imghdr--top .calc-imghdr__text    { justify-content: flex-start; }
.calc-imghdr--middle .calc-imghdr__text { justify-content: center; }
.calc-imghdr--bottom .calc-imghdr__text { justify-content: flex-end; }

.calc-imghdr--left .calc-imghdr__text   { text-align: left; align-items: flex-start; }
.calc-imghdr--center .calc-imghdr__text { text-align: center; align-items: center; }
.calc-imghdr--right .calc-imghdr__text  { text-align: right; align-items: flex-end; }

/*
 * Sizes scale with the width of the embed, not the visitor's screen. clamp()
 * keeps a heading readable in a narrow sidebar and stops it becoming a poster
 * in a wide one - and an explicit font size in the settings overrides all of
 * it, because that is written on the element itself.
 */
.calc-imghdr__heading {
    margin: 0;
    font-size: clamp(20px, 5cqi, 34px);
    font-weight: 700;
    line-height: 1.2;
    color: #fff;
}

.calc-imghdr__sub {
    margin: 0;
    font-size: clamp(14px, 2.6cqi, 18px);
    font-weight: 500;
    line-height: 1.35;
    color: #fff;
}

.calc-imghdr__desc {
    margin: 0;
    max-width: 62ch;
    font-size: clamp(13px, 2.2cqi, 15px);
    line-height: 1.5;
    color: #fff;
}

/* Where cqi is unsupported the clamp is ignored entirely, so a plain size is
   given first for the header text to fall back to. */
@supports not (width: 1cqi) {
    .calc-imghdr__heading { font-size: 26px; }
    .calc-imghdr__sub     { font-size: 16px; }
    .calc-imghdr__desc    { font-size: 14px; }
}

@container (max-width: 768px) {
    .calc-imghdr { height: var(--calc-hdr-h-tab, var(--calc-hdr-h, auto)); }
}

@container (max-width: 480px) {
    .calc-imghdr { height: var(--calc-hdr-h-mob, var(--calc-hdr-h-tab, var(--calc-hdr-h, auto))); }
}

@supports not (container-type: inline-size) {
    @media (max-width: 768px) {
        .calc-imghdr { height: var(--calc-hdr-h-tab, var(--calc-hdr-h, auto)); }
    }

    @media (max-width: 480px) {
        .calc-imghdr { height: var(--calc-hdr-h-mob, var(--calc-hdr-h-tab, var(--calc-hdr-h, auto))); }
    }
}

/* Heading and sub heading travel together, so the spacing sits on the pair. */
.calc-headingblock { margin: 0 0 8px; }
.calc-headingblock .calc-heading { margin-bottom: 2px; }

/*
 * A heading with an icon beside it.
 *
 * The words go into a column of their own, so the icon sits against the pair
 * rather than against the first line - which is what somebody means by putting
 * a heading and its description next to an icon.
 *
 * Only this variant is a flex row. A heading without an icon is left exactly
 * as it was, because every calculator already built is one.
 */
.calc-headingblock--icon {
    display: flex;
    align-items: flex-start;
    gap: 14px;
}

.calc-heading__text { min-width: 0; }

/*
 * Sized from the heading unless the owner said otherwise, and nudged down by a
 * hair: an icon set to the same size as the text beside it sits optically high
 * against a capital letter.
 */
.calc-heading__icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-top: 2px;
    font-size: 1.5em;
    line-height: 1;
    color: var(--calc-heading);
}

/* A picture rather than a glyph - see es_icon_markup(). */
.calc-heading__icon .calc-icon-img {
    width: 1.5em;
    height: 1.5em;
    object-fit: contain;
    display: block;
}

/*
 * Narrow enough that a row would leave the words in a column two words wide.
 * The icon goes above them instead, keeping whatever alignment was chosen.
 */
@container (max-width: 340px) {
    .calc-headingblock--icon {
        flex-direction: column;
        gap: 8px;
    }
}

.calc-subheading {
    margin: 0;
    color: var(--calc-muted-text);
    font-size: 14px;
    font-weight: 400;
    line-height: 1.5;
}
.calc-text { color: var(--calc-muted-text); margin: 0; }
/*
 * Image sizing, in three steps.
 *
 * The element writes --calc-img-* onto the image; these rules decide which of
 * them apply at the width the calculator is actually being shown at. Container
 * queries, not media queries: an embedded calculator can be 400px wide inside a
 * 1400px page, and the visitor's screen width says nothing about the space the
 * image has to work with.
 *
 * A tablet or mobile value left blank falls through to the one above it, which
 * is what the nested var() fallbacks are doing.
 */
.calc-image {
    max-width: 100%;
    width: var(--calc-img-w, auto);
    height: var(--calc-img-h, auto);
    object-fit: var(--calc-img-fit, fill);
    object-position: center var(--calc-img-focal, center);
    border-radius: var(--calc-img-radius, var(--calc-border-radius));
}

/*
 * Only present when the image carries a wash, which needs something positioned
 * to sit inside. inline-block so the wrapper is the size of the picture rather
 * than the width of the column - otherwise the wash would extend past the
 * image it is meant to be tinting.
 */
.calc-imagewrap { position: relative; display: inline-block; max-width: 100%; line-height: 0; }

.calc-image__wash {
    position: absolute;
    inset: 0;
    pointer-events: none;
    border-radius: var(--calc-img-radius, var(--calc-border-radius));
}

/*
 * Filling the column, one link at a time.
 *
 * The row already stretches its columns to equal height, so the height is
 * there to be claimed - it just has to be passed down. The column becomes a
 * flex column, the image's element is the part that grows, and the image is
 * told to fill what it ends up with. min-height: 0 lets the element shrink
 * below its content, without which a tall image would push the column taller
 * than the one it is meant to match.
 */
.calc-col--fill { display: flex; flex-direction: column; }

.calc-col--fill > .calc-el:has(.calc-imagefill) { flex: 1 1 auto; min-height: 0; }

.calc-imagefill { height: 100%; min-height: inherit; }

/* A filling image with a wash: the wrapper has to carry the height down too,
   or the picture fills the column and the tint stays the size of nothing. */
.calc-imagefill > .calc-imagewrap { display: block; height: 100%; }

.calc-image--fill {
    width: 100%;
    height: 100%;
    min-height: 160px;
    object-fit: var(--calc-img-fit, cover);
}

/*
 * Without :has(), the column still stacks its children in a flex column and
 * the image keeps its natural proportions - the layout is ordinary rather
 * than broken, which is the right way for this to degrade.
 */

@container (max-width: 768px) {
    .calc-image {
        width: var(--calc-img-w-tab, var(--calc-img-w, auto));
        height: var(--calc-img-h-tab, var(--calc-img-h, auto));
    }
}

/* Stacked, a column has no neighbour to match, and a full-height image would
   be as tall as the questions it sits above. It goes back to a sane band. */
@container (max-width: 560px) {
    .calc-image--fill { height: auto; max-height: 240px; }
}

@container (max-width: 480px) {
    .calc-image {
        width: var(--calc-img-w-mob, var(--calc-img-w-tab, var(--calc-img-w, auto)));
        height: var(--calc-img-h-mob, var(--calc-img-h-tab, var(--calc-img-h, auto)));
    }
}

/* Where container queries are missing, the viewport is the closest thing to an
   answer available - wrong for a narrow embed on a wide page, but better than
   an image that ignores its mobile size entirely. */
@supports not (container-type: inline-size) {
    @media (max-width: 768px) {
        .calc-image {
            width: var(--calc-img-w-tab, var(--calc-img-w, auto));
            height: var(--calc-img-h-tab, var(--calc-img-h, auto));
        }
    }

    @media (max-width: 480px) {
        .calc-image {
            width: var(--calc-img-w-mob, var(--calc-img-w-tab, var(--calc-img-w, auto)));
            height: var(--calc-img-h-mob, var(--calc-img-h-tab, var(--calc-img-h, auto)));
        }
    }
}

.calc-divider { border: 0; border-top: 1px solid var(--calc-border); }
.calc-divider--small  { margin: 8px 0; }
.calc-divider--medium { margin: 18px 0; }
.calc-divider--large  { margin: 30px 0; }

.calc-empty { color: var(--calc-muted-text); text-align: center; padding: 32px; }

/* ---------------------------------------------------------------------------
   Fields
   --------------------------------------------------------------------------- */

.calc-label {
    display: block;
    margin-bottom: 6px;
    font-size: var(--calc-label-size, 14px);
    font-weight: var(--calc-label-weight, 560);
    color: var(--calc-label);
}

.calc-required { color: var(--calc-error); }

/*
 * The mark that says "there is a note on this question".
 *
 * It was a typed "?" in a drawn circle - the icon carries its own circle, so
 * the border and the background come off and what is left is the glyph. Kept
 * at the label's own colour rather than the icon's usual muted grey: beside a
 * question it has to be findable, and it is already small.
 */
.calc-tip {
    display: inline-flex;
    align-items: center;
    color: var(--calc-muted-text);
    font-size: 13px;
    line-height: 1;
    cursor: help;
    vertical-align: -1px;
    opacity: .8;
}

.calc-tip:hover { opacity: 1; }

.calc-input {
    width: 100%;
    /* An input with no icon draws its own box, so it needs its own inset. The
       rule below takes this back when the input sits inside a field wrapper,
       where the affix has already set the distance from the border. */
    padding: 10px 12px;
    font-family: inherit;
    font-size: var(--calc-input-size, 15px);
    font-weight: var(--calc-input-weight, 400);
    color: var(--calc-input-text);
    background: var(--calc-input-bg);
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: var(--calc-border-radius);
    transition: border-color .12s ease, box-shadow .12s ease;
}

/* Sized on its own: a prompt is not the answer, and somebody who wants
   theirs quieter than what gets typed should be able to have that. */
.calc-input::placeholder {
    color: var(--calc-placeholder);
    font-size: var(--calc-placeholder-size, 15px);
}

.calc-input:focus {
    outline: none;
    border-color: var(--calc-input-focus-border);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--calc-input-focus-border) 22%, transparent);
}

/* Disabled and read-only are themed states, not a blanket opacity. */
.calc-input:disabled,
.calc-input[readonly] {
    background: var(--calc-disabled-bg);
    color: var(--calc-disabled-text);
    cursor: not-allowed;
}

.calc-input[aria-invalid="true"] { border-color: var(--calc-error); }

.calc-select { appearance: none; padding-right: 34px;
    background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                      linear-gradient(135deg, currentColor 50%, transparent 50%);
    background-position: calc(100% - 18px) 52%, calc(100% - 13px) 52%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
}

.calc-help { display: block; margin-top: 5px; font-size: 13px; color: var(--calc-muted-text); }

/* Attachments ---------------------------------------------------------------
   A file is sent the moment it is chosen, so the visitor needs to be told
   what is happening to it. Without this line an upload of any size looks
   exactly like nothing happening, and people pick the file again. */

.calc-file__status {
    display: block;
    margin-top: 5px;
    font-size: 13px;
    color: var(--calc-muted-text);
    overflow-wrap: anywhere;
}

.calc-file__status.is-error { color: var(--calc-error, #b42318); }

/* Disabled while its file is in flight, so the same file is not sent twice. */
.calc-input--file:disabled { opacity: .6; cursor: progress; }

/* Terms checkbox ------------------------------------------------------------ */

/*
 * One box and the sentence it agrees to.
 *
 * The whole line is the label, so the words are as clickable as the box - a
 * 14px target beside a sentence somebody has just read is the wrong thing to
 * ask them to hit, and it is the last thing they do before submitting.
 */
.calc-terms__row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 0;
    cursor: pointer;
}

/* Nudged onto the first line's baseline; a checkbox is centred in its own box
   and the text beside it is not. */
.calc-terms__row input { margin: 2px 0 0; flex: 0 0 auto; cursor: pointer; }

.calc-terms__text {
    font-size: 14px;
    line-height: 1.5;
    color: var(--calc-text);
}

/*
 * A link inside a sentence has to look like one, and not only by its colour:
 * this is a page somebody is being asked to read BEFORE agreeing to it, and
 * colour alone is invisible to a reader who cannot tell these two apart.
 *
 * The underline is dotted and is not a setting. It is the one mark that says
 * "these words go somewhere" while leaving the sentence looking like a
 * sentence - a solid rule through a consent line reads as emphasis - and an
 * owner able to turn it off would be able to hide, by accident, the only clue
 * that there is something to read.
 *
 * The colour follows the theme unless one was chosen: a consent line often
 * sits on a coloured panel where the theme's link colour is not readable.
 */
.calc-terms__link {
    color: var(--calc-terms-link, var(--calc-primary));
    text-decoration: underline dotted;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
}

/* Solid on hover and on focus, so the pointer and the keyboard both get the
   same acknowledgement. */
.calc-terms__link:hover,
.calc-terms__link:focus-visible { text-decoration-style: solid; }

.calc-terms__required { margin-left: 2px; color: var(--calc-error, #cf3b3b); }

/* Input table --------------------------------------------------------------- */

/*
 * A grid of questions sharing one set of answers.
 *
 * The wrapper scrolls, the table does not shrink. A twelve-column grid in a
 * 400px embed has to go somewhere, and the two other places it could go are
 * both worse: squeezing the columns until the headings wrap to one word per
 * line, or pushing the page wider so the whole calculator scrolls sideways.
 */
.calc-grid-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.calc-grid {
    border-collapse: collapse;
    width: 100%;
    font-size: 14px;
    color: var(--calc-text);
}

/* A pinned width is only honoured cell by cell under a fixed layout; left to
   itself a browser widens whichever column holds the longest word. */
.calc-grid--fixed { table-layout: fixed; }

.calc-grid th,
.calc-grid td {
    padding: 9px 10px;
    border: 1px solid var(--calc-border);
    text-align: left;
    vertical-align: middle;
}

.calc-grid__corner,
.calc-grid__head {
    background: var(--calc-surface);
    font-weight: 600;
    color: var(--calc-label);
    line-height: 1.3;
}

/*
 * The answers are read as a set, so they are centred over the cells they label
 * rather than ranged left against the row headings.
 *
 * Written with the element name in front - `.calc-grid th.calc-grid__head` -
 * because `.calc-grid th` above is a class AND a type, and a lone class loses
 * to it however far down the file it is written. The headings came out ranged
 * left over centred answers, which reads as a column that has come loose.
 */
.calc-grid th.calc-grid__head { text-align: center; }

.calc-grid__row {
    font-weight: 500;
    line-height: 1.35;
    background: var(--calc-bg);
}

.calc-grid--striped tbody tr:nth-child(even) td,
.calc-grid--striped tbody tr:nth-child(even) th { background: var(--calc-surface); }

/* The cell is the target, not the 16px control inside it: a grid asks the
   same question nine times and every one of them should be easy to hit. */
.calc-grid__pick {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 26px;
    margin: 0;
    cursor: pointer;
}

/* ---------------------------------------------------------------------------
   The cells a grid is still owed

   A grid reports one message for the whole element - "Customize your catering
   package is required" - which on a four by three grid says nothing about
   which of the twelve cells to look at. The visitor is left to work out what
   the form wants, and the cells they have already filled in look exactly like
   the ones they have not.

   So the cells themselves say. Marked on the CELL rather than on the control
   inside it, because a cell can hold a tick box with no border of its own -
   there would be nothing to colour.

   A row marked as a whole is a question answered by choosing anywhere along
   it: no one cell is more at fault than another, so the row is outlined.
   --------------------------------------------------------------------------- */

.calc-grid__cell--owed {
    /* Inset, so the outline is not swallowed by the neighbouring cell's own
       border - a table collapses them, and the loser is whichever was drawn
       first. */
    box-shadow: inset 0 0 0 2px var(--calc-error);
    background: color-mix(in srgb, var(--calc-error) 6%, transparent);
}

.calc-grid__row--owed > * {
    box-shadow: inset 0 2px 0 var(--calc-error), inset 0 -2px 0 var(--calc-error);
    background: color-mix(in srgb, var(--calc-error) 6%, transparent);
}

.calc-grid__row--owed > *:first-child { box-shadow:
    inset 0 2px 0 var(--calc-error), inset 0 -2px 0 var(--calc-error),
    inset 2px 0 0 var(--calc-error); }

.calc-grid__row--owed > *:last-child { box-shadow:
    inset 0 2px 0 var(--calc-error), inset 0 -2px 0 var(--calc-error),
    inset -2px 0 0 var(--calc-error); }

/* The control inside a marked cell picks the colour up too, so a dropdown
   sitting on a white ground still reads as the thing being asked for. */
.calc-grid__cell--owed .calc-grid__control { border-color: var(--calc-error); }

.calc-grid td.calc-grid__cell { text-align: center; }
.calc-grid__pick input { margin: 0; cursor: pointer; }

/*
 * A box inside a box inside a box.
 *
 * An input drawn as an ordinary field puts a second border a few pixels inside
 * the cell's own, and its padding pushes what is typed out of line with the
 * heading above it and the question beside it - by exactly the width of that
 * padding and border, on every cell, all the way down the grid. A table whose
 * columns do not line up reads as broken however carefully each cell was
 * styled.
 *
 * So the cell is the field: the input fills it, borderless and transparent,
 * with the padding the headings have. The grid's own lines do the work the
 * input's border was doing, and one row of text runs straight down the column.
 *
 * The cell keeps its padding for a choice - a radio button is an object placed
 * in a cell, not text written in one.
 */
.calc-grid td[data-cell-type="text"],
.calc-grid td[data-cell-type="number"],
.calc-grid td[data-cell-type="dropdown"] { padding: 0; }

.calc-grid .calc-grid__control {
    display: block;
    width: 100%;
    min-width: 0;
    /* The same inset the headings and the row labels have, so the text in a
       cell starts where the heading above it starts. */
    padding: 9px 10px;
    /*
     * Centred, like the cell it fills.
     *
     * A form control does not inherit the cell's text-align - it starts from
     * its own `start` - so the default alignment centred the headings and the
     * ticks and left every dropdown and box ranged left beside them. The three
     * chosen alignments each said so explicitly and looked right; the one
     * nobody had to choose was the one that did not.
     *
     * Overridden by those three below, which weigh the same and come later.
     */
    text-align: center;
    font-size: inherit;
    color: var(--calc-input-text);
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

/*
 * Drawn inside the cell rather than around it: an outline on the outside would
 * be painted over by the neighbouring cells' borders on two sides, so a
 * focused cell in the middle of a grid would show a ring with two edges
 * missing.
 */
.calc-grid .calc-grid__control:focus {
    outline: 2px solid var(--calc-input-focus-border);
    outline-offset: -2px;
    background: var(--calc-input-bg);
}

/*
 * The name an answer reports under, printed on the grid itself.
 *
 * Shown only when the owner asks for it - see the "Show field keys" setting -
 * because one grid publishes as many names as it has rows and none of them
 * appears anywhere else they could read it.
 */
.calc-grid__key {
    display: block;
    margin-top: 3px;
    padding: 0 10px 7px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 10.5px;
    font-weight: 400;
    line-height: 1.3;
    color: var(--calc-muted-text);
    overflow-wrap: anywhere;
}

/*
 * One alignment for the whole grid, when one has been chosen.
 *
 * Written as `.calc-grid.calc-grid--align-x th` - two classes and a type,
 * which is exactly what the default centring above now weighs. Same weight, so
 * source order settles it, and these come last: an alignment somebody chose
 * beats the one the grid falls back to.
 *
 * Anything lighter loses to `.calc-grid th.calc-grid__head` and leaves the
 * headings centred whatever was asked for.
 *
 * The radio and checkbox cells need saying separately: they are flex rows, and
 * where a box sits in one is decided by justify-content rather than by
 * text-align. A grid that moved its words and left its boxes in the middle
 * would look like a bug rather than like a setting.
 */
.calc-grid.calc-grid--align-left th,
.calc-grid.calc-grid--align-left td { text-align: left; }
.calc-grid--align-left .calc-grid__pick { justify-content: flex-start; }

.calc-grid.calc-grid--align-center th,
.calc-grid.calc-grid--align-center td { text-align: center; }
.calc-grid--align-center .calc-grid__pick { justify-content: center; }

.calc-grid.calc-grid--align-right th,
.calc-grid.calc-grid--align-right td { text-align: right; }
.calc-grid--align-right .calc-grid__pick { justify-content: flex-end; }

/*
 * And what is typed into a box, which a cell's text-align does not reach.
 *
 * Written with the table class first so these outrank `.calc-grid
 * .calc-grid__control` above, which is two classes and would otherwise keep
 * every box ranged left whatever the table was set to.
 */
.calc-grid--align-left .calc-grid__control { text-align: left; }
.calc-grid--align-center .calc-grid__control { text-align: center; }
.calc-grid--align-right .calc-grid__control { text-align: right; }

/*
 * Narrow: the row headings take the width they need and the answers keep
 * theirs, and the wrapper scrolls. Wrapping "Somewhat satisfied" to three
 * lines to avoid a scrollbar makes a grid nobody can read either way.
 */
@container (max-width: 520px) {
    .calc-grid { font-size: 13px; }
    .calc-grid th, .calc-grid td { padding: 7px 8px; }
    .calc-grid__row { min-width: 130px; }
    .calc-grid__head { min-width: 74px; }
}

/* Choices ------------------------------------------------------------------- */

.calc-choices { display: flex; gap: 10px; }
.calc-choices--vertical { flex-direction: column; }
.calc-choices--horizontal { flex-direction: row; flex-wrap: wrap; }

/* Why a tick was refused. role="status" on the element, so a screen reader is
   told as well - a box that silently declines to tick reads as broken. */
.calc-choices__limit {
    margin-top: 6px;
    font-size: 12.5px;
    color: var(--calc-error, #b42318);
}

/*
 * A stacked list running in columns.
 *
 * Grid rather than a wrapping flex row, because what is wanted here is
 * alignment DOWN the page as well as across it - seven income bands in two
 * ragged columns read as two lists, not as one list in two columns.
 *
 * The column count is a custom property so the number an owner typed is the
 * number that applies, with no class per value.
 */
.calc-choices--grid {
    display: grid;
    /* Named columns, or as many as will fit when none was named. */
    grid-template-columns: repeat(var(--calc-choice-cols, auto-fill), minmax(150px, max-content));
    gap: 10px 18px;
    align-items: start;
}

/*
 * Equally spaced: every column the same width, so the options line up like a
 * table. max-content above sizes each column to its own longest answer, which
 * is tidier for words of very different lengths and wrong when the point is
 * that they line up.
 */
.calc-choices--even {
    grid-template-columns: repeat(var(--calc-choice-cols, auto-fit), minmax(150px, 1fr));
}

/*
 * Narrow: one per line, whatever was asked for. Two columns of "Prefer not to
 * answer" on a phone is two columns of wrapped text.
 */
@container (max-width: 420px) {
    .calc-choices--grid { grid-template-columns: minmax(0, 1fr); }
}

.calc-choice {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14.5px;
    color: var(--calc-text);
    cursor: pointer;
}

/*
 * Checkboxes and radios are drawn, not tinted.
 *
 * accent-color sets the FILL and lets the browser choose the tick on top of
 * it - always its own idea of a contrasting color, which came out black on a
 * mid-toned brand color and could not be changed. Drawing the control puts
 * both colors under the theme's control: --calc-choice-bg fills it and
 * --calc-choice-mark draws the tick or the dot.
 *
 * The input itself is still the control - it is styled, not replaced - so it
 * keeps its own focus handling, its label association and its place in the
 * tab order.
 */
.calc-choice input {
    -webkit-appearance: none;
    appearance: none;
    width: 17px;
    height: 17px;
    flex: 0 0 17px;
    margin: 0;
    position: relative;
    background: var(--calc-input-bg);
    border: 1px solid var(--calc-input-border);
    border-radius: 4px;
    cursor: pointer;
    transition: background-color .12s ease, border-color .12s ease;
}

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

.calc-choice input:checked {
    background: var(--calc-choice-bg, var(--calc-primary));
    border-color: var(--calc-choice-bg, var(--calc-primary));
}

/* The tick: two borders of a rotated box, so no image or font is needed. */
.calc-choice input[type="checkbox"]::after {
    content: "";
    position: absolute;
    left: 5px;
    top: 1px;
    width: 4px;
    height: 9px;
    border: solid var(--calc-choice-mark, #fff);
    border-width: 0 2px 2px 0;
    transform: rotate(45deg) scale(0);
    transition: transform .12s ease;
}

.calc-choice input[type="radio"]::after {
    content: "";
    position: absolute;
    inset: 4px;
    border-radius: 50%;
    background: var(--calc-choice-mark, #fff);
    transform: scale(0);
    transition: transform .12s ease;
}

.calc-choice input:checked::after { transform: rotate(45deg) scale(1); }
.calc-choice input[type="radio"]:checked::after { transform: scale(1); }

.calc-choice input:disabled { opacity: .55; cursor: not-allowed; }

/* ---------------------------------------------------------------------------
   Product cards
   --------------------------------------------------------------------------- */

.calc-products { display: flex; flex-direction: column; }

/*
 * A whole row is the label, so the picture and the name are part of the tick
 * target. Only the quantity picker is carved out of it, in the runtime.
 */
.calc-product {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 4px;
    border-bottom: 1px dashed var(--calc-border);
    cursor: pointer;
}

.calc-product:last-of-type { border-bottom: 0; }
.calc-product:hover { background: color-mix(in srgb, var(--calc-primary) 4%, transparent); }

.calc-product__tick { flex: 0 0 auto; }

.calc-product__image {
    flex: 0 0 auto;
    width: 62px;
    height: 62px;
    object-fit: cover;
    background: var(--calc-surface);
}

/* The placeholder is the same box as a picture, so a product without one does
   not collapse the row and shuffle everything beside it. */
.calc-product__image--none {
    display: grid;
    place-items: center;
    color: var(--calc-muted-text);
    font-size: 20px;
    border: 1px dashed var(--calc-border);
}

.calc-products--rounded .calc-product__image { border-radius: var(--calc-border-radius); }
.calc-products--circle .calc-product__image { border-radius: 50%; }

.calc-product__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.calc-product__title { color: var(--calc-text); font-size: 15px; font-weight: 600; }

.calc-product__qty { display: inline-flex; align-items: center; gap: 8px; }
.calc-product__qtylabel { color: var(--calc-muted-text); font-size: 13px; }

.calc-product__select {
    padding: 5px 8px;
    font: inherit;
    font-size: 13.5px;
    color: var(--calc-input-text);
    background: var(--calc-input-bg);
    border: 1px solid var(--calc-input-border);
    border-radius: var(--calc-border-radius);
    cursor: pointer;
}

.calc-product__select:focus { outline: none; border-color: var(--calc-input-focus-border); }

.calc-product__price {
    flex: 0 0 auto;
    color: var(--calc-heading);
    font-size: 15px;
    font-weight: 700;
}

.calc-product__stock { color: var(--calc-error); font-size: 12.5px; font-weight: 600; }

/* Not available: still listed, because knowing it exists is worth something,
   but plainly not on offer. */
.calc-product--out { opacity: .55; cursor: not-allowed; }
.calc-product--out:hover { background: none; }

.calc-products__total {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 22px;
    margin-top: 6px;
    padding: 14px 4px 4px;
    border-top: 1px solid var(--calc-border);
    color: var(--calc-heading);
    font-size: 15px;
    font-weight: 700;
}

.calc-products__empty { margin: 0; padding: 18px 4px; color: var(--calc-muted-text); font-size: 14px; }

/* A grid of cards: the same row turned on its side. */
.calc-products--grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 12px; }

.calc-products--grid .calc-product {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
    padding: 14px;
    border: 1px solid var(--calc-border);
    border-radius: var(--calc-border-radius);
}

.calc-products--grid .calc-product__image { width: 100%; height: 130px; }
.calc-products--grid .calc-product.is-chosen { border-color: var(--calc-primary); }

.calc-products--grid .calc-products__total,
.calc-products--grid .calc-products__empty { grid-column: 1 / -1; }

@container (max-width: 420px) {
    .calc-product { flex-wrap: wrap; gap: 10px; }
    .calc-product__image { width: 48px; height: 48px; }
    .calc-product__price { margin-left: auto; }
}

/* Slider -------------------------------------------------------------------- */

/*
 * Label and figure on one line, rail across the full width underneath.
 *
 * The figure used to ride above the handle in a bubble, which meant the number
 * was somewhere different on every drag, the row above the rail was empty
 * space reserved for it, and the rail itself had to share its line with a
 * value box. Beside the label the number never moves, and the rail gets the
 * whole width - which is what makes small movements near the ends readable.
 */
.calc-slider { display: block; }

.calc-slider__head {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 10px;
}

.calc-slider__head .calc-label { margin: 0; }

/* Pushed to the right whether or not there is a label to sit opposite. */
.calc-slider__head .calc-slider__value,
.calc-slider__head .calc-slider__entry { margin-left: auto; }

.calc-slider__rail { position: relative; width: 100%; }

.calc-slider__entry {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex: 0 0 auto;
    padding: 0 10px;
    background: var(--calc-surface);
    border: 1px solid var(--calc-border);
    border-radius: var(--calc-border-radius);
}

.calc-slider__entry:focus-within { border-color: var(--calc-primary); }

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

.calc-slider__input {
    width: 84px;
    padding: 7px 0;
    border: 0;
    background: none;
    color: var(--calc-text);
    font: inherit;
    font-size: 14px;
    font-weight: 580;
}

.calc-slider__input:focus, .calc-slider__input:focus-visible { outline: none; }

/*
 * The range control is drawn rather than left to the browser.
 *
 * A default slider is a different shape, weight and color in every browser,
 * and none of them can be told to paint the filled part - accent-color tints
 * the whole thing. So the track is ours: a rounded rail, the travelled part in
 * the calculator's own primary color, and a handle that reads as a handle.
 *
 * How much is filled comes from --calc-range-fill, a percentage written onto
 * the element: by PHP when the page is rendered, and by the runtime as the
 * visitor drags. Firefox has ::-moz-range-progress and needs neither, but is
 * given the same gradient so both browsers are one code path to change.
 */
/*
 * Width, not flex. The control lives inside .calc-slider__rail, which is a
 * plain block - a flex-grow here would be ignored and the input would fall
 * back to its intrinsic width, leaving the rail wide and the handle short.
 */
.calc-range {
    display: block;
    width: 100%;
    min-width: 0;
    -webkit-appearance: none;
    appearance: none;
    height: 22px;               /* the grab area, not the rail */
    background: transparent;
    cursor: pointer;
    margin: 0;
}

.calc-range::-webkit-slider-runnable-track {
    height: 8px;
    border-radius: 999px;
    background: linear-gradient(
        to right,
        var(--calc-primary) 0 var(--calc-range-fill, 0%),
        var(--calc-range-track, var(--calc-border)) var(--calc-range-fill, 0%) 100%
    );
}

.calc-range::-moz-range-track {
    height: 8px;
    border-radius: 999px;
    background: var(--calc-range-track, var(--calc-border));
}

.calc-range::-moz-range-progress {
    height: 8px;
    border-radius: 999px;
    background: var(--calc-primary);
}

/*
 * The handle: one filled disc, larger than the rail and lifted off it by a
 * soft shadow. It was three rings - fill, a gap in the surface color, then a
 * ring - which reads as a target at the size a slider is actually drawn at,
 * and left a white gap between the handle and the colored part of the rail.
 */
.calc-range::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 20px;
    height: 20px;
    border: 0;
    border-radius: 50%;
    background: var(--calc-primary);
    box-shadow: 0 1px 4px rgba(0, 0, 0, .22);
    /* Centres the 20px handle on the 8px rail. */
    margin-top: -6px;
    transition: transform .1s ease;
}

.calc-range::-moz-range-thumb {
    width: 20px;
    height: 20px;
    box-sizing: border-box;
    border: 0;
    border-radius: 50%;
    background: var(--calc-primary);
    box-shadow: 0 1px 4px rgba(0, 0, 0, .22);
    transition: transform .1s ease;
}

.calc-range:hover::-webkit-slider-thumb { transform: scale(1.08); }
.calc-range:hover::-moz-range-thumb     { transform: scale(1.08); }

.calc-range:active::-webkit-slider-thumb { transform: scale(1.14); }
.calc-range:active::-moz-range-thumb     { transform: scale(1.14); }

/* The ring already shows focus; a second outline around a round handle reads
   as a square box drawn over it. */
.calc-range:focus { outline: none; }

.calc-range:focus-visible::-webkit-slider-thumb {
    box-shadow: 0 1px 4px rgba(0, 0, 0, .22), 0 0 0 5px rgba(0, 0, 0, .14);
}

.calc-range:focus-visible::-moz-range-thumb {
    box-shadow: 0 1px 4px rgba(0, 0, 0, .22), 0 0 0 5px rgba(0, 0, 0, .14);
}

.calc-range:disabled { cursor: not-allowed; opacity: .55; }

/*
 * Read as a figure, not as a control. It was a bordered box beside the rail,
 * which made it look like something to be typed into - and where it can be,
 * that is .calc-slider__entry, which still looks like a box because it is one.
 */
.calc-slider__value {
    flex: 0 0 auto;
    padding: 0;
    background: none;
    border: 0;
    text-align: right;
    white-space: nowrap;
    font-size: 17px;
    font-weight: 700;
}

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

.calc-result {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
    padding: 14px 16px;
    background: var(--calc-result-bg);
    color: var(--calc-result-text);
    border: 1px solid var(--calc-result-border);
    border-radius: var(--calc-result-radius);
}

/*
 * The type on a result.
 *
 * Every size and weight here reads a variable with the old value as its
 * fallback, so a result that sets nothing is drawn exactly as it always was -
 * and one that sets a size keeps the weight it had. The variables are written
 * on the result itself; see calc_result_type_style().
 */
.calc-result__label {
    font-size: var(--calc-result-label-size, 14.5px);
    font-weight: var(--calc-result-label-weight, 560);
    color: var(--calc-result-label);
}

.calc-result__value {
    font-size: var(--calc-result-value-size, 21px);
    font-weight: var(--calc-result-value-weight, 680);
    letter-spacing: -0.02em;
    /* Long currency values must never break the layout. */
    overflow-wrap: anywhere;
    text-align: right;
}

/*
 * An editable result's box is its value, so it answers to the same two
 * settings the read-only one does - otherwise setting a value size on a
 * calculator holding both would change one and not the other.
 */
.calc-result__input {
    width: 140px;
    max-width: 100%;
    padding: 8px 10px;
    font-family: inherit;
    font-size: var(--calc-result-value-size, 17px);
    font-weight: var(--calc-result-value-weight, 620);
    text-align: right;
    color: var(--calc-input-text);
    background: var(--calc-input-bg);
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: var(--calc-border-radius);
}

.calc-result__input:focus {
    outline: none;
    border-color: var(--calc-input-focus-border);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--calc-input-focus-border) 22%, transparent);
}

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

.calc-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 11px 22px;
    font-family: inherit;
    font-size: var(--calc-button-size, 15px);
    font-weight: var(--calc-button-weight, 580);
    line-height: 1.2;
    border: 1px solid transparent;
    border-radius: var(--calc-button-radius);
    cursor: pointer;
    transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}

/*
 * A button in a template preview that has nowhere to send anything.
 *
 * Faded and given a "not allowed" cursor rather than the disabled attribute:
 * a disabled button is skipped by the keyboard and shows no tooltip in most
 * browsers, so the one explanation there is would reach nobody using one.
 */
.calc-btn.is-preview-locked {
    opacity: .55;
    cursor: not-allowed;
}

.calc-btn--primary {
    background: var(--calc-primary-button-bg);
    color: var(--calc-primary-button-text);
    border-color: var(--calc-button-border);
}

/* An explicit hover color rather than a brightness filter: on a black theme
   brightening a white button does nothing, and on a light one it can wash the
   label out entirely. */
.calc-btn--primary:hover {
    background: var(--calc-button-hover-bg);
    color: var(--calc-button-hover-text);
    border-color: var(--calc-button-hover-bg);
}

.calc-btn--secondary {
    background: transparent;
    /*
     * The words in the colour a primary button is filled with.
     *
     * A secondary button is the quiet half of a pair: same action, less
     * insistence. Black type makes it a different thing entirely rather than a
     * quieter version of the one beside it - and the border alone is too faint
     * to carry the button's identity, since it is the input border, chosen to
     * sit under fields rather than to name a control.
     *
     * So the words take the brand colour and the outline stays quiet. An owner
     * who tints their buttons gets both halves of the pair tinted, which is the
     * whole point of there being a pair.
     */
    color: var(--calc-primary-button-bg);
    border-color: var(--calc-input-border);
}
.calc-btn--secondary:hover { background: var(--calc-surface); }

.calc-btn:active { filter: brightness(0.96); }
.calc-btn:disabled { background: var(--calc-disabled-bg); color: var(--calc-disabled-text);
    border-color: var(--calc-border); cursor: not-allowed; }
.calc-btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--calc-primary) 30%, transparent);
}

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

/* Buttons go full width on genuinely narrow embeds so they stay tappable. */
@container (max-width: 380px) {
    .calc-btn { width: 100%; }
    .calc-result { flex-direction: column; align-items: flex-start; }
    .calc-result__value { text-align: left; }

    /* Side by side, the box takes about a third of a narrow embed and leaves
       the slider too short to aim with. Below this width it moves underneath. */
    .calc-slider--manual { flex-direction: column; align-items: stretch; gap: 10px; }
    .calc-slider--manual .calc-slider__entry { align-self: flex-start; }
}

/* ---------------------------------------------------------------------------
   Result paragraphs and gated results                            (Fixes V1)
   --------------------------------------------------------------------------- */

.calc-result-text__label {
    display: block;
    font-size: var(--calc-result-label-size, 14px);
    font-weight: var(--calc-result-label-weight, 560);
    color: var(--calc-label);
    margin-bottom: 5px;
}

.calc-result-text__body {
    font-size: var(--calc-result-value-size, 15px);
    font-weight: var(--calc-result-value-weight, 400);
    line-height: 1.6;
    color: var(--calc-text);
    overflow-wrap: anywhere;
}

.calc-result-text--highlight {
    padding: 14px 16px;
    background: var(--calc-result-bg);
    border: 1px solid var(--calc-result-border);
    border-radius: var(--calc-border-radius);
}
.calc-result-text--highlight .calc-result-text__body { color: var(--calc-result-text); }

/* A gated result: present, clearly labelled, value withheld. */
.calc-result--locked { border-style: dashed; opacity: .85; }

.calc-result__locked {
    font-size: 13.5px;
    font-style: italic;
    color: var(--calc-muted-text);
}

.calc-result__error {
    display: block;
    width: 100%;
    font-size: 13px;
    color: var(--calc-error);
    margin-top: 6px;
}

/* Lead form messages */
.calc-notice {
    padding: 12px 14px;
    border-radius: var(--calc-border-radius);
    font-size: 14px;
    line-height: 1.55;
    margin-bottom: var(--calc-gap);
}
.calc-notice--success {
    background: var(--calc-result-bg);
    border: 1px solid var(--calc-result-border);
    color: var(--calc-result-text);
}
.calc-notice--error {
    background: var(--calc-surface);
    border: 1px solid var(--calc-error);
    color: var(--calc-error);
}

.calc-field-error { display: block; font-size: 13px; color: var(--calc-error); margin-top: 5px; }


/* ---------------------------------------------------------------------------
   Component states
   Every control gets a visible focus, active and selected state in whichever
   theme is in use - the focus ring is drawn from the theme's own focus color
   rather than a fixed blue.
   --------------------------------------------------------------------------- */

/*
 * An outline only where nothing else marks focus.
 *
 * A .calc-input already shows focus by turning its border and growing a soft
 * ring, so an outline on top of that drew a SECOND rectangle - two lines
 * around one field, and inside a .calc-field wrapper the inner one looked like
 * a box within a box. The border and ring are the focus indicator; a checkbox
 * and a button have no such state of their own, so they keep the outline.
 */
.calc-choice input:focus-visible,
.calc-btn:focus-visible {
    outline: 2px solid var(--calc-input-focus-border);
    outline-offset: 2px;
}

/*
 * The browser's own outline, off everywhere.
 *
 * Removing it is only safe because every control here draws focus some other
 * way - a turned border, a ring, a marked handle. Taking it away without that
 * would leave a keyboard user with no idea where they are.
 */
.calc-input:focus,
.calc-input:focus-visible,
.calc-picker__toggle:focus,
.calc-picker__toggle:focus-visible,
.calc-multiselect__toggle:focus,
.calc-multiselect__toggle:focus-visible { outline: none; }

/*
 * Inside a wrapper the ring belongs to the wrapper, which draws it on
 * :focus-within. The input keeps neither border nor shadow of its own.
 */
.calc-field > .calc-input:focus,
.calc-field > .calc-input:focus-visible {
    outline: none;
    box-shadow: none;
    border-color: transparent;
}

/*
 * The slider is deliberately absent from the rule above. An outline follows
 * the element's box, which for a range control is the whole rail - so a
 * focused slider grew a rectangle around the entire width instead of marking
 * the handle. It draws its own focus on the handle instead.
 */

/* The choice controls are drawn from --calc-choice-* further up; there is no
   accent-color to re-state here, and one would do nothing to an appearance:none
   control anyway. */

/* No surface of its own to restate here any more - it is text on whatever the
   field sits on. The color still has to be, or a dark theme leaves the figure
   in the light theme's ink. */
.calc-slider__value { color: var(--calc-text); }

.calc-tip { color: var(--calc-muted-text); }

/* Transparent mode: no outer surface at all, so the calculator sits directly
   on whatever the host page has behind it. */
.calc--transparent { background: transparent; --calc-padding: 0px; padding: 0; }

/*
 * The PDF button while a document is being built.
 *
 * A Google Doc takes a copy, a rewrite and an export at Google's pace, which
 * is several seconds of nothing. A button that looks idle for that long reads
 * as broken, so it says what it is doing and refuses a second press.
 */
.calc-btn.is-working { cursor: progress; opacity: .85; }

.calc-btn__spinner {
    display: inline-block;
    width: 13px;
    height: 13px;
    margin-right: 8px;
    vertical-align: -2px;
    border: 2px solid currentColor;
    /* One transparent edge is what makes the ring read as spinning rather
       than merely rotating in place. */
    border-top-color: transparent;
    border-radius: 50%;
    animation: calc-spin .7s linear infinite;
}

@keyframes calc-spin { to { transform: rotate(360deg); } }

/* Someone who has asked not to see motion still needs to know it is busy. */
@media (prefers-reduced-motion: reduce) {
    .calc-btn__spinner { animation-duration: 2.4s; }
}

/* ---------------------------------------------------------------------------
   Multi-step calculators (Updates V8)

   Every step is in the page; moving between them shows and hides. A long form
   split into three screens is answered far more often than the same form shown
   all at once, and the split costs nothing at runtime because nothing is
   fetched when the visitor moves on.
   --------------------------------------------------------------------------- */

.calc-step[hidden] { display: none; }

/* The gap between a step's heading and the first field under it. 16px is
   what it always was, so a calculator that never sets it does not move. */
.calc-step__head { margin-bottom: var(--calc-step-head-space, 16px); }

.calc-step__title {
    margin: 0 0 4px;
    font-size: 17px;
    font-weight: 640;
    color: var(--calc-heading);
    line-height: 1.3;
}

.calc-step__description {
    margin: 0;
    font-size: 14px;
    color: var(--calc-muted-text);
    line-height: 1.55;
}

.calc-step__nav {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 20px;
}

/* Back sits left, Next right - the direction each one moves you. On a single
   remaining button that leaves Next where it has always been. */
.calc-step__back { margin-right: auto; }
.calc-step__next { margin-left: auto; }

/*
 * Why the visitor cannot move on yet.
 *
 * Named fields rather than "please complete this step": a form that refuses
 * without saying which answer is missing is one people abandon.
 */
.calc-step__blocked {
    margin-top: 12px;
    padding: 10px 12px;
    border-radius: var(--calc-border-radius);
    background: color-mix(in srgb, var(--calc-error) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--calc-error) 35%, transparent);
    color: var(--calc-error);
    font-size: 13.5px;
    line-height: 1.5;
}

.calc-step__blocked[hidden] { display: none; }

/* --- the progress bar ---------------------------------------------------- */

.calc-progress {
    margin-bottom: 20px;
    /* Both fall through to the calculator's own theme when the owner has not
       chosen, so a bar left alone still matches what it sits on. */
    --calc-progress-fill: var(--calc-primary);
    --calc-progress-text: var(--calc-muted-text);
    /* The part not yet travelled, in both designs: the unfilled length of the
       bar and the line between one circle and the next. */
    --calc-progress-track: var(--calc-border);
}

/* ---------------------------------------------------------------------------
   Where the caption sits
   ---------------------------------------------------------------------------
   The alignment moves the "Step 2 of 4" line and nothing else.
   
   The graphic always fills the width, whichever design it is. A bar narrowed
   to its content is not a bar, and a row of circles pushed to one side stops
   being a scale of the journey - the first and last circles mark the start and
   the end, so they belong at the edges. An earlier version moved the whole
   indicator and it read as a mistake.
   ------------------------------------------------------------------------ */

.calc-progress[data-align="center"] .calc-progress__text { text-align: center; }
.calc-progress[data-align="right"] .calc-progress__text { text-align: right; }

/*
 * A caption under the graphic rather than over it. The margin moves with it,
 * or the gap ends up on the wrong side and the caption sits against whatever
 * follows.
 */
.calc-progress__track + .calc-progress__text,
.calc-progress__steps + .calc-progress__text {
    margin: 8px 0 0;
}

.calc-progress__text {
    margin-bottom: 6px;
    font-size: 12.5px;
    font-weight: 560;
    color: var(--calc-progress-text);
}

.calc-progress__track {
    height: var(--calc-progress-height, 8px);
    border-radius: 999px;
    background: var(--calc-progress-track);
    overflow: hidden;
}

.calc-progress__fill {
    height: 100%;
    border-radius: 999px;
    background: var(--calc-progress-fill);
    transition: width .25s ease;
}

/* A bar that animates while someone is trying to read it is a distraction, and
   the width is meaningful without the movement. */
@media (prefers-reduced-motion: reduce) {
    .calc-progress__fill { transition: none; }
}

/* ---------------------------------------------------------------------------
   The stepped designs
   ---------------------------------------------------------------------------
   A row of circles, numbered, with a line between them and optionally a label
   underneath. Three states, set by calculator.js as the visitor moves:

       is-done      behind them  - filled, showing a tick
       is-current   here now     - filled and ringed, showing its number
       (neither)    ahead        - hollow and grey

   The connector belongs to the step on its right rather than sitting between
   two of them, so colouring the path travelled is one class on one element
   rather than a second set of nodes to keep in step.
   ------------------------------------------------------------------------ */

/* The step's heading and its sentence, moved together - they are one block and
   splitting them across two alignments reads as a mistake. */
.calc-step__head[data-align="center"] { text-align: center; }
.calc-step__head[data-align="right"] { text-align: right; }

.calc-progress__steps {
    display: flex;
    list-style: none;
    margin: 0;
    padding: 0;
    /* Every step the same width, so the circles are evenly spaced whatever
       their labels say. min-width:0 because a flex item will not shrink below
       its content otherwise, and a long label would push the row wider than
       the calculator. */
    align-items: flex-start;
}

.calc-progress__step {
    position: relative;
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    text-align: center;
}

/* The line runs from the previous circle to this one, so whichever step is
   first on the row draws none - there is nothing to its left to join.
   .is-first as well as :first-child, because a rule can hide the first one and
   CSS cannot ask "first that is still visible"; calculator.js marks it. */
.calc-progress__step:first-child .calc-progress__line,
.calc-progress__step.is-first .calc-progress__line { display: none; }

/* A step a rule has taken out is not part of this visitor's journey, so it
   leaves the row rather than sitting there greyed - four circles above the
   words "Step 2 of 3" is a contradiction the visitor has to resolve. */
.calc-progress__step.is-hidden { display: none; }

.calc-progress__line {
    position: absolute;
    top: calc(var(--calc-progress-dot, 28px) / 2 - 1px);
    right: 50%;
    left: -50%;
    height: 2px;
    background: var(--calc-progress-track);
    transition: background-color .25s ease;
}

.calc-progress__dot {
    position: relative;
    z-index: 1;
    width: var(--calc-progress-dot, 28px);
    height: var(--calc-progress-dot, 28px);
    flex: 0 0 var(--calc-progress-dot, 28px);
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--calc-surface, #fff);
    /*
     * The ring of a step not yet reached, so it matches the line arriving at
     * it. The inside stays the calculator's own surface - a filled circle is
     * what "done" looks like, and colouring these would take that distinction
     * away.
     */
    border: 2px solid var(--calc-progress-track);
    color: var(--calc-muted-text);
    font-size: 12px;
    font-weight: 700;
    line-height: 1;
    transition: background-color .25s ease, border-color .25s ease, color .25s ease;
}

.calc-progress__tick { display: none; width: 60%; height: 60%; }

.calc-progress__label {
    font-size: 12px;
    line-height: 1.35;
    color: var(--calc-progress-text);
    /* A long step name wraps rather than widening the row. */
    overflow-wrap: anywhere;
}

/* --- behind them ---------------------------------------------------------- */

.calc-progress__step.is-done .calc-progress__dot {
    background: var(--calc-progress-fill);
    border-color: var(--calc-progress-fill);
    color: #fff;
}

.calc-progress__step.is-done .calc-progress__num { display: none; }
.calc-progress__step.is-done .calc-progress__tick { display: block; }

/* The line arriving at a step you have reached is part of the path travelled,
   so it is coloured by that step rather than by the one before it. */
.calc-progress__step.is-done .calc-progress__line,
.calc-progress__step.is-current .calc-progress__line {
    background: var(--calc-progress-fill);
}

/* --- here now ------------------------------------------------------------- */

.calc-progress__step.is-current .calc-progress__dot {
    background: var(--calc-progress-fill);
    border-color: var(--calc-progress-fill);
    color: #fff;
    /* The ring is what separates "here" from "done" at a glance when both are
       filled in the same colour. */
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--calc-progress-fill) 22%, transparent);
}

.calc-progress__step.is-current .calc-progress__label {
    font-weight: 700;
    color: var(--calc-text);
}

/*
 * Browsers without color-mix get a plain ring in the border colour rather than
 * none at all - the state has to be visible, and this is the fallback that
 * says so.
 */
@supports not (color: color-mix(in srgb, red 50%, blue)) {
    .calc-progress__step.is-current .calc-progress__dot {
        box-shadow: 0 0 0 3px var(--calc-border);
    }
}

@media (prefers-reduced-motion: reduce) {
    .calc-progress__line,
    .calc-progress__dot { transition: none; }
}

/* ---------------------------------------------------------------------------
   Narrow calculators
   ---------------------------------------------------------------------------
   A container query, not a media query: this is embedded in somebody else's
   page and the window says nothing about how much room the calculator was
   given. See the same approach on rows.
   ------------------------------------------------------------------------ */

@container (max-width: 520px) {
    .calc-progress__steps { --calc-progress-dot: 24px; }

    /*
     * The labels shrink before they go. Six words under six circles at this
     * width is unreadable, and the circles alone still say where you are -
     * with the "Step 2 of 4" line above them saying it in words.
     */
    .calc-progress--labelled .calc-progress__label { font-size: 10.5px; }
}

@container (max-width: 380px) {
    .calc-progress__steps { --calc-progress-dot: 20px; }
    .calc-progress__dot { font-size: 10px; }
    .calc-progress--labelled .calc-progress__label { display: none; }
}

/* Fallback for browsers without container query support. */
@supports not (container-type: inline-size) {
    @media (max-width: 560px) {
        .calc-progress__steps { --calc-progress-dot: 24px; }
        .calc-progress--labelled .calc-progress__label { font-size: 10.5px; }
    }

    @media (max-width: 420px) {
        .calc-progress__steps { --calc-progress-dot: 20px; }
        .calc-progress__dot { font-size: 10px; }
        .calc-progress--labelled .calc-progress__label { display: none; }
    }
}

/* ===========================================================================
   Charts and tables (Update v8.2)
   ---------------------------------------------------------------------------
   Every color comes from the calculator's own theme variables, so a chart or
   a table looks like the calculator it sits in rather than like a widget
   bolted onto it. Nothing here fixes a width: the wrapper scrolls, the page
   never does.
   =========================================================================== */

.calc-visual__heading {
    font-size: 15px;
    font-weight: 600;
    color: var(--calc-heading);
    margin-bottom: 10px;
}

.calc-visual__empty {
    padding: 22px;
    border: 1px dashed var(--calc-border);
    border-radius: var(--calc-border-radius);
    color: var(--calc-muted-text);
    font-size: 13.5px;
    text-align: center;
}

/* Chart tooltips -----------------------------------------------------------
   The library ships its own tooltip: a translucent panel, a grey title band,
   and label and value run together as one line of text. These rules restate
   it in the calculator's own terms - a clean surface, a colored dot carrying
   the series identity, a muted label and the number in bold beside it.

   Every selector carries `.calc` so it outranks the stylesheet ApexCharts
   injects into the page at runtime, which is what the tooltip would otherwise
   be wearing. Colors come from the theme rather than being fixed white, so a
   dark calculator gets a dark tooltip instead of a white flash over it - on
   the light presets that resolves to white, which is what these look like. */

/*
 * The library's panel is stripped of every visible quality - no background,
 * no border, no shadow - and the box is drawn on OUR content instead.
 *
 * This is what stops an empty tooltip appearing as a blank white box
 * following the pointer around. When the pointer is between two slices or
 * over the hole in a donut there is nothing to report, so the renderer
 * returns nothing - but the library still shows its panel, and it keeps its
 * own empty scaffolding inside, so the panel is neither empty nor removable.
 * Hiding it by asking whether it contains our markup needs :has(), which is
 * too new to rely on for something this visible. An unstyled panel wrapping
 * nothing is invisible in every browser, with no selector support required.
 */
.calc .apexcharts-tooltip.apexcharts-theme-light {
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    color: var(--calc-text);
    font-family: inherit;
    overflow: visible;
}

/* The scaffolding the library keeps inside its panel. Ours replaces it, so
   it should never take up room. */
.calc .apexcharts-tooltip > .apexcharts-tooltip-title,
.calc .apexcharts-tooltip > .apexcharts-tooltip-series-group { display: none; }

/* The contents are ours - see tooltipRenderer() in visuals.js - so these are
   plain rules over our own markup rather than overrides of the library's. */

.calc .calc-chart-tip {
    padding: 9px 12px;
    background: var(--calc-bg);
    border: 1px solid var(--calc-border);
    border-radius: var(--calc-border-radius);
    box-shadow: 0 6px 20px rgba(16, 24, 40, .12);
    color: var(--calc-text);
}

.calc-chart-tip__title {
    margin-bottom: 6px;
    color: var(--calc-muted-text);
    font-size: 12px;
    font-weight: 600;
}

.calc-chart-tip__row {
    display: flex;
    align-items: baseline;
    gap: 18px;
    padding: 2px 0;
    font-size: 12.5px;
}

.calc-chart-tip__dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    flex-shrink: 0;
    /* Baseline alignment would sit a bare span on the text baseline; this
       centres it against the line beside it. */
    align-self: center;
    margin-right: -8px;
}

.calc-chart-tip__name { color: var(--calc-muted-text); }

/* Hard right, so a column of figures lines up instead of ragging with the
   length of each name. */
.calc-chart-tip__value {
    margin-left: auto;
    color: var(--calc-text);
    font-weight: 600;
    white-space: nowrap;
}

.calc .apexcharts-xaxistooltip.apexcharts-theme-light {
    background: var(--calc-bg);
    border: 1px solid var(--calc-border);
    border-radius: var(--calc-border-radius);
    color: var(--calc-text);
    box-shadow: 0 4px 12px rgba(16, 24, 40, .1);
}

.calc .apexcharts-xaxistooltip.apexcharts-theme-light::before { border-bottom-color: var(--calc-border); }
.calc .apexcharts-xaxistooltip.apexcharts-theme-light::after  { border-bottom-color: var(--calc-bg); }

/* Present for a screen reader and for print, absent from the page. A chart is
   a picture; the numbers behind it must still be reachable. */
.calc-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;
}

/* Tables ------------------------------------------------------------------ */

.calc-table__wrap { width: 100%; }
.calc-table__wrap--auto  { width: max-content; max-width: 100%; }
.calc-table__wrap--fixed { max-width: 100%; }

.calc-table__wrap--align-center { margin-left: auto; margin-right: auto; }
.calc-table__wrap--align-right  { margin-left: auto; }

.calc-table__tools { margin-bottom: 10px; }
.calc-table__search { max-width: 280px; }

/* The scroller, not the page. A wide table on a phone slides within its own
   box while everything around it stays put. */
.calc-table__scroll {
    overflow: auto;
    border: 1px solid var(--calc-border);
    border-radius: var(--calc-border-radius);
    background: var(--calc-bg);
    -webkit-overflow-scrolling: touch;
}

.calc-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13.5px;
    color: var(--calc-text);
}

/* ---------------------------------------------------------------------------
   A legend along the top or bottom does not scroll

   ApexCharts reserves a strip for the legend inside the height the chart was
   given, then sets overflow-y: auto on it. Its measurement is a pixel or two
   short of what the legend's own line-height and margins take, so a scrollbar
   appears beside a single row of two labels - which reads as a rendering
   fault, because there is nothing to scroll to.

   Turned off only for the horizontal sides. A legend there WRAPS and grows;
   one down the left or right is a single tall column, and on a chart with many
   series scrolling it is the only way to reach the rest.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   A chart's own figures, listed under it

   A chart says which is bigger; it does not say by how much. This is a key
   rather than a table of data - no borders and no header - so the colour ties
   each line to its slice and the figure is what somebody came for.

   The name goes left with its swatch and the figure goes right, because a
   column of figures is read down its last digit, and that only works when they
   share an edge.
   --------------------------------------------------------------------------- */

.calc-chart__values { margin-top: 10px; }

.calc-chart__value {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 2px;
    font-size: 13.5px;
    color: var(--calc-text);
}

.calc-chart__vswatch {
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    border-radius: 2px;
    /* A slice with no colour of its own still gets a square, or the names
       below would not line up with each other. */
    background: var(--calc-primary);
}

.calc-chart__vname {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--calc-muted-text);
}

.calc-chart__vfigure {
    flex: 0 0 auto;
    /* Figures line up under each other, which is most of what makes a column
       of them readable. */
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

/*
 * A stacked legend: one name per line.
 *
 * Apex lays its names out inline and wraps them, and has no option for a
 * column - so the items are given the full width, which is what puts each on a
 * line of its own. Only for a legend along the top or bottom: one down a side
 * is already a column.
 *
 * And centred, which the full width is exactly what breaks. Apex centres a
 * horizontal legend by centring its ITEMS in the row, and an item that has
 * been told to fill the row is already centred - so every name inside those
 * full-width items lines up hard against the left edge instead. Each item is
 * one of Apex's own flex rows, so centring its contents is what puts the
 * column of names back under the middle of the chart.
 */
.calc-chart--legend-stacked.calc-chart--legend-top .apexcharts-legend-series,
.calc-chart--legend-stacked.calc-chart--legend-bottom .apexcharts-legend-series {
    width: 100%;
    margin: 0 0 3px !important;
    justify-content: center;
}

.calc-chart--legend-top .apexcharts-legend,
.calc-chart--legend-bottom .apexcharts-legend {
    /*
     * !important because the value it replaces is written inline by the
     * library, on an element the library owns and re-creates on every redraw -
     * there is nothing of ours to put a class on.
     */
    overflow: visible !important;
}

.calc-table th,
.calc-table td {
    padding: 10px 14px;
    text-align: left;
    /* The line colour is a variable so a table can be given one of its own -
       see the Borders settings - while a table that asks for nothing keeps
       the calculator's own border colour, exactly as it always had. */
    border-bottom: 1px solid var(--calc-table-line, var(--calc-border));
    white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   Which lines a table draws

   A line under each row is what it has always drawn, so that is the case with
   no class at all - every table already built keeps the look it has, and only
   one asking for something else says so.

   The other two are different tables to read rather than more or less of one:
   a schedule of figures reads down its rows and wants the rules; a short
   summary beside a paragraph often wants none.
   --------------------------------------------------------------------------- */

.calc-table--borders-none th,
.calc-table--borders-none td { border-bottom: 0; }

.calc-table--borders-all th,
.calc-table--borders-all td {
    border: 1px solid var(--calc-table-line, var(--calc-border));
}

/*
 * The heading keeps its own colour whatever the lines are doing: it is painted
 * in the theme's primary, and a grey rule drawn over that edge reads as a seam
 * rather than as a border.
 */
.calc-table--borders-all thead th { border-color: var(--calc-primary); }

/* The header carries the theme color, so a table reads as part of the
   calculator rather than as a grey box dropped into it.

   The text uses --calc-primary-button-text rather than a hard-coded white:
   that token already answers "what color goes on top of primary", and an
   owner with a pale brand color has set it to something dark. Hard-coding
   white would make their header unreadable. */
.calc-table thead th {
    background: var(--calc-primary);
    color: var(--calc-primary-button-text);
    border-bottom-color: var(--calc-primary);
    font-weight: 600;
    font-size: 12px;
    letter-spacing: .03em;
    text-transform: uppercase;
    white-space: nowrap;
}

/* The last row carries the outer border instead of its own. */
.calc-table tbody tr:last-child th,
.calc-table tbody tr:last-child td { border-bottom: 0; }

.calc-table tbody th { font-weight: 500; color: var(--calc-muted-text); }

/* Shaded rows are a wash of the theme color rather than plain grey, so the
   banding belongs to the same family as the header.

   Two declarations on purpose: the flat surface is the fallback, and any
   browser that understands color-mix takes the tint on the line below. The
   mix is to transparent, not to white, so it also works over a dark theme. */
.calc-table--zebra tbody tr:nth-child(even) { background: var(--calc-surface); }
.calc-table--zebra tbody tr:nth-child(even) {
    background: color-mix(in srgb, var(--calc-primary) 7%, transparent);
}
.calc-table--center th, .calc-table--center td { text-align: center; }
.calc-table--right  th, .calc-table--right  td { text-align: right; }

/* Digits only line up for comparison when they share an edge, so a numeric
   table puts labels left and figures right. */
.calc-table--numeric td:not(:first-child),
.calc-table--numeric th:not(:first-child) { text-align: right; }

.calc-table--sticky thead th {
    position: sticky;
    top: 0;
    z-index: 1;
}

/* Sorting ----------------------------------------------------------------- */

/* A sortable header sits on the theme color now, so it cannot signal hover
   by darkening its text and cannot draw a primary-colored focus ring on a
   primary background - both would vanish. It shifts opacity instead, and the
   ring is drawn in the same ink as the label. */
.calc-table th[data-sort] { cursor: pointer; user-select: none; }
.calc-table th[data-sort]:hover { opacity: .85; }
.calc-table th[data-sort]:focus-visible {
    outline: 2px solid var(--calc-primary-button-text);
    outline-offset: -2px;
}

.calc-table__sort {
    position: relative;
    display: inline-block;
    width: 8px;
    height: 10px;
    margin-left: 6px;
    vertical-align: -1px;
}

.calc-table__sort::before,
.calc-table__sort::after {
    content: '';
    position: absolute;
    left: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    opacity: .55;
}

.calc-table__sort::before { top: 0; border-bottom: 4px solid currentColor; }
.calc-table__sort::after  { bottom: 0; border-top: 4px solid currentColor; }

.calc-table th[aria-sort="ascending"] .calc-table__sort::before,
.calc-table th[aria-sort="descending"] .calc-table__sort::after { opacity: 1; }

.calc-table th[aria-sort="ascending"] .calc-table__sort::after,
.calc-table th[aria-sort="descending"] .calc-table__sort::before { opacity: .18; }

.calc-table__none {
    padding: 18px;
    text-align: center;
    color: var(--calc-muted-text);
    font-size: 13px;
}

/* Charts ------------------------------------------------------------------ */

.calc-chart { margin: 0; }
.calc-chart__canvas { width: 100%; }

/* Drawn into a viewBox and scaled by CSS, so one drawing serves every
   width - no redraw on resize, and nothing to measure before drawing. */
.calc-chart__canvas svg { display: block; width: 100%; height: auto; overflow: visible; }

/* A mark lifts slightly under the pointer, which is what tells someone
   the tooltip is coming from the thing they are pointing at. */
.calc-chart__mark { transition: opacity .12s ease; }
.calc-chart__canvas svg:hover .calc-chart__mark { opacity: .55; }
.calc-chart__canvas svg .calc-chart__mark:hover { opacity: 1; }


/* ---------------------------------------------------------------------------
   The List: a column of answers, each with an icon
   ---------------------------------------------------------------------------

   Six figures in one card. Built as one element rather than six results
   because the spacing, the lines between the rows and the corner radius are
   one decision about the group - six results could match by hand and drift
   apart the first time one of them was edited.

   Every size an owner can set arrives as a custom property on the card, so a
   row needs no inline styles of its own and the whole list restyles from one
   declaration. See calc_render_list().
   --------------------------------------------------------------------------- */

.calc-list {
    --calc-list-radius: 12px;
    --calc-list-pad: 12px;
    --calc-list-padx: 12px;
    --calc-list-gap: 0px;
    --calc-list-label: 14px;
    --calc-list-value: 14px;
    --calc-list-icon: 16px;

    border-radius: var(--calc-list-radius);
    background: var(--calc-surface);
    overflow: hidden;
}

.calc-list__heading {
    margin-bottom: 8px;
    font-weight: 600;
    color: var(--calc-heading, var(--calc-text));
}

.calc-list__row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: calc(var(--calc-list-pad) + var(--calc-list-gap)) var(--calc-list-padx, var(--calc-list-pad));
}

/* A line between the rows, and never after the last one - a rule under the
   bottom row is a rule under nothing. */
.calc-list--ruled .calc-list__row + .calc-list__row {
    /* The owner's colour where they set one, the theme's border otherwise. */
    border-top: 1px solid var(--calc-list-line, var(--calc-border));
}

/* A gap between two rows.
   Not a row, deliberately: the rule above is a top border on a row FOLLOWING
   a row, so a gap between them leaves the row below with no line against it
   and the row above with none under it - which is the whole point of putting
   one there. Its height is written inline: it belongs to that one gap. */
.calc-list__space { flex: 0 0 auto; }

/* A gap that has been given a name, which makes it a heading over the rows
   beneath it. The height is the gap ABOVE - written as padding here - so that
   naming a gap does not change how far it sits from the row above. */
.calc-list__space--titled {
    padding-left: var(--calc-list-padx, var(--calc-list-pad));
    padding-right: var(--calc-list-padx, var(--calc-list-pad));
    padding-bottom: calc(var(--calc-list-pad) / 2);
}

.calc-list__spacetitle {
    display: block;
    font-size: var(--calc-list-label);
    font-weight: 640;
    line-height: 1.35;
    color: var(--calc-text, inherit);
}

.calc-list__space--titled[data-align="center"] { text-align: center; }
.calc-list__space--titled[data-align="right"]  { text-align: right; }

.calc-list__icon {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: calc(var(--calc-list-icon) * 2);
    height: calc(var(--calc-list-icon) * 2);
    border-radius: 8px;
    /* The owner's colour where they set one, the theme's tint otherwise - the
       same fallback shape the separator line uses. */
    background: var(--calc-list-iconbg, var(--calc-primary-soft, rgba(0, 0, 0, .04)));
    color: var(--calc-primary);
    font-size: var(--calc-list-icon);
}

.calc-list__icon img { width: var(--calc-list-icon); height: auto; }

.calc-list__label {
    font-size: var(--calc-list-label);
    color: var(--calc-muted-text);
    min-width: 0;
}

.calc-list__value {
    font-size: var(--calc-list-value);
    font-weight: 600;
    color: var(--calc-text);
    /* Figures line up under each other, which is most of what makes a column
       of them readable. */
    font-variant-numeric: tabular-nums;
    min-width: 0;
}

/* --- where the label and the value sit --------------------------------- */

/* The shape in the design: the name on the left, the figure hard right. */
.calc-list--split .calc-list__label { flex: 1 1 auto; }
.calc-list--split .calc-list__value { flex: 0 0 auto; margin-left: auto; text-align: right; }

.calc-list--center .calc-list__row { justify-content: center; }
.calc-list--center .calc-list__label,
.calc-list--center .calc-list__value { flex: 0 0 auto; }

/* "Normal": neither pushed apart nor centred - they simply follow each other. */
.calc-list--normal .calc-list__label { flex: 0 1 auto; }
.calc-list--normal .calc-list__value { flex: 0 1 auto; }

/* --- the card's own weight --------------------------------------------- */

.calc-list--shadow-soft   { box-shadow: 0 1px 2px rgba(16, 24, 40, .06); }
.calc-list--shadow-medium { box-shadow: 0 2px 6px rgba(16, 24, 40, .09),
                                        0 1px 2px rgba(16, 24, 40, .05); }
.calc-list--shadow-strong { box-shadow: 0 4px 10px rgba(16, 24, 40, .12),
                                        0 2px 4px rgba(16, 24, 40, .06); }

/* ---------------------------------------------------------------------------
   On each screen

   Three sizes, each with its own answer to "show the icons", "stack a long
   row" and "where does the content sit". Written as media queries because
   none of these can be expressed inline - which is why the server writes
   classes rather than styles for them.
   --------------------------------------------------------------------------- */

.calc-list--noicons-desktop .calc-list__icon { display: none; }

/* A stacked row puts the value under its label rather than beside it, for a
   figure too long to share the line. */
.calc-list--stacked-desktop .calc-list__row { flex-wrap: wrap; }
.calc-list--stacked-desktop .calc-list__value { flex-basis: 100%; margin-left: 0; text-align: left; }

.calc-list--desktop-center .calc-list__row { justify-content: center; text-align: center; }
.calc-list--desktop-right .calc-list__row { justify-content: flex-end; text-align: right; }

@media (max-width: 900px) {
    .calc-list--noicons-tablet .calc-list__icon { display: none; }

    .calc-list--stacked-tablet .calc-list__row { flex-wrap: wrap; }
    .calc-list--stacked-tablet .calc-list__value {
        flex-basis: 100%; margin-left: 0; text-align: left;
    }

    .calc-list--tablet-center .calc-list__row { justify-content: center; text-align: center; }
    .calc-list--tablet-right .calc-list__row { justify-content: flex-end; text-align: right; }
}

@media (max-width: 640px) {
    .calc-list--noicons-mobile .calc-list__icon { display: none; }

    .calc-list--stacked-mobile .calc-list__row { flex-wrap: wrap; }
    .calc-list--stacked-mobile .calc-list__value {
        flex-basis: 100%; margin-left: 0; text-align: left;
    }

    .calc-list--mobile-center .calc-list__row { justify-content: center; text-align: center; }
    .calc-list--mobile-right .calc-list__row { justify-content: flex-end; text-align: right; }

    /*
     * A row kept inline on a phone can still be wider than the phone. It
     * scrolls sideways within its own row rather than pushing the whole
     * calculator wide, which is the failure this replaces.
     */
    .calc-list:not([class*="stacked-mobile"]) .calc-list__value {
        overflow-x: auto;
        white-space: nowrap;
    }
}

/* HTML, not SVG text: a key with five entries has to wrap on a phone,
   and text inside the drawing would run off the side of it instead. */
.calc-chart__legend {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 16px;
    margin-top: 10px;
    font-size: 12.5px;
    color: var(--calc-muted-text);
}

.calc-chart__key { display: inline-flex; align-items: center; gap: 6px; }

.calc-chart__swatch {
    width: 10px; height: 10px;
    border-radius: 2px;
    flex: 0 0 10px;
}

/* The figure keeps its caption for a screen reader and for print; the
   picture is what everyone else reads. */
@media print {
    .calc-chart__table { position: static; width: auto; height: auto; clip: auto; overflow: visible; }
}

@media (max-width: 640px) {
    .calc-table th, .calc-table td { padding: 9px 11px; font-size: 13px; }
    .calc-table__search { max-width: 100%; }
}

/* A visual the server did not wait for ---------------------------------------
   Reading a spreadsheet is a round trip to Google. The page goes out without
   it and this holds the space, sized to what is coming so nothing jumps when
   it arrives. */

.calc-visual__loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    border: 1px solid var(--calc-border);
    border-radius: var(--calc-border-radius);
    background: var(--calc-surface);
    color: var(--calc-muted-text);
    font-size: 13px;
}

.calc-visual__spinner {
    width: 14px;
    height: 14px;
    border: 2px solid var(--calc-border);
    border-top-color: var(--calc-primary);
    border-radius: 50%;
    animation: calc-visual-spin .7s linear infinite;
}

@keyframes calc-visual-spin { to { transform: rotate(360deg); } }

/* Someone who has asked not to see motion gets a static ring rather than
   nothing at all - the box still has to read as "working on it". */
@media (prefers-reduced-motion: reduce) {
    .calc-visual__spinner { animation: none; border-top-color: var(--calc-primary); }
}

/* A configuration note on a chart - shown in the owner's preview only, never
   on a published page, because a visitor can do nothing about it. */
.calc-chart__notice {
    margin-top: 8px;
    padding: 8px 11px;
    border-radius: var(--calc-border-radius);
    background: #fdf3e6;
    border: 1px solid #f5ddb9;
    color: #8a4d05;
    font-size: 12.5px;
    line-height: 1.5;
}

/* ===========================================================================
   Richer field types

   Every control below is built on a real <input> - a checkbox, a radio, a
   select - with a label wrapped around it. None of them replace the control
   with a div and a click handler, so the keyboard, the screen reader, the
   browser's own "required" and a phone's native picker all keep working
   without any of it being written again here.
   =========================================================================== */

/* Long text ----------------------------------------------------------------- */

.calc-textarea {
    display: block;
    min-height: 92px;
    line-height: 1.5;
    resize: vertical;
}

.calc-counter {
    display: block;
    margin-top: 5px;
    font-size: 12px;
    color: var(--calc-muted-text);
}

/* The count turns when the limit is close, so running out is not a surprise
   that arrives as keystrokes silently stopping. */
.calc-counter.is-near { color: var(--calc-error); }

/* Image / icon choice cards ------------------------------------------------- */

.calc-choice-cards {
    display: grid;
    grid-template-columns: repeat(var(--calc-choice-columns, 4), minmax(0, 1fr));
    gap: 10px;
}

/*
 * The cards stack on a narrow calculator regardless of how many per row the
 * owner asked for. Four 60px cards are not four cards, they are unreadable.
 */
@container (max-width: 520px) {
    .calc-choice-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@supports not (container-type: inline-size) {
    @media (max-width: 560px) {
        .calc-choice-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    }
}

.calc-choice-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: 9px;
    padding: 34px 10px 14px;
    text-align: center;
    background: var(--calc-input-bg);
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: var(--calc-border-radius);
    cursor: pointer;
    transition: border-color .12s ease, box-shadow .12s ease, background-color .12s ease;
}

.calc-choice-card:hover { border-color: var(--calc-primary); }

/*
 * The real control, kept in the page and taken out of sight rather than
 * display:none - a hidden input is not focusable, so the whole set would drop
 * out of the tab order and off a screen reader.
 */
.calc-choice-card input,
.calc-choice-button input,
.calc-toggle input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
    margin: 0;
}

/* The tick in the corner. Drawn here because the native box cannot be placed
   independently of its label, and the card needs it in a fixed spot. */
.calc-choice-card__box {
    position: absolute;
    top: 10px;
    left: 10px;
    width: 19px;
    height: 19px;
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: 5px;
    background: var(--calc-input-bg);
    transition: background-color .12s ease, border-color .12s ease;
}

.calc-choice-card__box::after {
    content: "";
    position: absolute;
    left: 6px;
    top: 2px;
    width: 5px;
    height: 10px;
    border: solid var(--calc-choice-mark, #fff);
    border-width: 0 2px 2px 0;
    transform: rotate(45deg) scale(0);
    transition: transform .12s ease;
}

.calc-choice-card input:checked ~ .calc-choice-card__box {
    background: var(--calc-choice-bg, var(--calc-primary));
    border-color: var(--calc-choice-bg, var(--calc-primary));
}

.calc-choice-card input:checked ~ .calc-choice-card__box::after { transform: rotate(45deg) scale(1); }

/*
 * :has() styles the whole card from the state of the input inside it. Where it
 * is missing the tick above still tells the story, so a chosen card simply
 * does not gain its ring - it is never left looking unselected.
 */
.calc-choice-card:has(input:checked) {
    border-color: var(--calc-primary);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--calc-primary) 22%, transparent);
}

.calc-choice-card input:focus-visible ~ .calc-choice-card__box {
    outline: 2px solid var(--calc-input-focus-border);
    outline-offset: 2px;
}

.calc-choice-card__icon { font-size: 30px; line-height: 1; color: var(--calc-primary); }

/*
 * The picture on a choice card.
 *
 * Sized by two variables the element sets when the owner asks for something
 * other than the default. --calc-choice-img is a WIDTH; the height follows it
 * so a photograph keeps its shape, which is the whole point of showing a
 * bigger one. At the default 74px the height is pinned to 46px, because at
 * icon size a row of cards looks wrong if each one is a different height.
 */
.calc-choice-card__image {
    width: 100%;
    max-width: var(--calc-choice-img, 74px);
    height: 46px;
    object-fit: contain;
    border-radius: var(--calc-choice-img-radius, 0);
}

/*
 * Given a size of its own, the picture stops being an icon and starts being
 * the choice - so it fills the width it was given, keeps its aspect ratio,
 * and is cropped rather than letterboxed. A tile sample with grey bars down
 * both sides is not a tile sample.
 */
.calc-choice-cards--sized .calc-choice-card__image {
    height: auto;
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

/*
 * The stand-in shown on a card with no icon and no picture yet.
 *
 * Square and faded, so it reads as an empty slot rather than as a picture
 * somebody chose. Without it the card is a box with a checkbox floating in the
 * corner and a label at the bottom, which stops looking like a card at all -
 * and the owner cannot see where their icon is going to land.
 */
.calc-choice-card__image--placeholder {
    width: 46px;
    max-width: 46px;
    height: 46px;
    opacity: .45;
}

.calc-choice-card__label {
    font-size: 13.5px;
    line-height: 1.35;
    color: var(--calc-text);
}

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

.calc-choice-buttons { display: flex; gap: 10px; }
.calc-choice-buttons--horizontal { flex-wrap: wrap; }
.calc-choice-buttons--vertical { flex-direction: column; }
.calc-choice-buttons--fill > .calc-choice-button { flex: 1 1 0; min-width: 0; }

.calc-choice-button {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    /* The field's own size where it set one - see calc_render_choice_cards().
       The values here are the fallbacks, so a field that sets nothing is
       drawn exactly as it was before the settings existed. */
    padding: var(--calc-choice-btn-pad-y, 12px) var(--calc-choice-btn-pad-x, 18px);
    font-size: var(--calc-choice-btn-size, 14.5px);
    font-weight: 500;
    color: var(--calc-text);
    background: var(--calc-input-bg);
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: var(--calc-button-radius);
    cursor: pointer;
    transition: border-color .12s ease, background-color .12s ease, color .12s ease;
}

.calc-choice-button:hover { border-color: var(--calc-primary); }

.calc-choice-button:has(input:checked) {
    background: var(--calc-primary-button-bg);
    border-color: var(--calc-primary-button-bg);
    color: var(--calc-primary-button-text);
}

.calc-choice-button input:focus-visible ~ .calc-choice-button__label {
    outline: 2px solid var(--calc-input-focus-border);
    outline-offset: 3px;
}

/*
 * The picture on a button, at whatever size the field asked for.
 *
 * The stylesheet keeps its own value as the fallback, so a field that sets
 * nothing is drawn exactly as it always was - 15px for a glyph and 18px for a
 * picture, sized to sit beside a line of text.
 *
 * A picture is given a line-height to match, or a large one sits on a button
 * still only as tall as the words next to it.
 */
.calc-choice-button .calc-choice-card__icon {
    flex: 0 0 auto;
    font-size: var(--calc-choice-btn-icon, 15px);
    line-height: 1;
}

.calc-choice-button .calc-choice-card__image {
    flex: 0 0 auto;
    width: var(--calc-choice-btn-icon, 18px);
    height: var(--calc-choice-btn-icon, 18px);
    max-width: var(--calc-choice-btn-icon, 18px);
    object-fit: contain;
}
.calc-choice-button:has(input:checked) .calc-choice-card__icon { color: inherit; }

/* Multi-select dropdown ----------------------------------------------------- */

.calc-multiselect { position: relative; }

.calc-multiselect__toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    text-align: left;
    cursor: pointer;
}

.calc-multiselect__summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.calc-multiselect__summary.is-empty { color: var(--calc-placeholder); }

.calc-multiselect__caret {
    flex: 0 0 auto;
    width: 9px;
    height: 9px;
    border-right: 2px solid var(--calc-muted-text);
    border-bottom: 2px solid var(--calc-muted-text);
    transform: rotate(45deg) translate(-2px, -2px);
}

.calc-multiselect__toggle[aria-expanded="true"] .calc-multiselect__caret {
    transform: rotate(-135deg) translate(-2px, -2px);
}

/*
 * Absolute, so opening the list does not push everything below it down the
 * page. z-index sits above the fields around it but nothing else - this is
 * inside an iframe on somebody else's page and has no business competing with
 * their header.
 */
.calc-multiselect__panel {
    position: absolute;
    z-index: 20;
    left: 0;
    right: 0;
    top: calc(100% + 6px);
    padding: 10px;
    background: var(--calc-input-bg);
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: var(--calc-border-radius);
    box-shadow: 0 12px 28px rgba(15, 23, 42, .13);
}

.calc-multiselect__panel[hidden] { display: none; }

.calc-multiselect__search { margin-bottom: 8px; }

.calc-multiselect__list { max-height: 220px; overflow-y: auto; }

/*
 * The same row as the single picker's, inset from the panel's own padding so
 * the highlight reads as a pill on the list rather than as a band across the
 * whole panel. It was padded 7px 4px here and 7px 8px there, so the two
 * dropdowns in one form highlighted differently.
 */
.calc-multiselect__option {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 8px;
    font-size: 14.5px;
    color: var(--calc-text);
    cursor: pointer;
    border-radius: 6px;
}

.calc-multiselect__option:hover { background: var(--calc-surface); }
.calc-multiselect__option[hidden] { display: none; }

/* Same drawn control as .calc-choice input, so a tick colour set in the theme
   applies here too rather than only to the checkboxes outside a dropdown. */
.calc-multiselect__option input {
    -webkit-appearance: none;
    appearance: none;
    position: relative;
    width: 17px;
    height: 17px;
    flex: 0 0 17px;
    margin: 0;
    background: var(--calc-input-bg);
    border: 1px solid var(--calc-input-border);
    border-radius: 4px;
    cursor: pointer;
}

.calc-multiselect__option input:checked {
    background: var(--calc-choice-bg, var(--calc-primary));
    border-color: var(--calc-choice-bg, var(--calc-primary));
}

.calc-multiselect__option input::after {
    content: "";
    position: absolute;
    left: 5px;
    top: 1px;
    width: 4px;
    height: 9px;
    border: solid var(--calc-choice-mark, #fff);
    border-width: 0 2px 2px 0;
    transform: rotate(45deg) scale(0);
    transition: transform .12s ease;
}

.calc-multiselect__option input:checked::after { transform: rotate(45deg) scale(1); }

.calc-multiselect__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 8px;
    padding-top: 9px;
    border-top: 1px solid var(--calc-border);
    font-size: 13px;
    color: var(--calc-muted-text);
}

.calc-multiselect__clear {
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    font-weight: 600;
    color: var(--calc-primary);
    cursor: pointer;
}

.calc-multiselect__clear:hover { text-decoration: underline; }

.calc-multiselect__none {
    padding: 12px 4px;
    font-size: 13.5px;
    color: var(--calc-muted-text);
}

.calc-multiselect__none[hidden] { display: none; }

/* Country and international phone ------------------------------------------- */

/*
 * A custom listbox, not a <select>.
 *
 * Emoji flags were the first attempt: Windows ships no flag glyphs, so Chrome
 * and Edge there drew "us" in letters. Real flag images cannot go in a native
 * option - an option renders text and nothing else - so the list had to be
 * built by hand.
 *
 * The native <select> is still in the markup and still holds the value. It is
 * what posts and what the runtime reads; this is a face over it, not a
 * replacement for it. With no JavaScript a <noscript> block in the markup
 * swaps them back and the plain select works as it always did.
 */

.calc-picker { position: relative; }

.calc-picker__native {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
    /* Not display:none - a hidden control is skipped by "required" and by
       autofill, and this one is still the real field. */
}

.calc-picker__toggle {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    text-align: left;
    cursor: pointer;
}

.calc-picker__label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The placeholder is not an answer, so it reads like one of those. */
/* A dropdown showing its placeholder is showing a placeholder, so it takes
   the placeholder's size as well as its colour. */
.calc-picker__label.is-empty {
    color: var(--calc-placeholder);
    font-size: var(--calc-placeholder-size, 15px);
}

/* A plain dropdown - no flag, so the panel matches the control's width
   rather than the wider one a list of country names needs. */
.calc-picker--plain .calc-picker__panel { min-width: 0; }
.calc-picker__dial { flex: 0 0 auto; color: var(--calc-input-text); }

/*
 * The flag's own aspect ratio is 4:3, and a box that does not match it letters
 * a white margin down each side of every flag. The border keeps a white flag -
 * Japan, Switzerland's field - from disappearing into a white control.
 */
.calc-flag-img {
    flex: 0 0 auto;
    width: 22px;
    height: 16.5px;
    object-fit: cover;
    border-radius: 2px;
    box-shadow: 0 0 0 1px rgba(15, 23, 42, .12);
}

.calc-picker--dial { flex: 0 0 auto; }
.calc-picker--dial .calc-picker__toggle { gap: 6px; padding-right: 10px; }
.calc-picker--dial .calc-picker__label { display: none; }

/* The panel, shared in look with the multi-select's so the two read as one
   family rather than as two components that happen to be on the same page. */
.calc-picker__panel {
    position: absolute;
    z-index: 25;
    left: 0;
    min-width: 260px;
    top: calc(100% + 6px);
    padding: 10px;
    background: var(--calc-input-bg);
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: var(--calc-border-radius);
    box-shadow: 0 12px 28px rgba(15, 23, 42, .13);
}

.calc-picker:not(.calc-picker--dial) .calc-picker__panel { right: 0; }

.calc-picker__panel[hidden] { display: none; }
.calc-picker__search { margin-bottom: 8px; }

.calc-picker__list { max-height: 244px; overflow-y: auto; }

.calc-picker__option {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 8px;
    font-size: 14.5px;
    color: var(--calc-text);
    cursor: pointer;
    border-radius: 6px;
}

.calc-picker__option[hidden] { display: none; }

/*
 * One highlight for both the mouse and the keyboard. Two - a hover colour and
 * a separate "active" one - means the row under the pointer and the row the
 * arrow keys are on can both look chosen, and Enter then takes the other one.
 */
.calc-picker__option:hover,
.calc-picker__option.is-active {
    background: var(--calc-surface);
}

.calc-picker__option[aria-selected="true"] { font-weight: 600; }

.calc-picker__name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.calc-picker__option .calc-picker__dial { color: var(--calc-muted-text); font-size: 13.5px; }

/*
 * The picture on an option, and the space it occupies when there is none.
 *
 * One fixed box either way. The label column has to start in the same place on
 * every row: a list of ten options where three have icons must not read as
 * three lists.
 */
.calc-picker__icon {
    flex: 0 0 20px;
    width: 20px;
    height: 20px;
    object-fit: contain;
    display: inline-grid;
    place-items: center;
    font-size: 15px;
    color: var(--calc-primary);
}

/* An option with no picture. Faint, so it reads as a space rather than as a
   broken image - but drawn, so the row cannot collapse it. */
.calc-picker__icon--none {
    border: 1px dashed var(--calc-border);
    border-radius: 5px;
    opacity: .55;
}

/*
 * Icons after the label. The row is a flex line, so this is an ordering
 * question rather than a positioning one - and the label takes the space in
 * between either way, which is what keeps the icons in one column down the
 * right-hand edge.
 */
.calc-picker--icons-right .calc-picker__icon { order: 2; }
.calc-picker--icons-right .calc-picker__name,
.calc-picker--icons-right .calc-picker__label { order: 1; }

.calc-picker--icons-right .calc-picker__label { flex: 1 1 auto; }

.calc-picker__none { padding: 12px 6px; font-size: 13.5px; color: var(--calc-muted-text); }
.calc-picker__none[hidden] { display: none; }

.calc-phone-intl { display: flex; gap: 8px; align-items: stretch; }
.calc-phone-intl__number { flex: 1 1 auto; min-width: 0; }

/* Yes / No ------------------------------------------------------------------ */

.calc-toggle {
    display: inline-flex;
    width: 100%;
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: var(--calc-border-radius);
    overflow: hidden;
}

.calc-toggle__side {
    position: relative;
    flex: 1 1 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 12px 16px;
    font-size: 14.5px;
    font-weight: 500;
    color: var(--calc-text);
    background: var(--calc-input-bg);
    cursor: pointer;
    transition: background-color .12s ease, color .12s ease;
}

.calc-toggle__side:hover { background: var(--calc-surface); }

.calc-toggle__side:has(input:checked) {
    background: var(--calc-primary-button-bg);
    color: var(--calc-primary-button-text);
}

.calc-toggle__side:has(input:checked):hover { background: var(--calc-primary-button-bg); }

/* The tick only exists on Yes, and only once Yes is the answer. */
.calc-toggle__tick { display: none; width: 14px; height: 8px; }

.calc-toggle__side--yes:has(input:checked) .calc-toggle__tick {
    display: inline-block;
    border: solid currentColor;
    border-width: 0 0 2px 2px;
    transform: rotate(-45deg) translate(1px, -2px);
}

.calc-toggle__side input:focus-visible ~ span {
    outline: 2px solid var(--calc-input-focus-border);
    outline-offset: 3px;
}

/* WhatsApp ------------------------------------------------------------------ */

/*
 * WhatsApp green is fixed, not themed. It is the brand's colour and the whole
 * point of the button is that it is recognised without being read - a
 * theme-coloured WhatsApp button is just a button.
 */
.calc-btn--whatsapp {
    background: #25d366;
    border-color: #25d366;
    color: #fff;
}

.calc-btn--whatsapp:hover { background: #1eb855; border-color: #1eb855; color: #fff; }

.calc-whatsapp__glyph { display: inline-flex; align-items: center; }

/*
 * Pinned to the calculator, not the window. An embedded calculator is an
 * iframe on somebody else's page; a bubble that escaped it would land in the
 * middle of their content, and position:fixed inside a frame is measured
 * against the frame anyway. Sticking it to the calculator makes that a
 * decision rather than an accident.
 */
.calc { position: relative; }

.calc-whatsapp-bubble {
    position: absolute;
    right: 18px;
    bottom: 18px;
    z-index: 30;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: #25d366;
    color: #fff;
    box-shadow: 0 6px 20px rgba(37, 211, 102, .42);
    transition: transform .14s ease;
}

.calc-whatsapp-bubble:hover { transform: scale(1.06); }
.calc-whatsapp-bubble svg { width: 30px; height: 30px; }

@media (prefers-reduced-motion: reduce) {
    .calc-whatsapp-bubble { transition: none; }
    .calc-whatsapp-bubble:hover { transform: none; }
}

/* reCAPTCHA ----------------------------------------------------------------- */

.calc-recaptcha { display: inline-block; margin-bottom: 12px; text-align: left; }

/*
 * The widget is a fixed-width iframe Google serves and cannot be made
 * responsive. On a calculator narrower than it, it is scaled down rather than
 * left to push a horizontal scrollbar onto somebody else's page.
 */
@container (max-width: 340px) {
    .calc-recaptcha { transform: scale(.86); transform-origin: left top; }
}

.calc-recaptcha__error {
    margin: 6px 0 0;
    font-size: 13px;
    color: var(--calc-error);
}

/* ===========================================================================
   Fields with something inside them

   An icon or a symbol sharing the box the visitor types into. The border moves
   from the input to the wrapper so the two read as one field - an icon in its
   own box beside an input in its own box is two controls, and nobody can tell
   which one to click.
   =========================================================================== */

.calc-field {
    display: flex;
    align-items: stretch;
    gap: 0;
    background: var(--calc-input-bg);
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: var(--calc-border-radius);
    transition: border-color .12s ease, box-shadow .12s ease;
}

/* The focus ring belongs to the whole field now, not to the input inside it. */
.calc-field:focus-within {
    border-color: var(--calc-input-focus-border);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--calc-input-focus-border) 22%, transparent);
}

/*
 * The input gives up its own frame. Its border and background would draw a
 * second box inside the first, and its focus ring would sit inside the
 * wrapper's.
 */
.calc-field > .calc-input {
    border: 0;
    background: transparent;
    border-radius: 0;
    flex: 1 1 auto;
    min-width: 0;
    /* The affix owns the left inset here; the input adding its own on top is
       what put the visible gap between the icon and the first character. */
    padding-left: 4px;
    padding-right: 12px;
}

.calc-field > .calc-input:focus { box-shadow: none; }

/*
 * The affix is only as wide as what is in it.
 *
 * It used to be a 42px box with the icon centred in it, which left the icon
 * floating well clear of the text - the visitor reads a gap the icon is not
 * responsible for. Now the padding sets the two distances directly: the icon
 * sits in from the border, and the text follows just after it.
 */
.calc-field__affix {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
    padding: 0 7px 0 12px;
    color: var(--calc-primary);
    font-size: 15px;
}

/*
 * A currency or percentage symbol gets a tinted tile; an icon does not.
 *
 * The symbol is part of the VALUE - "$" and "1,250.00" are one figure read
 * together - so it is given a surface of its own to sit on. An icon only says
 * what the field is for, and a tile around it would give it the same weight as
 * the answer.
 */
/* The tile keeps a width of its own: it is a block beside the value, and one
   sized to the character would jump about between "$" and "USD". */
.calc-field__affix--symbol {
    margin: 5px 0 5px 5px;
    border-radius: calc(var(--calc-border-radius) - 3px);
    background: color-mix(in srgb, var(--calc-primary) 11%, transparent);
    font-weight: 640;
    min-width: 38px;
    padding: 0 8px;
}

/* A textarea's icon sits at the top: centred against six lines of text it
   floats in the middle of the box with nothing to align to. */
.calc-field:has(.calc-textarea) { align-items: flex-start; }
.calc-field:has(.calc-textarea) .calc-field__affix { padding-top: 10px; }

/* ---------------------------------------------------------------------------
   Dates and times

   The field already shows a calendar or a clock in its affix, so the icon the
   browser draws on the right is the same thing said twice - and the one on the
   right is the only one that opens anything, which is worse than redundant:
   two icons, one of which does nothing.

   The native one is taken out of sight and stretched over the whole control
   instead of being removed. display:none would take the picker away from any
   browser where showPicker() does not exist, leaving a date that can only be
   typed. Invisible and full-width, it stays as the mechanism: a click anywhere
   on the field lands on the browser's own trigger, and the affix icon becomes
   the thing that appears to open it.
   --------------------------------------------------------------------------- */

.calc-field > input[type="date"],
.calc-field > input[type="time"] { padding-left: 2px; cursor: pointer; }

.calc-field:has(> input[type="date"]),
.calc-field:has(> input[type="time"]) { position: relative; }

.calc-field > input[type="date"]::-webkit-calendar-picker-indicator,
.calc-field > input[type="time"]::-webkit-calendar-picker-indicator,
.calc-appointment__date::-webkit-calendar-picker-indicator {
    position: absolute;
    inset: 0;
    width: auto;
    height: auto;
    margin: 0;
    padding: 0;
    opacity: 0;
    cursor: pointer;
}

/* The appointment's date box has no affix wrapper of its own, so it carries
   the positioning the rule above needs. */
.calc-appointment__date { position: relative; cursor: pointer; }

/* An empty date reads as a prompt, not as a value somebody chose. */
.calc-field > input[type="date"]:invalid,
.calc-field > input[type="time"]:invalid,
.calc-appointment__date:invalid { color: var(--calc-placeholder); }

/* File drop zone ------------------------------------------------------------
   A <label> wrapping the real file input, so the whole area is clickable with
   no script at all. Dragging is the part the runtime adds. */

.calc-drop {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 16px;
    border: 1.5px dashed color-mix(in srgb, var(--calc-primary) 42%, var(--calc-border));
    border-radius: var(--calc-border-radius);
    background: color-mix(in srgb, var(--calc-primary) 3%, var(--calc-input-bg));
    cursor: pointer;
    transition: border-color .12s ease, background-color .12s ease;
}

.calc-drop:hover { border-color: var(--calc-primary); }

/* Dragging over it. A colour change is the only feedback a drop target can
   give, and without one nobody knows whether letting go will work. */
.calc-drop.is-over {
    border-color: var(--calc-primary);
    border-style: solid;
    background: color-mix(in srgb, var(--calc-primary) 9%, var(--calc-input-bg));
}

/*
 * The input is taken out of sight, not out of the page. display:none would
 * drop it from the tab order and from "required", and it is still the control
 * holding the file.
 */
.calc-drop input[type="file"] {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
}

.calc-drop__icon {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 46px;
    height: 46px;
    border-radius: 11px;
    background: color-mix(in srgb, var(--calc-primary) 13%, transparent);
    color: var(--calc-primary);
    font-size: 19px;
}

.calc-drop__text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }

.calc-drop__title { font-size: 13.5px; font-weight: 620; color: var(--calc-text); }

/* "or drag and drop" is the alternative, not the instruction, so it recedes. */
.calc-drop__or { font-weight: 400; color: var(--calc-muted-text); }

.calc-drop__name {
    font-size: 12.5px;
    color: var(--calc-muted-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Once a file is chosen its name is the useful line, not a grey aside. */
.calc-drop__name.is-chosen { color: var(--calc-text); font-weight: 560; }

/*
 * What the field accepts. The quietest thing in the box: somebody who is about
 * to attach a PDF does not need telling that PDFs are allowed, and somebody
 * holding a video does - so it has to be readable without competing with the
 * instruction above it.
 */
.calc-drop__types {
    display: block;
    margin-top: 2px;
    font-size: 11.5px;
    line-height: 1.4;
    color: var(--calc-muted-text);
    opacity: .85;
}

.calc-drop__browse {
    flex: 0 0 auto;
    padding: 9px 18px;
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: var(--calc-button-radius);
    background: var(--calc-input-bg);
    color: var(--calc-primary);
    font-size: 14px;
    font-weight: 600;
}

.calc-drop:hover .calc-drop__browse { border-color: var(--calc-primary); }

/* ---------------------------------------------------------------------------
 * Files attached to a multiple-upload field
 *
 * Below the zone rather than inside it: a <label> forwards every click to its
 * control, so a remove button in there would open the file picker on its way
 * to removing the file.
 * ------------------------------------------------------------------------ */
.calc-files {
    list-style: none;
    margin: 8px 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.calc-files[hidden] { display: none; }

.calc-files__item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 10px 7px 12px;
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: var(--calc-border-radius);
    background: var(--calc-input-bg);
    font-size: 13.5px;
    color: var(--calc-text);
}

/* A long filename shortens rather than pushing the remove button off. */
.calc-files__name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.calc-files__remove {
    flex: 0 0 auto;
    width: 24px;
    height: 24px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--calc-muted-text);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    transition: background-color .12s ease, color .12s ease;
}

.calc-files__remove:hover {
    background: color-mix(in srgb, var(--calc-error, #b42318) 12%, transparent);
    color: var(--calc-error, #b42318);
}

.calc-files__remove:focus-visible {
    outline: 2px solid var(--calc-input-focus-border);
    outline-offset: 1px;
}

/* The input is invisible, so its focus ring has to be drawn on the zone. */
.calc-drop:focus-within {
    border-color: var(--calc-input-focus-border);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--calc-input-focus-border) 22%, transparent);
}

/*
 * Narrow: the Browse button drops under the text rather than squeezing the
 * filename into three characters and an ellipsis.
 */
@container (max-width: 420px) {
    .calc-drop { flex-wrap: wrap; }
    .calc-drop__browse { width: 100%; text-align: center; }
}

@supports not (container-type: inline-size) {
    @media (max-width: 460px) {
        .calc-drop { flex-wrap: wrap; }
        .calc-drop__browse { width: 100%; text-align: center; }
    }
}

/* Toggle switch -------------------------------------------------------------
   The alternative shape for a Yes/No field: a switch and one label, where the
   segmented pair is two labels and a choice. A switch suits a setting being
   turned on; the pair suits a question with two real answers. */

.calc-switch { display: inline-flex; align-items: center; gap: 11px; cursor: pointer; }

.calc-switch input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.calc-switch__track {
    flex: 0 0 auto;
    position: relative;
    width: 46px;
    height: 26px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--calc-text) 22%, transparent);
    transition: background-color .16s ease;
}

.calc-switch__track::after {
    content: "";
    position: absolute;
    top: 3px;
    left: 3px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgba(15, 23, 42, .3);
    transition: transform .16s ease;
}

.calc-switch input:checked ~ .calc-switch__track { background: var(--calc-primary); }
.calc-switch input:checked ~ .calc-switch__track::after { transform: translateX(20px); }

.calc-switch input:focus-visible ~ .calc-switch__track {
    outline: 2px solid var(--calc-input-focus-border);
    outline-offset: 3px;
}

.calc-switch__label { font-size: 14.5px; color: var(--calc-text); }

@media (prefers-reduced-motion: reduce) {
    .calc-switch__track, .calc-switch__track::after { transition: none; }
}

/* Embeds and slider -------------------------------------------------------- */

.calc-embed { width: 100%; }

/* Inline-block so text-align on the wrapper positions it, which is how every
   other alignable element in this stylesheet is placed. */
.calc-embed__frame { display: inline-block; max-width: 100%; background: var(--calc-surface); }

.calc-embed__empty {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    max-width: 100%;
    padding: 20px;
    text-align: center;
    background: var(--calc-surface);
    border: 1px dashed var(--calc-border);
    color: var(--calc-muted-text);
    font-size: 13.5px;
}

.calc-embed__link {
    display: inline-block;
    margin-top: 8px;
    font-size: 13.5px;
    color: var(--calc-primary);
}

.calc-gallery {
    position: relative;
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    background: var(--calc-surface);
}

.calc-gallery__track { position: relative; width: 100%; height: 100%; }

/*
 * Every slide is in the page; one is shown. Opacity rather than display, so a
 * slide that is arriving can be faded rather than appearing all at once - and
 * so the images are all laid out and measured before anybody presses an arrow.
 */
.calc-gallery__slide {
    position: absolute;
    inset: 0;
    margin: 0;
    opacity: 0;
    visibility: hidden;
    transition: opacity .32s ease;
}

.calc-gallery__slide.is-current { opacity: 1; visibility: visible; }
.calc-gallery__slide img { width: 100%; height: 100%; object-fit: cover; display: block; }

.calc-gallery__slide figcaption {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    padding: 10px 14px;
    /* Clear of the arrows, which sit halfway down at the sides and overlap a
       caption that runs the full width. */
    padding-left: 52px;
    padding-right: 52px;
    /* A gradient, not a solid bar: a caption over a light photo needs the
       contrast, and a solid strip would crop the picture instead. */
    background: linear-gradient(transparent, rgba(15, 23, 42, .72));
    color: #fff;
    font-size: 13.5px;
}

/*
 * A caption above the dots, not under them.
 *
 * Both sit along the bottom of the slide: the dots 10px up, the caption from
 * the very bottom - so a slider with both drew the dots straight through the
 * middle of the words. The caption gives way, because the dots have a fixed
 * place to be and a caption is a line of text that can start higher.
 *
 * Only where there are dots to clear; see calc_render_gallery(). A slider
 * without them would carry a band of empty space under every caption.
 */
.calc-gallery--dotted .calc-gallery__slide figcaption { padding-bottom: 30px; }

.calc-gallery__arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 36px;
    height: 36px;
    border: 0;
    border-radius: 50%;
    background: rgba(15, 23, 42, .48);
    color: #fff;
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
}

.calc-gallery__arrow:hover { background: rgba(15, 23, 42, .72); }
.calc-gallery__arrow--prev { left: 10px; }
.calc-gallery__arrow--next { right: 10px; }

.calc-gallery__dots {
    position: absolute;
    left: 0; right: 0; bottom: 10px;
    display: flex;
    justify-content: center;
    gap: 6px;
}

.calc-gallery__dot {
    width: 8px; height: 8px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, .55);
    cursor: pointer;
}

.calc-gallery__dot.is-current { background: #fff; width: 20px; border-radius: 999px; }

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

/* Appointment -------------------------------------------------------------- */

.calc-appointment { display: flex; gap: 8px; flex-wrap: wrap; }
.calc-appointment__date { flex: 1 1 160px; min-width: 0; }
.calc-appointment__time { flex: 1 1 140px; min-width: 0; }

/* Said out loud, because a date picker that silently refuses half its days
   reads as broken rather than as restricted. */
.calc-appointment__note { flex: 1 1 100%; color: var(--calc-error); }
.calc-appointment__note[hidden] { display: none; }

/* ===========================================================================
   Google reviews

   Everything except the Google wordmark and the stars is drawn from --calc-*
   variables, so the widget follows the calculator's theme - light, dark or
   custom - without a second set of colours to keep in step. The logo keeps its
   four brand colours because a monochrome Google mark says nothing, and gold
   stars are gold everywhere.
   =========================================================================== */

.calc-reviews { width: 100%; }

.calc-reviews__head {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    margin-bottom: 16px;
}

.calc-reviews__google {
    font-family: "Product Sans", "Segoe UI", Roboto, Arial, sans-serif;
    font-size: 30px;
    font-weight: 600;
    letter-spacing: -.5px;
    line-height: 1;
}

.calc-reviews__score { display: flex; align-items: center; gap: 8px; }

.calc-reviews__rating { font-size: 17px; font-weight: 700; color: var(--calc-heading); }
.calc-reviews__total { font-size: 13.5px; color: var(--calc-muted-text); }

/* Stars ---------------------------------------------------------------------
   A gold star clipped over a grey one, so 4.5 shows as four and a half rather
   than rounding to something the business did not earn. */

.calc-reviews__stars { display: inline-flex; gap: 1px; line-height: 1; font-size: 17px; }
.calc-reviews__star { position: relative; display: inline-block; width: 1em; height: 1em; }
.calc-reviews__star-off { color: #d9dee7; }

.calc-reviews__star-on {
    position: absolute;
    inset: 0;
    color: #fbbc05;
    overflow: hidden;
    white-space: nowrap;
}

/* Layouts -------------------------------------------------------------------
   One list, three arrangements. Grid reflows by itself, so it needs no
   breakpoint; the scroller and the stack are a row and a column of the same
   cards. */

.calc-reviews__list { display: grid; gap: 12px; text-align: left; }

.calc-reviews--grid .calc-reviews__list {
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}

.calc-reviews--stack .calc-reviews__list { grid-template-columns: 1fr; }

/*
 * A row that scrolls. Scroll snapping so a swipe lands on a card rather than
 * between two, and a little end padding so the last card does not sit flush
 * against the edge.
 */
.calc-reviews--scroll .calc-reviews__list {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding-bottom: 6px;
    scrollbar-width: thin;
}

.calc-reviews--scroll .calc-reviews__card {
    flex: 0 0 min(280px, 82%);
    scroll-snap-align: start;
}

.calc-reviews__card {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 14px;
    background: var(--calc-input-bg);
    border: var(--calc-input-border-width) solid var(--calc-border);
    border-radius: var(--calc-border-radius);
}

.calc-reviews__author { display: flex; align-items: center; gap: 10px; }

.calc-reviews__avatar {
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--calc-surface);
}

/* No picture is not a hole: the reviewer's initial fills the circle. */
.calc-reviews__avatar--letter {
    display: grid;
    place-items: center;
    color: var(--calc-muted-text);
    font-weight: 700;
    font-size: 15px;
}

.calc-reviews__who { display: flex; flex-direction: column; min-width: 0; }

.calc-reviews__name {
    font-size: 14px;
    font-weight: 620;
    color: var(--calc-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.calc-reviews__when { font-size: 12px; color: var(--calc-muted-text); }

/*
 * Clipped by CSS, not by cutting the string.
 *
 * The whole review stays in the markup - readable by a screen reader, printed
 * in full, and expanded by anyone who removes the clamp - and the number of
 * lines shown adapts to the width, which a fixed character count cannot.
 */
.calc-reviews__text {
    margin: 0;
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--calc-muted-text);
    display: -webkit-box;
    -webkit-line-clamp: 5;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Stacked cards have the full width to play with, so they show more. */
.calc-reviews--stack .calc-reviews__text { -webkit-line-clamp: 8; }

/* The way through to the rest of the reviews. Centred under the list, since
   it belongs to all of them rather than to the last card. */
.calc-reviews__more {
    display: inline-block;
    margin-top: 14px;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--calc-primary);
    text-decoration: none;
}

.calc-reviews__more:hover { text-decoration: underline; }

.calc-reviews__empty {
    padding: 18px;
    border: 1px dashed var(--calc-border);
    border-radius: var(--calc-border-radius);
    color: var(--calc-muted-text);
    font-size: 13.5px;
    text-align: center;
}

/* Narrow: the grid becomes a single column rather than two 100px cards. */
@container (max-width: 420px) {
    .calc-reviews--grid .calc-reviews__list { grid-template-columns: 1fr; }
}

@supports not (container-type: inline-size) {
    @media (max-width: 460px) {
        .calc-reviews--grid .calc-reviews__list { grid-template-columns: 1fr; }
    }
}

/* Spacer ---------------------------------------------------------------------
   A gap and nothing else. The two heights arrive as custom properties; which
   one applies is decided here, by the width of the CALCULATOR rather than the
   screen - so an embed in a narrow sidebar of a wide monitor gets the narrow
   spacing, which is the only reason to have a second number at all. */

.calc-spacer { height: var(--calc-spacer, 40px); }

/* A faint rule centred in the gap, for splitting a long form in two. */
.calc-spacer--rule {
    display: flex;
    align-items: center;
}

.calc-spacer--rule::before {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: var(--calc-border);
}

@container (max-width: 520px) {
    .calc-spacer { height: var(--calc-spacer-narrow, 24px); }
}

@supports not (container-type: inline-size) {
    @media (max-width: 560px) {
        .calc-spacer { height: var(--calc-spacer-narrow, 24px); }
    }
}

/* A map the visitor can move the pin on. The canvas fills the element the
   embed would otherwise have occupied, so the height setting still applies. */
.calc-google-map__canvas { width: 100%; height: 100%; }

.calc-google-map__hint {
    margin: 6px 0 0;
    color: var(--calc-muted-text);
    font-size: 12.5px;
    text-align: left;
}

/* A send button held while an upload finishes. Dimmed and not-allowed rather
   than truly disabled: a disabled button gives no tooltip and no click, so the
   visitor gets no explanation for why nothing happened. */
/*
 * A button waiting for an upload, and the reason on hover.
 *
 * Drawn here rather than left to the browser's own tooltip, because a native
 * one cannot be closed from script. Removing `title` stops the next hover and
 * leaves the tooltip already on screen sitting there - so a visitor who kept
 * the pointer on the button watched it come back to life still saying it was
 * waiting. This disappears with the class, whatever the pointer is doing.
 */
.calc-btn.is-waiting { opacity: .6; cursor: progress; position: relative; }

.calc-btn.is-waiting::after {
    content: attr(data-waiting);
    position: absolute;
    top: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    z-index: 5;
    padding: 5px 9px;
    border-radius: 5px;
    background: #1f2937;
    color: #fff;
    font-size: 12px;
    line-height: 1.4;
    white-space: nowrap;
    /* Shown on hover only, and never in the way of a press: the button is what
       the pointer is over, not this. */
    opacity: 0;
    pointer-events: none;
    transition: opacity .12s ease;
}

.calc-btn.is-waiting:hover::after,
.calc-btn.is-waiting:focus-visible::after { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
    .calc-btn.is-waiting::after { transition: none; }
}

/* ---------------------------------------------------------------------------
   A file going up
   --------------------------------------------------------------------------- */

/*
 * The drop zone stops accepting clicks entirely while an upload runs.
 *
 * pointer-events, not just a disabled input: the LABEL is what opens the file
 * picker, and the drop zone takes dragged files too. With only the input
 * disabled, both still worked - so a second file could be chosen mid-upload
 * and replace the one being sent.
 */
.calc-drop.is-uploading {
    pointer-events: none;
    cursor: progress;
    border-style: solid;
    border-color: color-mix(in srgb, var(--calc-primary) 55%, var(--calc-border));
    background: color-mix(in srgb, var(--calc-primary) 6%, var(--calc-input-bg));
}

/* The cloud icon becomes a spinner, so the wait has something moving in it. */
.calc-drop.is-uploading .calc-drop__icon i { display: none; }

.calc-drop.is-uploading .calc-drop__icon::after {
    content: "";
    display: block;
    width: 18px;
    height: 18px;
    border: 2px solid color-mix(in srgb, var(--calc-primary) 30%, transparent);
    border-top-color: var(--calc-primary);
    border-radius: 50%;
    animation: calc-upload-spin .7s linear infinite;
}

@keyframes calc-upload-spin { to { transform: rotate(360deg); } }

.calc-drop.is-uploading .calc-drop__browse {
    opacity: .6;
    cursor: progress;
}

/* A spinner that never stops is worse than none for anybody who needs stillness;
   the border alone still shows the control is busy. */
@media (prefers-reduced-motion: reduce) {
    .calc-drop.is-uploading .calc-drop__icon::after { animation: none; }
}

/* ---------------------------------------------------------------------------
   Rich text
   ---------------------------------------------------------------------------
   Prose the owner formatted: instructions, terms, a long note. It inherits the
   calculator's colours rather than carrying its own, so a page of terms reads
   as part of the calculator on a Light, Dark or Black theme alike.
   ------------------------------------------------------------------------- */

.calc-rich {
    --calc-rich-border:  var(--calc-border);
    --calc-rich-bg:      var(--calc-surface);
    --calc-rich-radius:  8px;
    --calc-rich-padding: 16px;

    color: var(--calc-text);
    line-height: 1.6;
}

.calc-rich--card {
    border: 1px solid var(--calc-rich-border);
    background: var(--calc-rich-bg);
    border-radius: var(--calc-rich-radius);
    padding: var(--calc-rich-padding);
}

/*
 * A page of terms above a Calculate button pushes the button off the screen.
 * Scrolling keeps the calculator the length it was - at the cost of hiding
 * text, which is why it is the owner's choice and not the default.
 */
.calc-rich--scroll {
    max-height: var(--calc-rich-height, 260px);
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* The first and last lines sit flush, so a card's padding is not doubled. */
.calc-rich > :first-child { margin-top: 0; }
.calc-rich > :last-child  { margin-bottom: 0; }

.calc-rich p { margin: 0 0 0.85em; }

.calc-rich h1, .calc-rich h2, .calc-rich h3,
.calc-rich h4, .calc-rich h5, .calc-rich h6 {
    color: var(--calc-heading);
    margin: 1.2em 0 0.5em;
    line-height: 1.3;
}

.calc-rich ul, .calc-rich ol { margin: 0 0 0.85em; padding-left: 1.4em; }
.calc-rich li { margin-bottom: 0.3em; }

.calc-rich a { color: var(--calc-primary); }

.calc-rich img { max-width: 100%; height: auto; border-radius: 4px; }

.calc-rich blockquote {
    margin: 0 0 0.85em;
    padding-left: 12px;
    border-left: 3px solid var(--calc-border);
    color: var(--calc-muted-text);
}

.calc-rich code, .calc-rich pre {
    font-family: ui-monospace, Menlo, Consolas, monospace;
    font-size: 0.92em;
}

.calc-rich pre {
    background: var(--calc-surface);
    padding: 10px 12px;
    border-radius: 6px;
    overflow-x: auto;
}

.calc-rich table { width: 100%; border-collapse: collapse; margin: 0 0 0.85em; }

.calc-rich th, .calc-rich td {
    border: 1px solid var(--calc-border);
    padding: 7px 9px;
    text-align: left;
}

.calc-rich hr { border: 0; border-top: 1px solid var(--calc-border); margin: 1.2em 0; }

/* ---------------------------------------------------------------------------
 * A dropdown that opens upwards
 *
 * Set by placePanel() in assets/js/calculator.js when there is not room below
 * the control. A calculator is usually in an iframe sized to its own content,
 * so a panel opening from the last field on the form has nothing underneath to
 * open into - it is drawn past the bottom edge and cannot be reached.
 *
 * The 6px matches the gap the downward rules leave, so the panel sits the same
 * distance from its control whichever way it goes.
 * ------------------------------------------------------------------------- */

.calc-multiselect__panel.is-above,
.calc-picker__panel.is-above {
    top: auto;
    bottom: calc(100% + 6px);
}

/* The lift belongs on the side the panel is actually on. */
.calc-multiselect__panel.is-above,
.calc-picker__panel.is-above {
    box-shadow: 0 -12px 28px rgba(15, 23, 42, .13);
}

/* ---------------------------------------------------------------------------
 * Date fields
 *
 * A text box written in the calculator's own format, with a hidden ISO date
 * beside it and a calendar of our own. <input type="date"> draws itself in the
 * visitor's locale and cannot be told otherwise, which made the Date format
 * setting meaningless on the one field that is entirely about dates.
 * ------------------------------------------------------------------------- */

.calc-datefield { position: relative; width: 100%; }

.calc-datefield .calc-date { cursor: pointer; }

/*
 * Inside an affix wrapper, the date field stands where the input used to.
 *
 * .calc-field gives the box its border and expects its control to be a direct
 * child - that is how ".calc-field > .calc-input" strips the input's own frame
 * so the two read as one field. A date field puts a div in between, so those
 * rules stopped matching: the input kept its border and drew a second box
 * inside the first, and the div, having no flex of its own, shrank to the
 * width of the text instead of filling the field.
 *
 * These say the same two things about the wrapper and the input inside it.
 */
.calc-field > .calc-datefield { flex: 1 1 auto; min-width: 0; }

.calc-field > .calc-datefield > .calc-input {
    border: 0;
    background: transparent;
    border-radius: 0;
    padding-left: 0;
    padding-right: 12px;
}

.calc-field > .calc-datefield > .calc-input:focus { box-shadow: none; }

/* The icon is a button in all but name, so it says so under the pointer. */
.calc-field:has(> .calc-datefield) .calc-field__affix { cursor: pointer; }

/*
 * The calendar. Positioned against the field, and flipped above it by
 * placePanel() when there is not room below - the same rule the dropdowns
 * follow, and for the same reason: an embedded calculator has an edge.
 */
.calc-datecal {
    position: absolute;
    z-index: 22;
    left: 0;
    top: calc(100% + 6px);
    width: 260px;
    max-width: 100%;
    padding: 10px;
    background: var(--calc-input-bg);
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: var(--calc-border-radius);
    box-shadow: 0 12px 28px rgba(15, 23, 42, .13);
}

.calc-datecal.is-above {
    top: auto;
    bottom: calc(100% + 6px);
    box-shadow: 0 -12px 28px rgba(15, 23, 42, .13);
}

.calc-datecal__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 8px;
}

.calc-datecal__month { font-weight: 600; font-size: 0.95em; }

.calc-datecal__nav {
    width: 28px; height: 28px;
    display: grid; place-items: center;
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: var(--calc-border-radius);
    background: var(--calc-input-bg);
    color: inherit;
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
}

.calc-datecal__nav:hover { border-color: var(--calc-primary); color: var(--calc-primary); }

/* Seven columns, so the weekday headings sit over their own days. */
.calc-datecal__grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
}

.calc-datecal__weekday {
    display: grid;
    place-items: center;
    height: 26px;
    font-size: 0.72em;
    font-weight: 600;
    opacity: .6;
    text-transform: uppercase;
}

.calc-datecal__day {
    height: 30px;
    border: 0;
    border-radius: var(--calc-border-radius);
    background: none;
    color: inherit;
    font-size: 0.86em;
    cursor: pointer;
}

.calc-datecal__day:hover:not(:disabled) { background: var(--calc-surface); }

/* Out of the field's allowed range - shown, so the shape of the month is
   still readable, but plainly not a choice. */
.calc-datecal__day:disabled { opacity: .3; cursor: default; }

.calc-datecal__day.is-today { box-shadow: inset 0 0 0 1px var(--calc-primary); }

.calc-datecal__day.is-chosen {
    background: var(--calc-primary);
    color: #fff;
    font-weight: 600;
}

.calc-datecal__foot {
    display: flex;
    gap: 6px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--calc-border);
}

.calc-datecal__foot button {
    flex: 1;
    height: 28px;
    border: var(--calc-input-border-width) solid var(--calc-input-border);
    border-radius: var(--calc-border-radius);
    background: var(--calc-input-bg);
    color: inherit;
    font-size: 0.8em;
    cursor: pointer;
}

.calc-datecal__foot button:hover:not(:disabled) {
    border-color: var(--calc-primary);
    color: var(--calc-primary);
}

.calc-datecal__foot button:disabled { opacity: .4; cursor: default; }

/*
 * The distance widget, when the owner has made it visible.
 *
 * It is hidden by default - a value the calculator works out, not a question -
 * but a delivery quote very often wants to SAY "14.2 miles, about 23 minutes"
 * rather than only charge for it.
 */
.calc-distance-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 15px;
}

.calc-distance-table th,
.calc-distance-table td {
    padding: 9px 0;
    text-align: left;
    border-bottom: 1px solid var(--calc-border);
}

.calc-distance-table tr:last-child th,
.calc-distance-table tr:last-child td { border-bottom: 0; }

.calc-distance-table th {
    font-weight: 500;
    color: var(--calc-muted-text);
}

.calc-distance-table td {
    text-align: right;
    font-weight: 600;
    color: var(--calc-text);
    font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------------
   A picture used as an icon

   Sized from the text around it rather than in pixels, so it matches a glyph
   in the same position: an icon beside a button label should grow with the
   label, which is what "1em" does and what a fixed 16px does not.
   --------------------------------------------------------------------------- */
.calc-icon-img {
    width: 1.15em;
    height: 1.15em;
    object-fit: contain;
    vertical-align: -0.15em;
    display: inline-block;
}

/* ---------------------------------------------------------------------------
   Payment form

   The layout from the brief: a tick, an optional picture, the name, and the
   amount hard against the right. A rule, then the total in the same column as
   the amounts above it, so the eye reads straight down the figures.
   --------------------------------------------------------------------------- */

.calc-pay { display: block; }

.calc-pay__row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 2px;
}

.calc-pay__row + .calc-pay__row { border-top: 1px solid var(--calc-border); }

.calc-pay__tick {
    flex: 0 0 auto;
    width: 16px;
    height: 16px;
    margin: 0;
    accent-color: var(--calc-primary);
}

/* A locked line is disabled, which browsers grey out. It is not unavailable -
   it is compulsory - so it keeps its normal weight and only loses the pointer. */
.calc-pay__row--locked .calc-pay__tick { opacity: 1; cursor: not-allowed; }
.calc-pay__row--locked .calc-pay__name { cursor: default; }

.calc-pay__icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    font-size: 26px;
    line-height: 1;
    color: var(--calc-primary);
}

.calc-pay__icon .calc-icon-img { width: 30px; height: 30px; }

.calc-pay__name {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    font-weight: 600;
    cursor: pointer;
}

.calc-pay__amount {
    flex: 0 0 auto;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.calc-pay__total {
    display: flex;
    align-items: baseline;
    justify-content: flex-end;
    gap: 14px;
    margin-top: 4px;
    padding: 14px 2px 4px;
    border-top: 1px solid var(--calc-border);
}

.calc-pay__totallabel { font-weight: 700; }

.calc-pay__totalvalue {
    font-size: 1.15em;
    font-variant-numeric: tabular-nums;
    color: var(--calc-primary);
    white-space: nowrap;
}

.calc-pay__empty {
    padding: 14px 2px;
    color: var(--calc-muted);
    font-size: 0.95em;
}

/* --- the buttons ---------------------------------------------------------- */

/*
 * Equal widths, centred as a group.
 *
 * Left to their natural sizes the four buttons are four different widths -
 * "Pay now" is a third of "Checkout with stripe" - which reads as four
 * unrelated things rather than one row of choices. Growing from a shared
 * basis makes every button on a line the same width, and the cap stops a
 * single button stretching the whole width of the form.
 */
.calc-pay__buttons {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
    margin-top: 16px;
}

/*
 * Shaped like the buttons these services actually ship: the wordmark on the
 * left, the amount on a panel to the right of it. A payment button that does
 * not say what it will charge asks somebody to leave the page to find out.
 *
 * The colours arrive as custom properties rather than as background and color,
 * because :hover cannot be written inline and this rule needs to reach the
 * same values the element was given.
 */
.calc-pay__btn {
    display: flex;
    /*
     * Two to a row, whatever the width. Half, less half the gap between them.
     * Four across is four buttons too narrow to read the wordmark on, and one
     * across wastes the room; two is the arrangement that holds
     * "Checkout with stripe" and its amount without either being squeezed.
     *
     * The cap is the owner's, from the Design tab, and 300px unless they say
     * otherwise - past that a payment button starts to look like a banner.
     */
    flex: 1 1 calc(50% - 5px);
    max-width: min(calc(50% - 5px), var(--pay-btn-max, 300px));
    min-width: 0;
    align-items: stretch;
    overflow: hidden;
    border: 0;
    border-radius: 8px;
    font-size: 0.95em;
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
    background: var(--pay-bg, var(--calc-primary));
    color: var(--pay-fg, var(--calc-primary-button-text, #fff));
}

.calc-pay__mark {
    display: inline-flex;
    flex: 1 1 auto;
    min-width: 0;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 14px 20px;
    font-weight: 600;
    white-space: nowrap;
    /*
     * If it ever will not fit, it trails off rather than being cut through a
     * letter. "Checkout with stripe" was arriving as "neckout with stripe",
     * which reads as a rendering fault rather than as a button too narrow.
     */
    overflow: hidden;
    text-overflow: ellipsis;
}

.calc-pay__mark b { font-weight: 800; }

/* Stripe sets its own name apart from the sentence around it. */
.calc-pay__stripe { font-weight: 800; letter-spacing: -0.01em; }

/*
 * The PayPal wordmark, which IS the button - there is no wording of ours on
 * it. Two blues, italic and tight, as the mark is drawn.
 */
.calc-pay__mark--paypal {
    font-style: italic;
    font-weight: 800;
    letter-spacing: -0.02em;
    font-size: 1.12em;
    gap: 0;
}

.calc-pay__pp1 { color: #003087; }
.calc-pay__pp2 { color: #009CDE; }

/*
 * The amount panel. A tint of the button rather than a colour of its own, so
 * it works on the yellow, the black and the indigo without three rules.
 */
.calc-pay__btnamount {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    /* A floor rather than a fixed width, so a long amount is never clipped
       and short ones still line up down the column. */
    min-width: 104px;
    padding: 14px 18px;
    background: var(--pay-accent, rgba(255, 255, 255, 0.16));
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.calc-pay__btn:hover { background: var(--pay-hover, var(--pay-bg, var(--calc-primary))); }

/*
 * Only when no hover colour was given. Darkening a colour the owner chose FOR
 * hover would move it somewhere they did not pick.
 */
.calc-pay__btn:not([style*="--pay-hover"]):hover { filter: brightness(0.93); }
.calc-pay__btn:active { filter: brightness(0.87); }

.calc-pay__btn .calc-icon-img { width: 1.1em; height: 1.1em; }

/* --- what comes back after paying ----------------------------------------- */

.calc-pay__ask { margin-top: 16px; }
.calc-pay__ask .calc-label { display: block; margin-bottom: 6px; }

/*
 * One per row, when that is what was asked for.
 *
 * A column, not a wrapping row with a 100% basis. Wrapping is decided on each
 * item's hypothetical size AFTER max-width has clamped it, so two buttons
 * capped at 300px still fitted comfortably on one line and "One per row" did
 * nothing at all. A column cannot put two things side by side.
 */
.calc-pay__buttons--stacked {
    flex-direction: column;
    align-items: center;
}

.calc-pay__buttons--stacked .calc-pay__btn {
    width: 100%;
    max-width: var(--pay-btn-max, 300px);
}

/*
 * Narrow screens: one per line and a size down.
 *
 * Two halves of a phone's width cannot hold "Checkout with stripe" and an
 * amount, and the label was being cut through a letter. Full width and a
 * slightly smaller face fits both without an ellipsis; the padding comes in
 * with it so the button does not become mostly air.
 */
@media (max-width: 560px) {
    .calc-pay__btn {
        flex: 1 1 100%;
        max-width: none;
        font-size: 0.88em;
    }

    .calc-pay__mark { padding: 13px 12px; }

    .calc-pay__btnamount {
        min-width: 88px;
        padding: 13px 12px;
    }
}

/*
 * Smaller still. At this width the wordmark and the amount are competing for
 * room that is not there, so the amount panel gives up its floor and takes
 * only what it needs.
 */
@media (max-width: 380px) {
    .calc-pay__btn { font-size: 0.82em; }
    .calc-pay__mark { padding: 12px 10px; }
    .calc-pay__btnamount { min-width: 0; padding: 12px 10px; }
}

/* ---------------------------------------------------------------------------
   Share these results

   Sits at the foot of the calculator, below everything including a multi-step
   calculator's steps - it belongs to the calculator rather than to any one
   step of it.
   --------------------------------------------------------------------------- */

.calc-share {
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px solid var(--calc-border);
}

.calc-share__btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 18px;
    border: 1px solid var(--calc-border);
    border-radius: var(--calc-radius, 8px);
    background: var(--calc-surface, transparent);
    color: var(--calc-text);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}

.calc-share__btn:hover { border-color: var(--calc-primary); color: var(--calc-primary); }

.calc-share__out { margin-top: 10px; }

/* Readable at a glance and selectable by hand, because copying can be refused
   by the browser and there has to be something to fall back to. */
.calc-share__url {
    width: 100%;
    font-size: 0.9em;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

.calc-share__said {
    display: block;
    margin-top: 6px;
    font-size: 0.85em;
    color: var(--calc-muted);
}
