/*
 * ispweb.css — additions on top of the compiled design stylesheet (site.css).
 *
 * site.css is the original build output and is left untouched, so the site
 * still looks exactly as it did. Everything here is either:
 *   - styling for markup the block system introduced (pricing cards, the
 *     for-sale list, the myip definition list, status severity)
 *   - accessibility and responsive fixes that the original layout lacked
 *
 * Keeping the two files apart means the design build can be regenerated from
 * resources/scss without losing any of this.
 */

/* --------------------------------------------------------------------------
   Tokens, mirrored from resources/scss/site/variables/_colors.scss
   -------------------------------------------------------------------------- */

:root {
    --ispweb-primary: #02437d;
    --ispweb-secondary: #0078be;
    --ispweb-tertiary: #a0c92d;
    --ispweb-light-blue: #f4fbff;
    --ispweb-font: #131313;
    --ispweb-muted: #5b6470;
    --ispweb-warning: #b8860b;
    --ispweb-danger: #c0392b;
    --ispweb-radius: 4px;
}

/* --------------------------------------------------------------------------
   Accessibility
   -------------------------------------------------------------------------- */

/* Visible only to screen readers. Bootstrap ships this, but site.css predates
   the utility and several blocks depend on it. */
.visually-hidden {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
}

/* The original markup used <div> for the hamburger, so it could not be reached
   by keyboard. It is a <button> now; this strips the default chrome. */
button.btn-mobile-menu {
    padding: 0;
    background: none;
    border: 0;
    cursor: pointer;
}

/* A focus ring that is actually visible against the dark blue. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 3px solid var(--ispweb-tertiary);
    outline-offset: 2px;
    border-radius: 2px;
}

.footer-section a:focus-visible,
.copyright-section a:focus-visible,
.bg-primary a:focus-visible {
    outline-color: #fff;
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* --------------------------------------------------------------------------
   Text-with-image section
   -------------------------------------------------------------------------- */

/*
 * Room for the two blue decorations.
 *
 * .about-section .bg-block::after sits at top:-70px with height:140%, and
 * .about-section figure::after at bottom:-4rem — both deliberately reach past
 * the article. The bg-block overhang is 0.4 × article height − 70px, so it
 * grows with the column width; hence a larger reserve at the wider breakpoints.
 *
 * The original page made this room with an empty spacer section between this
 * one and the next — markup that looked dead but was carrying the layout.
 * Removing it let the decoration run over whatever followed.
 *
 * Reserving the space on the section itself keeps the block self-contained, so
 * it can be placed anywhere in a page without bleeding into the next one.
 *
 * Below 992px the bg-block decoration is display:none and only the smaller
 * figure overhang remains.
 */
@media (min-width: 992px) {
    .about-section {
        padding-bottom: 6rem;
    }
}

@media (min-width: 1400px) {
    .about-section {
        padding-bottom: 8rem;
    }
}

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

/* The original cards were different heights within a row whenever a title
   wrapped. Flex column with the link pushed to the bottom evens them out. */
.product-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    border-radius: var(--ispweb-radius);
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.product-card .h3 {
    margin: 0.75rem 0 0.5rem;
}

.product-card-link {
    margin-top: auto;
    color: #fff;
    font-weight: 700;
}

.product-card:hover,
.product-card:focus-within {
    transform: translateY(-2px);
    box-shadow: 0 8px 24px rgba(2, 67, 125, 0.22);
}

.product-index {
    padding-left: 1.1rem;
}

.product-index li {
    margin-bottom: 0.35rem;
}

/* --------------------------------------------------------------------------
   Pricing cards
   -------------------------------------------------------------------------- */

.pricing-card {
    border-radius: var(--ispweb-radius);
    /* The banded bar, as on the product and news cards. Declared here rather
       than beside them because the accent tokens are defined further down and
       a custom property resolves at use, not at parse. */
    position: relative;
    overflow: hidden;
}

.pricing-card::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 4px;
    background: var(--ispweb-accent-bar);
}

.pricing-card .title {
    font-size: 1.15rem;
}

.pricing-features {
    margin: 0.9rem 0 1rem;
    padding-left: 1.1rem;
}

.pricing-features li {
    margin-bottom: 0.3rem;
}

.pricing-price {
    padding-top: 0.75rem;
    border-top: 1px solid rgba(255, 255, 255, 0.25);
}

.pricing-price .price {
    font-size: 1.35rem;
    font-weight: 700;
}

/* --------------------------------------------------------------------------
   Status
   -------------------------------------------------------------------------- */

.status-line {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.75rem;
    font-weight: 700;
}

.status-line::before {
    content: "";
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 50%;
    flex: 0 0 auto;
}

/* Severity: ok, then low / medium / high. */
.status-line-ok::before { background: var(--ispweb-tertiary); }
.status-line-low::before { background: var(--ispweb-accent-yellow); }
.status-line-medium::before { background: var(--ispweb-warning); }
.status-line-high::before { background: var(--ispweb-danger); }

.status-line-low { color: var(--ispweb-warning); }
.status-line-medium { color: var(--ispweb-warning); }
.status-line-high { color: var(--ispweb-danger); }

/* --------------------------------------------------------------------------
   Domain check
   -------------------------------------------------------------------------- */

/* Replaces the inline styles the original put on the readonly WWW. input. */
.domain-check-prefix {
    display: flex;
    align-items: center;
    background: var(--ispweb-primary);
    color: #f5f5f5;
    border: 0;
    font-weight: 700;
}

.domain-check-form .input-group {
    flex-wrap: nowrap;
}

/* Below the small breakpoint the three-part input group does not fit on one
   line; stack it rather than letting the field shrink to nothing. */
@media (max-width: 575.98px) {
    .domain-check-form .input-group {
        flex-wrap: wrap;
    }

    .domain-check-form .domain-check-prefix {
        display: none;
    }

    .domain-check-form .form-control,
    .domain-check-form .btn {
        flex: 1 1 100%;
        border-radius: var(--ispweb-radius) !important;
    }

    .domain-check-form .btn {
        margin-top: 0.5rem !important;
    }
}

/* --------------------------------------------------------------------------
   MyIP
   -------------------------------------------------------------------------- */

.myip-details {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.4rem 1.25rem;
    margin: 0;
}

.myip-details dt {
    font-weight: 700;
    color: var(--ispweb-primary);
}

.myip-details dd {
    margin: 0;
    word-break: break-word;
}

/* User agents are long and arbitrary; never let one widen the page. */
.myip-agent {
    overflow-wrap: anywhere;
    font-size: 0.9rem;
    color: var(--ispweb-muted);
}

@media (max-width: 575.98px) {
    .myip-details {
        grid-template-columns: 1fr;
        gap: 0 0;
    }

    .myip-details dd {
        margin-bottom: 0.75rem;
    }
}

/* --------------------------------------------------------------------------
   For-sale domain list
   -------------------------------------------------------------------------- */

/* The original built two columns with PHP and a hard-coded row count, which
   broke whenever the list length changed. CSS columns handle any length. */
.forsale-list {
    columns: 2;
    column-gap: 2rem;
    padding: 0;
    margin: 0;
    list-style: none;
}

.forsale-list li {
    break-inside: avoid;
    margin-bottom: 0.3rem;
}

.forsale-list a {
    text-decoration: none;
    transition: color 0.12s ease;
}

.forsale-list a:hover,
.forsale-list a:focus {
    color: var(--ispweb-tertiary);
}

@media (max-width: 575.98px) {
    .forsale-list {
        columns: 1;
    }
}

/* The original pulled this image up 16rem with a negative margin, which put it
   behind the text on narrow desktops. */
.forsale-image {
    opacity: 0.3;
    pointer-events: none;
}

/* --------------------------------------------------------------------------
   Partner banner
   --------------------------------------------------------------------------
   A continuous marquee, in CSS alone. The logo list is emitted twice and the
   strip is moved by exactly half its width, so the end meets the beginning.

   The spacing is a right margin on each logo, not `gap` on the track, and that
   detail is the whole trick. With `gap`, N logos duplicated to 2N produce
   2N-1 gaps, so half the track width is half a gap short of one real cycle --
   the strip jumps back a fraction every lap, which reads as a stutter and can
   leave the last logo never fully in view. A margin makes each logo a
   self-contained unit of (logo + margin), so N units are exactly half of 2N and
   -50% lands precisely on the copy.

   No JavaScript is involved: the Splide carousel this replaced left an empty
   band whenever its bundle failed to mount.
   -------------------------------------------------------------------------- */

.partner-band {
    --partner-gap: clamp(2rem, 6vw, 5.5rem);

    /* Bleeds past the container so logos enter and leave off-screen rather
       than appearing at a visible edge. */
    margin-inline: calc(50% - 50vw);
    padding-inline: calc(50vw - 50%);
    overflow: hidden;
}

.partner-track {
    display: flex;
    align-items: center;
    margin: 0;
    padding: 0;
    list-style: none;
    justify-content: center;
    flex-wrap: wrap;
}

.partner-item {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    margin-right: var(--partner-gap);
}

.partner-item img {
    display: block;
    /* A definite height, not just a maximum: an SVG whose intrinsic size cannot
       be read has no ratio to scale from, and width:auto against a bare
       max-height collapses it to nothing. Sizing from the height gives every
       logo something to work with, whatever the format. */
    height: clamp(2rem, 4vw, 3.25rem);
    width: auto;
    max-width: clamp(7rem, 12vw, 11rem);
    object-fit: contain;
}

/* Scrolling variant ------------------------------------------------------- */

.partner-band.is-scrolling .partner-track {
    /* One long line: no wrapping, no centring, or it cannot slide. */
    flex-wrap: nowrap;
    justify-content: flex-start;
    width: max-content;
    animation: partner-marquee var(--partner-speed, 40s) linear infinite;
    /* Its own layer, so the browser does not repaint the section every frame. */
    will-change: transform;
}

@keyframes partner-marquee {
    from { transform: translate3d(0, 0, 0); }
    /* Half the strip is the duplicate, so this lands exactly on the copy. */
    to   { transform: translate3d(-50%, 0, 0); }
}

/* Standing still, the trailing margin on the last logo would push the row off
   centre; take it back. */
.partner-band:not(.is-scrolling) .partner-track {
    margin-right: calc(-1 * var(--partner-gap));
}

/* Motion is decoration here; without it the band simply stands still. */
@media (prefers-reduced-motion: reduce) {
    .partner-band.is-scrolling .partner-track {
        animation: none;
        flex-wrap: wrap;
        justify-content: center;
        width: auto;
        margin-right: calc(-1 * var(--partner-gap));
    }

    .partner-band.is-scrolling .partner-item[aria-hidden="true"] {
        display: none;
    }
}
/* --------------------------------------------------------------------------
   News
   -------------------------------------------------------------------------- */

.news-empty {
    color: var(--ispweb-muted);
}

/*
 * Fallback for a carousel that never got initialised.
 *
 * Splide adds `is-initialized` when it mounts. Until then .splide__track is
 * overflow:hidden around a flex list whose slides have no width, so the whole
 * thing collapses to nothing -- which is exactly how the partner logos
 * disappeared without a trace. This keeps an unmounted carousel usable as a
 * plain horizontally scrollable row instead of an empty gap.
 */
.splide:not(.is-initialized) .splide__track {
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
}

.splide:not(.is-initialized) .splide__list {
    display: flex;
    gap: 1.5rem;
}

.splide:not(.is-initialized) .splide__slide {
    flex: 0 0 auto;
    width: min(300px, 80vw);
    scroll-snap-align: start;
}

/* The arrows do nothing until Splide wires them up. */
.splide:not(.is-initialized) .splide__arrows {
    display: none;
}

/* Fixes the fixed 300px inline sizing the original put on every thumbnail. */
.news-thumb img {
    width: 100%;
    max-width: 300px;
    height: auto;
    aspect-ratio: 1 / 1;
    object-fit: cover;
}

.news-section .date {
    color: var(--ispweb-muted);
    font-size: 0.9rem;
}

/* --------------------------------------------------------------------------
   Long-form content pages
   -------------------------------------------------------------------------- */

/* The policy and product pages are a wall of text; these give them a readable
   measure and consistent rhythm without touching the rest of the design. */
/* The body sits in its own .block-body wrapper -- that is what carries the
   optional newspaper columns -- so the rhythm rule has to reach inside it as
   well as apply to the article itself. */
.longform > * + *,
.longform .block-body > * + * {
    margin-top: 0.9rem;
}

.longform h6 {
    margin-top: 1.6rem;
    color: var(--ispweb-primary);
    font-weight: 700;
}

.longform ul,
.longform ol {
    padding-left: 1.25rem;
}

.longform li + li {
    margin-top: 0.4rem;
}

.longform.col-12 {
    max-width: 72ch;
}

/* The policy pages are a numbered list of sections: the list item's own text is
   the section title and everything nested inside it is that section. Left at
   the default the titles read as smaller than the bold leads underneath them,
   which put the visual hierarchy upside down. */
.longform ol > li {
    margin-top: 1.75rem;
    color: var(--ispweb-primary);
    font-weight: 700;
    font-size: 1.0625rem;
}

.longform ol > li:first-child {
    margin-top: 1rem;
}

/* Back to body text for the section's own content. */
.longform ol > li > p,
.longform ol > li > ul,
.longform ol > li > ol {
    color: var(--ispweb-font);
    font-weight: 400;
    font-size: 1rem;
}

.longform ol > li > ul {
    margin-top: 0.6rem;
}

/* Long domain names and URLs must not push the layout sideways. */
.longform a {
    overflow-wrap: anywhere;
}

/* The list of downloadable terms. Seventeen PDF file names in a single column
   is a wall; they are short and independent, so let them flow into as many
   columns as the width allows. */
.longform .download-list {
    margin-top: 0.75rem;
    padding-left: 0;
    list-style: none;
    /* Wide enough for the longest file name to stay on one line. Narrower
       columns fit more per row but split names mid-word, and a broken
       "…programmatuur.p / df" is harder to read than a shorter list. */
    columns: 30rem auto;
    column-gap: 2.5rem;
}

.longform .download-list li {
    margin: 0 0 0.35rem;
    break-inside: avoid;
}

.longform .download-list a {
    display: inline-block;
    padding-left: 1.4rem;
    position: relative;
}

/* A document mark in place of the old literal ■ character. */
.longform .download-list a::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.15em;
    width: 0.75em;
    height: 0.95em;
    border: 1px solid currentColor;
    border-radius: 1px;
}

/* --------------------------------------------------------------------------
   Global responsive guards
   -------------------------------------------------------------------------- */

/*
 * Pulls the illustration on a content page up beside the heading, the way the
 * homepage hero sits.
 *
 * The original declared this in an inline <style> block on every product page.
 * Removing inline styles took the declaration with it, so the class stayed in
 * the markup while meaning nothing -- and the image dropped to the bottom of
 * the text instead of sitting alongside it.
 *
 * Desktop only: below 1200px there is one column and pulling up would drag the
 * image over the text.
 */
@media (min-width: 1200px) {
    .mt-xl-n16 {
        margin-top: -16rem !important;
    }
}

@media (max-width: 1199.98px) {
    .mt-xl-n16 {
        margin-top: 0 !important;
    }
}

img {
    max-width: 100%;
    height: auto;
}

/* Nothing may cause a horizontal scrollbar. */
html,
body {
    overflow-x: hidden;
}

/* Give the sticky header something to scroll to when following an anchor. */
:target {
    scroll-margin-top: 6rem;
}

/* --------------------------------------------------------------------------
   Page layout: content with an illustration beside it
   --------------------------------------------------------------------------
   Used by product pages: the blocks flow down the page and a single tall image
   sits beside them.

   The illustration is floated rather than placed in a grid column, so a block
   sits beside it while there is room and takes the whole width once it has
   ended. A column would have been as tall as the row, keeping every block at
   half width for the whole page and leaving a large empty area beside the ones
   that start below the artwork.

   For that to work each block has to be its own block formatting context:
   ordinary block boxes slide *under* a float and only their text wraps around
   it, which would put a card grid behind the illustration. A flow-root box is
   placed beside the float instead, narrowed to the space left over, and moves
   below it when too little remains.

   The section reuses the homepage hero's classes -- .intro-section and
   .image-right -- so the artwork's size and position come from site.css and
   match the homepage by construction rather than by a second set of numbers
   kept in step by hand. What is left below is only what differs.
   -------------------------------------------------------------------------- */

@media (min-width: 992px) {
    .page-aside-figure {
        float: right;
        width: 50%;
        text-align: right;
    }

    .page-aside-figure.is-left {
        float: left;
        text-align: left;
    }

    /* Everything except plain prose. A flow-root box is placed beside the float
       and stays that narrow for its whole height; prose is left as an ordinary
       block so its lines wrap around the illustration and widen out underneath
       it, which is how the policy pages read. */
    .page-aside .block-part:not(.is-prose) {
        display: flow-root;
    }

    /* A list's markers sit outside its content box, which for a wrapping block
       means they can end up underneath the illustration. Moving them inside
       keeps every number and bullet visible beside it. */
    .page-aside .block-part.is-prose ul,
    .page-aside .block-part.is-prose ol {
        list-style-position: inside;
        padding-left: 0;
    }

    .page-aside .block-part.is-prose li > ul,
    .page-aside .block-part.is-prose li > ol {
        padding-left: 1.25rem;
    }

    /* "Volle breedte": always start below the illustration. */
    .page-aside .block-part.is-full {
        clear: both;
    }
}

/* On a phone there are no two columns. The float is declared first because a
   float has to be, but the illustration belongs under the opening text there,
   the way the hero's does -- so the container becomes a flex column and the
   figure is ordered between the first block and the rest. Ordering it simply
   last would drop the artwork below the whole page. */
@media (max-width: 991.98px) {
    .page-aside > .container {
        display: flex;
        flex-direction: column;
    }

    /* The first selector has to outweigh the catch-all below it, or the two tie
       on specificity and the later rule sends every block to order 3 -- which
       put the illustration above the heading. */
    .page-aside .page-aside-figure + .block-part {
        order: 1;
    }

    .page-aside .page-aside-figure {
        order: 2;
    }

    .page-aside .block-part {
        order: 3;
    }
}

/* Where the heading starts.

   The homepage has no number for this: its heading lands wherever centring puts
   it, which is half the illustration's height minus half the text block's. The
   illustration scales with the viewport, so the position slides continuously --
   measured on the homepage: 221px at 1024 wide, 271 at 1200, 168 at 1320, 219
   at 1440, 264 at 1600, 355 at 1920, 372 at 2200 (there the artwork hits its
   natural 1101px and stops growing).

   A product page's content is several screens long, so it cannot be centred.
   These reproduce that curve instead, one segment per band of site.css's own
   .col-text offset. Verified against the homepage at each width above; worst
   case is 12px out, most within 10px. It sits on the first block, the one that
   starts level with the illustration. */
@media (min-width: 992px) and (max-width: 1300px) {
    .page-aside .page-aside-figure + .block-part {
        padding-top: calc(33.2vw - 110px);
    }
}

@media (min-width: 1301px) and (max-width: 1420px) {
    .page-aside .page-aside-figure + .block-part {
        padding-top: calc(27.5vw - 186px);
    }
}

@media (min-width: 1421px) {
    /* The clamp matches the artwork: it stops growing at its natural height,
       and the lower bound keeps the heading clear of the fixed header. */
    .page-aside .page-aside-figure + .block-part {
        padding-top: clamp(200px, calc(27.6vw - 169px), 372px);
    }
}

/* A page with no illustration has nothing to line up with, but still has to
   clear the fixed header. */
.page-aside.has-no-figure > .container > .block-part:first-child {
    padding-top: 7rem;
}

@media (min-width: 992px) {
    .page-aside.has-no-figure > .container > .block-part:first-child {
        padding-top: 11rem;
    }
}

/* No position:sticky on the illustration.
   It looked like a free improvement -- the artwork keeping pace with a long
   column -- but a sticky element with `top: 6rem` is already pushed down by
   those 6rem at scroll position 0, because its natural place is above the
   threshold. Measured: the illustration started 96px lower than the homepage's,
   which is precisely that 6rem. It starts flush with the top of the section,
   like the hero's. */

/* .col-text gets a large top offset from site.css when it sits in an
   .intro-section -- correct once, for the column at the top of the section.
   A block nested inside that column keeps the class for its typography but must
   not repeat the offset, or the heading is pushed down by it twice. */
.page-aside .block-part .col-text,
.page-aside .block-part.col-text {
    padding-top: 0;
}

/* Text in newspaper columns. Only from lg: on a phone there is no width to
   divide, and a two-column list there would be unreadable. */
@media (min-width: 992px) {
    .block-columns-2 {
        columns: 2;
    }

    .block-columns-3 {
        columns: 3;
    }

    .block-columns {
        column-gap: 3rem;
    }

    /* A list must not be cut in half across the break... */
    .block-columns > * {
        break-inside: avoid;
    }

    /* ...and a heading must not be left behind at the foot of one column while
       the list it introduces starts in the next. break-inside cannot prevent
       that: the heading and the list are separate elements, so the break falls
       between them. break-after moves the heading along with its list. */
    .block-columns h1,
    .block-columns h2,
    .block-columns h3,
    .block-columns h4,
    .block-columns h5,
    .block-columns h6 {
        break-after: avoid;
    }

    .block-columns > :first-child {
        margin-top: 0;
    }
}

/* An image inside a column block sits under its text at the column's width;
   the bleeding .image-right treatment is only for the illustration beside the
   whole page. */
.page-aside .block-figure img {
    max-width: 100%;
    height: auto;
}

/* Blocks inside the column: no band, no container, just stacked parts. */
.page-aside .block-part + .block-part {
    margin-top: 1.5rem;
}

/* The first block sets the page heading; it needs no extra lead-in. */
.page-aside .block-part:first-child {
    margin-top: 0;
}
/* --------------------------------------------------------------------------
   Block spacing
   --------------------------------------------------------------------------
   Set per block in the portal, so an editor can close a gap between two blocks
   without anyone touching a stylesheet.
   -------------------------------------------------------------------------- */

/*
 * These sit on the block's container, which is the element the design's own
 * `my-6` / `pt-6` utilities are on. They therefore have to be at least as
 * loud as those utilities -- Bootstrap's carry !important, so a plain
 * declaration here would simply lose and the setting would appear to do
 * nothing.
 *
 * `normal` deliberately has no rule at all: it means "leave the design's own
 * rhythm alone", and it is the default on every block, so anything declared
 * for it would change every page on the site.
 */
.space-top-none    { margin-top: 0 !important; padding-top: 0 !important; }
.space-top-small   { margin-top: 1.5rem !important; padding-top: 0 !important; }
.space-top-large   { margin-top: 7rem !important; }

.space-bottom-none  { margin-bottom: 0 !important; padding-bottom: 0 !important; }
.space-bottom-small { margin-bottom: 1.5rem !important; padding-bottom: 0 !important; }
.space-bottom-large { margin-bottom: 7rem !important; }

/*
 * The block's own first row carries `mt-6` as well, to sit clear of the header
 * when it is the opening band of a page. With the container's spacing zeroed
 * that margin collapses straight out of the section and reappears as the gap
 * the setting was supposed to remove -- so it goes too.
 */
.space-top-none > .row:first-child,
.space-top-small > .row:first-child {
    margin-top: 0 !important;
}

.space-bottom-none > .row:last-child,
.space-bottom-small > .row:last-child {
    margin-bottom: 0 !important;
}

/* Inside a column the same choices control the stack rhythm. */
.block-part.space-top-none    { margin-top: 0 !important; }
.block-part.space-top-small   { margin-top: .5rem; }
.block-part.space-top-large   { margin-top: 3rem; }

.block-part.space-bottom-none  { margin-bottom: 0 !important; }
.block-part.space-bottom-small { margin-bottom: .5rem; }
.block-part.space-bottom-large { margin-bottom: 3rem; }

/* ==========================================================================
   Accents
   ==========================================================================
   The house style is one blue with a green button, which over a long page
   reads as a wall of the same colour. The logo has always carried a red, a
   yellow and a green next to that blue; security.ispweb.nl puts them to work
   as a banded accent line, and this is the same set, used the same way.

   The green here is the site's own #A0C92D rather than the security site's
   #A1CE16 -- they are a shade apart, and matching the button colour already on
   the page beats matching another site's stylesheet.
   -------------------------------------------------------------------------- */

:root {
    --ispweb-accent-red: #e5051f;
    --ispweb-accent-yellow: #eed500;
    --ispweb-accent-green: #a0c92d;
    --ispweb-accent-blue: #0078be;

    /* Four equal bands, no blending: a gradient with coincident stops. A soft
       fade between them turns to mud at four pixels tall. */
    --ispweb-accent-bar: linear-gradient(
        90deg,
        var(--ispweb-accent-red) 0 25%,
        var(--ispweb-accent-yellow) 25% 50%,
        var(--ispweb-accent-green) 50% 75%,
        var(--ispweb-accent-blue) 75% 100%
    );
}

/* The banded line itself, available to any block that wants to close a section
   off, and used below on the footer and under the header. */
.accent-bar {
    display: block;
    height: 5px;
    background: var(--ispweb-accent-bar);
}

/*
 * Two footers, one sticky-footer rule.
 *
 * site.css has `body footer { margin-top: auto !important }`, written for a
 * layout with a single <footer>: in a flex column an auto top margin eats the
 * free space and pushes the footer to the bottom of a short page.
 *
 * This site emits two footers -- the contact block and the copyright bar -- and
 * an auto margin on *each* means the free space is divided between them. That
 * is where the two identical bands of white came from: one above the contact
 * block and one between it and the copyright bar, growing on exactly the pages
 * with the least content.
 *
 * Only the first footer should push. The second sits directly under it, so the
 * pair travels to the bottom as one block.
 */
.site-wrapper > footer + footer {
    margin-top: 0 !important;
}

/* The footer opens with it, so every page ends on the full palette. */
.footer-section {
    position: relative;
}

.footer-section::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 5px;
    background: var(--ispweb-accent-bar);
}

/*
 * And the header closes with a thinner one -- but only once it has shrunk.
 *
 * In its large state the header is the logo on white with nothing under it, and
 * a full-width band of colour there competes with the logo's own coloured
 * blocks and reads as a second, heavier rule. Once the page is scrolled the
 * header becomes a compact white bar over the content, and there the line is
 * doing a job: it separates the bar from whatever is passing underneath it.
 *
 * `body.scroll` is set by ispweb.js from the scroll position, and is the same
 * class site.css already uses to shrink the header. The line fades rather than
 * appears, so it arrives with the shrink instead of blinking in.
 */
.header-section::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 3px;
    background: var(--ispweb-accent-bar);
    opacity: 0;
    transition: opacity 0.2s ease;
}

body.scroll .header-section::after {
    opacity: 1;
}

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

/* --------------------------------------------------------------------------
   Section headings
   -------------------------------------------------------------------------- */

/* A short banded rule under the heading of each homepage band. Scoped to those
   sections on purpose: give every <h2> one and it stops meaning "new section"
   and starts meaning nothing. */
.products-section h2,
.news-section h2,
.status-section h2,
.domain-section h2,
.partners-section h2 {
    position: relative;
    padding-bottom: 0.6rem;
}

.products-section h2::after,
.news-section h2::after,
.status-section h2::after,
.domain-section h2::after,
.partners-section h2::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    width: 72px;
    height: 4px;
    border-radius: 2px;
    background: var(--ispweb-accent-bar);
}

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

/* Every card carries the whole banded bar, the same one as the footer.
   Giving each card the next single colour instead made the grid look like a
   sequence -- as though the cards were ranked, or as though the colour meant
   something per product. It does not: the four together are the house style,
   and repeating the full set says "ISPWeb" on every card rather than "this is
   the red one". */
.product-card {
    position: relative;
    overflow: hidden;
}

.product-card::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 4px;
    background: var(--ispweb-accent-bar);
}

/* --------------------------------------------------------------------------
   Incident accordion
   --------------------------------------------------------------------------
   One incident open at a time, newest first, resolved ones behind their own
   fold. Built on <details name="…">, so the browser closes the siblings and
   there is no JavaScript involved — it works with scripting off, and it is
   keyboard- and screen-reader-accessible without any wiring.

   Tabs were considered and rejected: incident titles are sentences rather than
   labels, the number of them changes by the hour, and a tab strip is the first
   thing to break on a phone.
   -------------------------------------------------------------------------- */

.incident-list {
    display: grid;
    gap: 0.6rem;
    margin: 1.25rem 0 0;
    padding: 0;
    list-style: none;
}

.incident {
    position: relative;
    overflow: hidden;
    background: #fff;
    border: 1px solid rgba(2, 67, 125, 0.12);
    border-radius: var(--ispweb-radius);
    box-shadow: 0 2px 10px rgba(2, 67, 125, 0.06);
}

/* The severity stripe. A left edge rather than a full border, so the panel
   still reads as one object while the colour stays unmissable. */
.incident::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 5px;
    background: var(--ispweb-accent-yellow);
    z-index: 1;
}

.incident-high::before   { background: var(--ispweb-accent-red); }
.incident-medium::before { background: var(--ispweb-warning); }
.incident-low::before    { background: var(--ispweb-accent-yellow); }
.incident-resolved::before { background: var(--ispweb-accent-green); }

/* -- the row you click ----------------------------------------------------- */

.incident-summary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 0.75rem;
    padding: 0.85rem 2.5rem 0.85rem 1.35rem;
    cursor: pointer;
    /* Both are needed to drop the default disclosure triangle: the standard
       property and the WebKit pseudo-element that predates it. */
    list-style: none;
}

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

.incident-summary:hover {
    background: var(--ispweb-light-blue);
}

.incident-summary:focus-visible {
    outline: 3px solid var(--ispweb-tertiary);
    outline-offset: -3px;
}

/* The chevron, drawn rather than an image so it can turn with the panel. */
.incident-summary::after {
    content: "";
    position: absolute;
    top: 1.35rem;
    right: 1.15rem;
    width: 0.5rem;
    height: 0.5rem;
    border-right: 2px solid var(--ispweb-secondary);
    border-bottom: 2px solid var(--ispweb-secondary);
    transform: rotate(45deg);
    transform-origin: center;
    transition: transform 0.15s ease;
}

.incident[open] > .incident-summary::after {
    transform: rotate(-135deg);
}

.incident-title {
    margin: 0;
    flex: 1 1 12rem;
    min-width: 0;
    font-size: 1rem;
    line-height: 1.35;
    color: var(--ispweb-primary);
}

/* Pushed to the right of the title, and the first thing to wrap away when the
   title needs the room. */
.incident-when {
    flex: none;
    font-size: 0.82rem;
    color: var(--ispweb-muted);
    font-variant-numeric: tabular-nums;
}

.incident-badge {
    flex: none;
    padding: 0.15rem 0.55rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #fff;
    background: var(--ispweb-muted);
    white-space: nowrap;
}

/* Yellow needs dark text: white on #eed500 is about 1.7:1. */
.incident-low .incident-badge {
    color: #3a3000;
    background: var(--ispweb-accent-yellow);
}

.incident-medium .incident-badge {
    background: var(--ispweb-warning);
}

.incident-high .incident-badge {
    background: var(--ispweb-accent-red);
}

.incident-resolved .incident-badge {
    color: #1d2a00;
    background: var(--ispweb-accent-green);
}

/* -- what unfolds ---------------------------------------------------------- */

.incident-detail {
    padding: 0 1.35rem 1.1rem;
    border-top: 1px solid rgba(2, 67, 125, 0.08);
    margin-top: -1px;
    padding-top: 0.9rem;
}

/* The full severity wording, which the collapsed row shortens to one word. */
/*
 * Urgency, impact and phase, side by side.
 *
 * Three short facts rather than one word: they are separate questions and a
 * visitor reads them as a sentence -- "hoog · merkbare hinder · wordt opgelost".
 * The separator is a border on the left of each following item, so it never
 * strands on its own when the row wraps on a phone.
 */
.incident-severity {
    margin: 0 0 0.5rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 0.6rem;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--ispweb-muted);
}

.incident-fact + .incident-fact {
    padding-left: 0.6rem;
    border-left: 1px solid rgba(148, 163, 184, 0.45);
}

.incident-fact-level {
    color: var(--ispweb-font);
}

.incident-fact-phase {
    font-weight: 600;
}

.incident-services {
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.incident-service {
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    font-size: 0.8rem;
    color: var(--ispweb-primary);
    background: var(--ispweb-light-blue);
    border: 1px solid rgba(2, 67, 125, 0.14);
}

.incident-times {
    margin: 0.55rem 0 0;
    font-size: 0.85rem;
    color: var(--ispweb-muted);
}

.incident-body {
    margin-top: 0.6rem;
    font-size: 0.94rem;
}

.incident-body > :last-child {
    margin-bottom: 0;
}

/* -- resolved, behind its own fold ----------------------------------------- */

.incident-archive {
    margin-top: 0.9rem;
}

.incident-archive > summary {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.2rem;
    cursor: pointer;
    list-style: none;
    font-size: 0.88rem;
    font-weight: 700;
    color: var(--ispweb-muted);
}

.incident-archive > summary::-webkit-details-marker {
    display: none;
}

.incident-archive > summary:hover {
    color: var(--ispweb-secondary);
}

.incident-archive > summary::before {
    content: "";
    width: 0.45rem;
    height: 0.45rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg);
    transition: transform 0.15s ease;
}

.incident-archive[open] > summary::before {
    transform: rotate(-135deg);
}

.incident-archive-count {
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
    background: rgba(2, 67, 125, 0.1);
    font-size: 0.78rem;
}

/* History, not news: present but quieter than what is still running. */
.incident-archive .incident {
    box-shadow: none;
}

@media (prefers-reduced-motion: reduce) {
    .incident-summary::after,
    .incident-archive > summary::before {
        transition: none;
    }
}

/* --------------------------------------------------------------------------
   News cards
   -------------------------------------------------------------------------- */

/* The cards themselves are styled further down, under "News carousel" -- the
   markup is a scroll-snap track rather than the Splide list this block used to
   emit. What stays here is the empty state, which is not part of the track. */

.news-empty {
    padding: 1.25rem 1.4rem;
    border-left: 4px solid var(--ispweb-accent-yellow);
    background: var(--ispweb-light-blue);
    border-radius: 0 var(--ispweb-radius) var(--ispweb-radius) 0;
    color: var(--ispweb-muted);
}

/* --------------------------------------------------------------------------
   Reduced motion
   -------------------------------------------------------------------------- */

/* The card lift is decoration. Someone who has asked their system to stop
   moving things has asked for this too. */
@media (prefers-reduced-motion: reduce) {
    .product-card {
        transition: none;
    }

    .product-card:hover,
    .product-card:focus-within {
        transform: none;
    }
}

/* --------------------------------------------------------------------------
   News carousel
   --------------------------------------------------------------------------
   A scroll-snap row rather than a mounted slider: it works with no JavaScript
   at all, which matters because the design bundle that used to mount the
   Splide carousel here cannot run under this site's CSP. See the comment at
   the top of src/Render/Blocks/news.php.
   -------------------------------------------------------------------------- */

.news-carousel {
    position: relative;
}

.news-track {
    list-style: none;
    margin: 0;
    padding: 0 0 1rem;
    display: flex;
    gap: 1.5rem;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    /* Firefox; WebKit gets the ::-webkit-scrollbar rules below. */
    scrollbar-width: thin;
    scrollbar-color: rgba(2, 67, 125, 0.35) transparent;
}

/*
 * Card width.
 *
 * Flexbox rather than grid-auto-columns: a percentage there is measured
 * against the grid's own content box, which on a track that is deliberately
 * wider than its container is not the number wanted -- five cards ended up
 * sharing the visible width instead of three sticking out past it. A flex
 * basis resolves against the container, which is exactly the intent.
 *
 * flex-shrink: 0 is what keeps them from being squeezed back in to fit; the
 * overflow is the point.
 */
.news-item {
    flex: 0 0 min(280px, 78%);
}

/* Two across on a tablet, three on a desktop. On a phone the card stops just
   short of the full width, and that peek of the next one is what tells a
   visitor the row scrolls at all. */
@media (min-width: 768px) {
    .news-item { flex-basis: calc((100% - 1.5rem) / 2); }
}

@media (min-width: 992px) {
    .news-item { flex-basis: calc((100% - 3rem) / 3); }
}

.news-track:focus-visible {
    outline: 3px solid var(--ispweb-tertiary);
    outline-offset: 4px;
}

.news-track::-webkit-scrollbar {
    height: 6px;
}

.news-track::-webkit-scrollbar-thumb {
    background: rgba(2, 67, 125, 0.35);
    border-radius: 3px;
}

/*
 * No banded bar on a news card, unlike the product and pricing cards.
 *
 * Those sit in a grid with nothing above them. A news card sits directly under
 * the section heading, which carries the same banded rule -- so the two landed
 * a few pixels apart and read as one double line that had come out wrong. With
 * a single item published it is worse still: the card is as wide as the column,
 * so the two lines are the same length as well.
 *
 * The heading keeps its rule, because that is what marks the section. The cards
 * below it do not need to say it a second time.
 */
.news-item {
    scroll-snap-align: start;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.news-thumb {
    margin: 0 0 1rem;
    overflow: hidden;
    border-radius: var(--ispweb-radius);
}

.news-thumb img {
    width: 100%;
    height: auto;
    aspect-ratio: 3 / 2;
    object-fit: cover;
}

.news-item-body {
    display: flex;
    flex-direction: column;
    flex: 1;
}

.news-item .date {
    color: var(--ispweb-muted);
    font-size: 0.85rem;
    margin-bottom: 0.25rem;
}

.news-item-title {
    margin-bottom: 0.5rem;
}

/* Pushes every "Lees meer" onto the same baseline, whatever the summary does. */
.news-item-link {
    margin-top: auto;
    font-weight: 700;
}

/* --------------------------------------------------------------------------
   Carousel arrows
   -------------------------------------------------------------------------- */

/* Revealed by ispweb.js, and only when the track actually overflows: two
   arrows that cannot move anything are worse than no arrows. */
.news-controls {
    display: flex;
    gap: 0.5rem;
    justify-content: flex-start;
}

@media (min-width: 992px) {
    .news-controls { justify-content: flex-end; }
}

.news-arrow {
    width: 42px;
    height: 42px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 2px solid var(--ispweb-secondary);
    border-radius: 50%;
    background: transparent;
    color: var(--ispweb-secondary);
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.news-arrow svg {
    width: 16px;
    height: 16px;
}

.news-arrow svg path {
    fill: currentColor;
}

.news-arrow:hover,
.news-arrow:focus-visible {
    background: var(--ispweb-secondary);
    color: #fff;
}

/* At either end the arrow is dimmed and unclickable rather than removed, so
   the pair does not shift sideways as you scroll. */
.news-arrow[disabled] {
    opacity: 0.35;
    cursor: default;
}

.news-arrow[disabled]:hover {
    background: transparent;
    color: var(--ispweb-secondary);
}

@media (prefers-reduced-motion: reduce) {
    .news-track {
        scroll-behavior: auto;
    }
}

/* --------------------------------------------------------------------------
   Long-form documents
   --------------------------------------------------------------------------
   The voorwaarden, privacy-, cookie- en disclaimerpagina's. These are pages
   people read to find one specific answer, so the styling is about
   scannability: a clear opening, headings that stand out from the body, and a
   comfortable measure.
   -------------------------------------------------------------------------- */

/* The opening paragraph: the summary in one or two sentences. */
.longform .block-body .lead {
    font-size: 1.12rem;
    line-height: 1.65;
    padding: 1.1rem 1.3rem;
    margin-bottom: 2rem;
    background: var(--ispweb-light-blue);
    border-left: 4px solid var(--ispweb-secondary);
    border-radius: 0 var(--ispweb-radius) var(--ispweb-radius) 0;
}

/* Section headings inside a document. A rule above rather than below, so the
   heading reads as belonging to the text that follows it. */
.longform .block-body h2 {
    margin-top: 2.4rem;
    margin-bottom: 0.75rem;
    padding-top: 1.1rem;
    border-top: 2px solid rgba(2, 67, 125, 0.12);
    font-size: 1.45rem;
}

/* The first heading needs no separator: nothing precedes it but the lead. */
.longform .block-body > h2:first-child {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}

.longform .block-body h3 {
    margin-top: 1.6rem;
    margin-bottom: 0.5rem;
    font-size: 1.12rem;
    color: var(--ispweb-primary);
}

.longform .block-body p,
.longform .block-body li {
    /* Long lines are hard to track back from; this caps the measure without
       narrowing a column that is already narrow. */
    max-width: 68ch;
}

.longform .block-body ul,
.longform .block-body ol {
    margin-bottom: 1.1rem;
    padding-left: 1.25rem;
}

.longform .block-body li {
    margin-bottom: 0.45rem;
}

/* Definition lists carry the company details in the privacy statement. */
.longform .block-body dl {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.3rem 1.25rem;
    margin: 1.1rem 0 1.4rem;
    padding: 1rem 1.25rem;
    background: var(--ispweb-light-blue);
    border-radius: var(--ispweb-radius);
    max-width: 68ch;
}

.longform .block-body dt {
    font-weight: 700;
    color: var(--ispweb-primary);
}

.longform .block-body dd {
    margin: 0;
}

@media (max-width: 575.98px) {
    .longform .block-body dl {
        grid-template-columns: 1fr;
        gap: 0 0;
    }

    .longform .block-body dd {
        margin-bottom: 0.6rem;
    }
}

/* The "laatst bijgewerkt" line, which closes every document. */
.longform .block-body .doc-updated {
    margin-top: 2.5rem;
    padding-top: 1rem;
    border-top: 2px solid rgba(2, 67, 125, 0.12);
    font-size: 0.9rem;
    color: var(--ispweb-muted);
}

/* --------------------------------------------------------------------------
   Check another domain
   --------------------------------------------------------------------------
   The search bar repeated under the verdict. Someone who has just looked up one
   name usually has a second one in mind, and the only route back to the bar was
   the homepage.
   -------------------------------------------------------------------------- */

.domain-recheck {
    margin-top: 2.5rem;
    padding-top: 1.75rem;
    border-top: 1px solid rgba(2, 67, 125, 0.14);
    max-width: 34rem;
}

/* Staat er een contactformulier, dan is de zoekbalk een link geworden: twee
   invulvelden onder elkaar concurreren met elkaar, en de pagina werd er twee
   schermen lang van. De link leidt naar de checkpagina zelf, die de zoekbalk
   toont -- er gaat dus niets verloren. */
.domain-recheck-back {
    margin-top: 1.5rem;
    font-size: 0.95rem;
}

/* --------------------------------------------------------------------------
   De uitslag met een contactformulier erin

   Zonder formulier is dit de hero-opmaak: een korte tekst, verticaal gecentreerd
   naast een illustratie van zo'n achthonderd pixels. Boven 1420px geeft site.css
   .intro-section .col-text geen bovenruimte -- de hoogte van die illustratie
   houdt de tekst vanzelf onder de vaste header.

   Met het formulier erbij is de tekstkolom net zo hoog als de illustratie, en
   dan klopt er niets meer van dat centreren: de tekst begint bovenaan de sectie
   (achter het logo) en de illustratie zakt naar het midden in plaats van
   rechtsboven te beginnen. Het blok lijnt daarom bovenaan uit zodra het
   formulier er staat, en de ruimte onder de header zit hier -- op de tékstkolom.

   Op de kolom en niet op de container of de rij, want die twee nemen de
   illustratie mee omlaag, en juist die hoort rechtsboven in de hoek te
   beginnen. Bovenaan uitgelijnd geeft centreren ook niets meer terug van wat
   hier wordt opgeteld.

   Alleen vanaf 992px: daaronder staan de kolommen onder elkaar, is de header een
   smalle balk met een hamburgerknop, en doet `pt-6` op de container het werk al.

   Het wint met opzet van de ruimtekeuze van de redacteur (`space-top-none` en
   familie): die gaat over ritme tussen blokken, dit gaat over niet achter het
   logo beginnen. Een blok dat onleesbaar is, is geen instelling.
   -------------------------------------------------------------------------- */

@media (min-width: 992px) {
    .domain-result-section.has-contact .col-text {
        padding-top: 11rem;
    }
}

.domain-recheck .input-group {
    flex-wrap: nowrap;
}

.domain-recheck h2 {
    color: var(--ispweb-primary);
}

/* Matches the stacking the homepage bar does at the same width. */
@media (max-width: 575.98px) {
    .domain-recheck .input-group {
        flex-wrap: wrap;
    }

    .domain-recheck .domain-check-prefix {
        display: none;
    }

    .domain-recheck .form-control,
    .domain-recheck .btn {
        flex: 1 1 100%;
        border-radius: var(--ispweb-radius) !important;
    }

    .domain-recheck .btn {
        margin-top: 0.5rem;
    }
}

/* --------------------------------------------------------------------------
   Long documents: contents beside the text
   --------------------------------------------------------------------------
   The voorwaarden, privacy-, cookie- en disclaimerpagina's.

   These were laid out as one column beside an illustration. With the amount of
   text they carry that produced a very tall page filled only down its left-hand
   side, and no way to reach a section other than scrolling past everything
   above it. The contents list fills that column, follows the reader down, and
   makes every section one click away.
   -------------------------------------------------------------------------- */

.document {
    display: grid;
    gap: 2.5rem;
    /* Centred rather than stretched: the text keeps a readable measure, and the
       leftover width is split evenly instead of collecting on the right. */
    justify-content: center;
}

@media (min-width: 992px) {
    .document {
        grid-template-columns: minmax(230px, 280px) minmax(0, 74ch);
        gap: 3.5rem;
        align-items: start;
    }
}

/* -- the list ------------------------------------------------------------- */

.document-toc {
    position: relative;
    padding: 1.4rem 1.5rem 1.5rem;
    background: var(--ispweb-light-blue);
    border-radius: var(--ispweb-radius);
    overflow: hidden;
}

/* The house palette, on the panel that opens the page. */
.document-toc::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 4px;
    background: var(--ispweb-accent-bar);
}

@media (min-width: 992px) {
    .document-toc {
        position: sticky;
        /* Clear of the fixed header once it has shrunk. */
        top: 96px;
        max-height: calc(100vh - 128px);
        overflow-y: auto;
    }
}

.document-toc-title {
    margin: 0.35rem 0 0.9rem;
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ispweb-muted);
    /* Overrides the document h2 styling further down: this is a label, not a
       section of the document. */
    padding-top: 0;
    border-top: 0;
}

.document-toc ol {
    counter-reset: toc;
    margin: 0;
    padding: 0;
    list-style: none;
}

.document-toc li {
    margin: 0;
    counter-increment: toc;
}

.document-toc a {
    display: block;
    padding: 0.4rem 0 0.4rem 2rem;
    position: relative;
    font-size: 0.94rem;
    line-height: 1.4;
    color: var(--ispweb-primary);
    text-decoration: none;
    border-radius: 3px;
}

/* Numbered without an <ol> marker, so the number can be styled and aligned. */
.document-toc a::before {
    content: counter(toc);
    position: absolute;
    left: 0;
    top: 0.4rem;
    width: 1.35rem;
    text-align: right;
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--ispweb-secondary);
}

.document-toc a:hover,
.document-toc a:focus-visible {
    color: var(--ispweb-secondary);
    text-decoration: underline;
}

/* -- the document itself --------------------------------------------------- */

.document-body {
    min-width: 0;
}

/*
 * `.longform.col-12` caps the article at 72ch so a full-width block of prose
 * keeps a readable line length. With a contents list that cap applies to the
 * whole two-column grid instead of to the text, squeezing both columns into
 * half the page -- which is the emptiness this layout exists to fix. Here the
 * measure is set by the grid's own text column, so the article may have the
 * full width.
 */
.longform.col-12.has-toc,
.longform.col-12.is-wide {
    max-width: none;
}

/* With the cap gone the article fills the container, so the measure has to be
   restored on the elements that are actually read. Cards, grids and lists are
   deliberately left out: filling the width is the point of them. */
.longform.is-wide .block-body > p,
.longform.is-wide .block-body > h2,
.longform.is-wide .block-body > h3 {
    max-width: 68ch;
}

/*
 * Section headings.
 *
 * A short banded rule above each one instead of a flat grey line: it is the
 * same palette as the footer and the cards, and on a page this long it gives
 * the reader something to count sections by.
 */
.longform.has-toc .document-body h2,
.longform.is-wide .block-body h2 {
    position: relative;
    margin-top: 2.75rem;
    margin-bottom: 0.85rem;
    padding-top: 1.25rem;
    border-top: 0;
    font-size: 1.4rem;
    color: var(--ispweb-primary);
    /* Clears the fixed header when jumped to from the contents. */
    scroll-margin-top: 110px;
}

.longform.has-toc .document-body h2::before,
.longform.is-wide .block-body h2::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 88px;
    height: 4px;
    border-radius: 2px;
    background: var(--ispweb-accent-bar);
}

.longform.has-toc .document-body > h2:first-child,
.longform.is-wide .block-body > h2:first-child {
    margin-top: 0;
}

.longform.has-toc .document-body h3,
.longform.is-wide .block-body h3 {
    scroll-margin-top: 110px;
}

/* The measure is already set by the grid column, so the per-element cap that
   the other documents use would only narrow it a second time. */
.longform.has-toc .document-body p,
.longform.has-toc .document-body li {
    max-width: none;
}

/* -- the download list ----------------------------------------------------- */

/* The voorwaarden page is mostly a list of PDF modules. As plain links they
   were an undifferentiated wall; as cards they are scannable, and the column
   count follows the width instead of being fixed. */
.longform.has-toc .download-list {
    columns: auto 1;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 0.6rem;
    margin: 1.1rem 0 0;
    padding: 0;
    list-style: none;
}

.longform.has-toc .download-list li {
    margin: 0;
}

.longform.has-toc .download-list a {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    height: 100%;
    /* Overrides the inline-block + left padding the plain list uses to make
       room for its absolutely positioned mark. */
    padding: 0.7rem 0.9rem;
    position: relative;
    background: #fff;
    border: 1px solid rgba(2, 67, 125, 0.16);
    border-left: 4px solid var(--ispweb-secondary);
    border-radius: var(--ispweb-radius);
    font-size: 0.88rem;
    line-height: 1.35;
    color: var(--ispweb-primary);
    text-decoration: none;
    overflow-wrap: anywhere;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.longform.has-toc .download-list a::before {
    content: "PDF";
    flex: none;
    /* The plain list further up styles this pseudo-element as a small outlined
       document mark: absolutely positioned, sized in em, with a border. Those
       declarations still apply here, so they are undone explicitly -- without
       this the badge is a red sliver a few pixels across with the word
       overflowing invisibly out of it. */
    position: static;
    width: auto;
    height: auto;
    border: 0;
    padding: 0.15rem 0.4rem;
    border-radius: 2px;
    background: var(--ispweb-accent-red);
    color: #fff;
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    line-height: 1.3;
    /* The link sets overflow-wrap: anywhere so a long module name can break;
       without this the badge inherits it and "PDF" is broken onto three lines,
       leaving a red sliver a few pixels wide. */
    white-space: nowrap;
    overflow-wrap: normal;
}

.longform.has-toc .download-list a:hover,
.longform.has-toc .download-list a:focus-visible {
    background: var(--ispweb-light-blue);
    border-left-color: var(--ispweb-accent-green);
}

/* -- callout --------------------------------------------------------------- */

/* A paragraph marked .note in the copy: something the reader should not skim
   past. Yellow, matching the warning band elsewhere on the site. */
.longform .block-body .note {
    padding: 1rem 1.25rem;
    margin: 1.5rem 0;
    background: #fffdf0;
    border-left: 4px solid var(--ispweb-accent-yellow);
    border-radius: 0 var(--ispweb-radius) var(--ispweb-radius) 0;
}

.longform .block-body .note > :last-child {
    margin-bottom: 0;
}

@media (prefers-reduced-motion: reduce) {
    .longform.has-toc .download-list a {
        transition: none;
    }
}

/* --------------------------------------------------------------------------
   Social icons
   --------------------------------------------------------------------------
   Every icon carries all four house accents as a ring, rather than one accent
   each. Same reasoning as the cards: cycling one colour per icon made the row
   read as a sequence, and it left each network arbitrarily "the yellow one".

   It also settles a mismatch in the icon set itself. Three of these marks are
   solid discs with the glyph knocked out; WhatsApp and Instagram are outlines.
   Coloured flat, side by side, an outline among discs reads as an icon that
   failed to load. Inside a ring on a white field they all read the same: a blue
   mark in a circle.
   -------------------------------------------------------------------------- */

.copyright-section .socialmedia li a .svg-icon {
    /* The ring. Four quadrants, hard stops -- a blended conic gradient turns to
       mud at three pixels wide. Starting at 45deg so a colour boundary is not
       sitting on the vertical axis, where it reads as a seam. */
    background: conic-gradient(
        from calc(45deg + var(--ring-rotate, 0deg)),
        var(--ispweb-accent-red) 0 25%,
        var(--ispweb-accent-yellow) 25% 50%,
        var(--ispweb-accent-green) 50% 75%,
        var(--ispweb-accent-blue) 75% 100%
    );
    border-radius: 50%;
    /*
     * The ring is drawn *inside* the box the icon already occupied.
     *
     * With content-box the badge grew by twice the padding, and four of them no
     * longer fitted the column the copyright bar gives them -- Instagram
     * dropped onto a second line. border-box plus an explicit size keeps the
     * footprint exactly what it was; the ring eats into the mark instead of
     * into the row.
     */
    box-sizing: border-box;
    width: 1em;
    height: 1em;
    padding: 3px;
}

/*
 * Each ring turned a quarter further than the one before it.
 *
 * All four buttons carry the whole palette -- that part does not change, and it
 * is what keeps them reading as one set rather than as four ranked things. What
 * changes is where the set starts, so the colour at twelve o'clock runs blue,
 * green, yellow, red across the row. Side by side that is the difference between
 * four identical stamps and a row that was arranged.
 *
 * Rotating rather than recolouring is also what keeps this honest at any length:
 * :nth-child(4n+…) means a fifth button starts the sequence again instead of
 * running out of colours.
 */
.copyright-section .socialmedia li:nth-child(4n + 2) a .svg-icon { --ring-rotate: 90deg; }
.copyright-section .socialmedia li:nth-child(4n + 3) a .svg-icon { --ring-rotate: 180deg; }
.copyright-section .socialmedia li:nth-child(4n + 4) a .svg-icon { --ring-rotate: 270deg; }

/* The white field the mark sits on, and what makes the gradient show as a ring
   instead of a filled circle. */
.copyright-section .socialmedia li a .svg-icon svg {
    display: block;
    width: 100%;
    height: 100%;
    background: #fff;
    border-radius: 50%;
}

/*
 * The mark itself, in the brand blue.
 *
 * Driven through `color` with the fill forced, because the icons disagree about
 * how they paint themselves: three carry a `cls-1` class filled from a <style>
 * block inside the SVG, and WhatsApp and Instagram carry an inline
 * `style="fill: currentColor"`. An inline style beats an ordinary rule, so
 * without !important one declaration would not cover both kinds.
 */
.copyright-section .socialmedia li a {
    color: var(--ispweb-primary);
}

.copyright-section .socialmedia li a .svg-icon path,
.copyright-section .socialmedia li a .svg-icon rect,
.copyright-section .socialmedia li a .svg-icon circle,
.copyright-section .socialmedia li a .svg-icon polygon {
    fill: currentColor !important;
}

/* On hover the ring stays and the mark deepens, so the state change does not
   depend on telling two accent colours apart. site.css already scales the icon
   up by 1.1 on hover. */
.copyright-section .socialmedia li a:hover,
.copyright-section .socialmedia li a:focus-visible {
    color: var(--ispweb-secondary);
}

/* ==========================================================================
   Product pages
   ==========================================================================
   The six pages under /producten. They were a wall of prose with an
   illustration beside it, written in a register that had drifted (Title Case
   Everywhere on one page, "u" on the next, "jouw" on a third) and structured
   with <h6> and <br><br> because the original editor had no headings.

   They are documents now, like the policy pages, but shorter and with the
   product's own mark at the top -- the same line-art icon the homepage cards
   use, which is the one distinctive image each product actually has.
   -------------------------------------------------------------------------- */

/* -- the opening panel ----------------------------------------------------- */

.longform .block-body .product-intro {
    display: grid;
    gap: 1.5rem;
    align-items: center;
    position: relative;
    overflow: hidden;
    margin: 0 0 2.25rem;
    padding: 1.75rem 1.9rem;
    background: var(--ispweb-light-blue);
    border-radius: var(--ispweb-radius);
}

@media (min-width: 576px) {
    .longform .block-body .product-intro {
        grid-template-columns: auto minmax(0, 1fr);
        gap: 1.9rem;
    }
}

.longform .block-body .product-intro::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 4px;
    background: var(--ispweb-accent-bar);
}

/* The product mark. The icons carry their own #52ade0 fill, so they arrive in
   the house blue without being recoloured here. */
.longform .block-body .product-mark {
    margin: 0;
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 92px;
    height: 92px;
    padding: 1.1rem;
    background: #fff;
    border-radius: var(--ispweb-radius);
    box-shadow: 0 2px 10px rgba(2, 67, 125, 0.08);
}

.longform .block-body .product-mark img {
    width: auto;
    height: 100%;
    max-width: 100%;
}

/* Inside the panel the lead loses the bar and the tint it has elsewhere -- the
   panel is already both. */
.longform .block-body .product-intro .lead {
    margin: 0;
    padding: 0;
    background: none;
    border: 0;
}

/* -- feature cards --------------------------------------------------------- */

.longform .block-body .feature-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15.5rem, 1fr));
    gap: 0.85rem;
    margin: 1.25rem 0 0;
    padding: 0;
    list-style: none;
}

.longform .block-body .feature-grid li {
    position: relative;
    margin: 0;
    max-width: none;
    padding: 1rem 1.1rem 1.05rem;
    background: #fff;
    border: 1px solid rgba(2, 67, 125, 0.14);
    border-radius: var(--ispweb-radius);
    font-size: 0.94rem;
    line-height: 1.5;
    overflow: hidden;
}

.longform .block-body .feature-grid li::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: var(--ispweb-accent-bar);
}

.longform .block-body .feature-grid strong {
    display: block;
    margin-bottom: 0.25rem;
    color: var(--ispweb-primary);
    font-size: 1rem;
}

/* -- specification list ---------------------------------------------------- */

/* Two columns of label and value: prices, extensions, what is included. The
   policy pages use a plain <dl> for company details; this is the same element
   with rules between the rows, because these lists are longer and read as a
   table. */
.longform .block-body .spec-list {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0;
    margin: 1.25rem 0 0;
    padding: 0;
    background: none;
    max-width: none;
}

@media (min-width: 576px) {
    .longform .block-body .spec-list {
        grid-template-columns: minmax(10rem, auto) minmax(0, 1fr);
    }
}

.longform .block-body .spec-list dt {
    padding: 0.7rem 1rem 0.2rem 0;
    color: var(--ispweb-primary);
    font-weight: 700;
    border-top: 1px solid rgba(2, 67, 125, 0.12);
}

.longform .block-body .spec-list dd {
    margin: 0;
    padding: 0 0 0.7rem;
}

@media (min-width: 576px) {
    .longform .block-body .spec-list dt {
        padding: 0.7rem 1rem 0.7rem 0;
    }

    .longform .block-body .spec-list dd {
        padding: 0.7rem 0;
        border-top: 1px solid rgba(2, 67, 125, 0.12);
    }
}

/* -- closing panel --------------------------------------------------------- */

/* The "what now" at the foot of each product page. Deliberately the brand blue
   rather than another tinted box: it is the one thing on the page asking the
   reader to do something. */
.longform .block-body .cta-panel {
    position: relative;
    overflow: hidden;
    margin: 2.5rem 0 0;
    padding: 1.6rem 1.8rem;
    background: var(--ispweb-primary);
    border-radius: var(--ispweb-radius);
    color: #fff;
}

.longform .block-body .cta-panel::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 4px;
    background: var(--ispweb-accent-bar);
}

.longform .block-body .cta-panel p {
    max-width: none;
}

.longform .block-body .cta-panel > :last-child {
    margin-bottom: 0;
}

.longform .block-body .cta-panel a {
    color: #fff;
    font-weight: 700;
    text-decoration: underline;
}

.longform .block-body .cta-panel a:hover {
    color: var(--ispweb-tertiary);
}

/* ==========================================================================
   Outage bar
   ==========================================================================
   Shown above the header on every page while an incident is marked `high`.
   Rendered inside <header>, which site.css fixes to the top of the viewport,
   so the bar travels with it.
   -------------------------------------------------------------------------- */

.alert-bar {
    background: var(--ispweb-accent-red);
    color: #fff;
    /*
     * A fixed height, and a single line of text.
     *
     * The page below clears the fixed header with paddings of its own rather
     * than a global offset, so the bar's height has to be a number this
     * stylesheet knows — otherwise the compensation on <main> is guesswork and
     * a long incident title pushes the logo over the hero.
     */
    height: 44px;
    display: flex;
    align-items: center;
    font-size: 0.92rem;
}

.alert-bar-inner {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    min-width: 0;
}

.alert-bar-tag {
    flex: none;
    padding: 0.1rem 0.5rem;
    border-radius: 2px;
    background: rgba(0, 0, 0, 0.22);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    white-space: nowrap;
}

/* The title is the part that gives way: it is the only piece whose length we
   do not control, and the link must stay reachable. */
.alert-bar-text {
    margin: 0;
    /* Grows to fill, which is what pushes the clock and the link to the right.
       Doing that with margin-left:auto on the link instead would break the
       moment the clock is absent -- an incident with an unreadable start time
       would leave the link stranded in the middle. */
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 700;
}

/* How long it has been running. Tabular figures so the seconds ticking over do
   not make the text jitter sideways once a second. */
.alert-bar-since {
    flex: none;
    padding: 0.1rem 0.5rem;
    border-radius: 2px;
    background: rgba(0, 0, 0, 0.18);
    font-size: 0.82rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.alert-bar-link {
    flex: none;
    margin-left: 0.75rem;
    color: #fff;
    font-weight: 700;
    text-decoration: underline;
    white-space: nowrap;
}

.alert-bar-link:hover,
.alert-bar-link:focus-visible {
    color: #fff;
    text-decoration: none;
}

.alert-bar a:focus-visible {
    outline-color: #fff;
}

/* Give back exactly what the bar took. Without this the header is 44px taller
   and overlaps the first section, which reserves its space for a header of the
   original height. */
body.has-alert main {
    padding-top: 44px;
}

/* On a phone the link label goes and the arrow carries it, so the title keeps
   the width it needs. */
@media (max-width: 575.98px) {
    .alert-bar {
        font-size: 0.85rem;
    }

    .alert-bar-tag,
    .alert-bar-since {
        display: none;
    }
}

/* Between phone and desktop there is room for the clock but not for both it
   and the tag. */
@media (min-width: 576px) and (max-width: 767.98px) {
    .alert-bar-tag {
        display: none;
    }
}

/* --------------------------------------------------------------------------
   Telephone hours in the footer
   --------------------------------------------------------------------------
   Directly under the number they belong to. Headed "telefonisch bereikbaar"
   and not "openingstijden": there is no counter to visit, and a heading that
   suggests one is the beginning of someone driving to Schijndel.
   -------------------------------------------------------------------------- */

/*
 * The heading carries the number it belongs to.
 *
 * "Tel: …" stood on a line of its own above an unrelated heading, which read as
 * two facts instead of one. The number and when it is answered are the same
 * answer to the same question.
 */
.footer-hours-title {
    position: relative;
    margin: 0 0 0.55rem;
    padding-bottom: 0.5rem;
    font-weight: 700;
    letter-spacing: 1px;
}

/*
 * The same banded line as everywhere else, at footnote scale.
 *
 * It was a grey hairline, which did the job and said nothing. The four accents
 * are what tie the cards, the news items, the incident stripes and the band
 * above the footer together, and there is no reason this one line should be the
 * exception.
 *
 * Full width of the column, exactly where the hairline ran: it lines up with the
 * right edge of the times below it, and a short stub under a heading that spans
 * the whole column just looks unfinished.
 */
.footer-hours-title::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 3px;
    background: var(--ispweb-accent-bar);
}

.footer-hours-title a {
    white-space: nowrap;
}

/*
 * The days and times as one small table.
 *
 * A grid on the <dl> rather than on each row: that way every time starts at the
 * same x *and* ends at the same x, whatever the longest day label is, and the
 * column is exactly as wide as it needs to be instead of a guessed 7.5rem.
 */
.footer-hours {
    margin: 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr) max-content;
    gap: 0.1rem 1.25rem;
    line-height: 1.5;
}

/* The heading above is the bold one. These are its detail, so they step back:
   shouting all three made the block read as a wall. */
.footer-hours-day {
    font-weight: 400;
}

.footer-hours-time {
    margin: 0;
    /* Tabular figures so the digits line up between rows as well, and a flush
       right edge so the block ends on a straight line. */
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
}

/* "Gesloten" is the absence of an answer; it should not carry the same weight
   as the hours that are worth acting on. */
.footer-hours-closed {
    opacity: 0.7;
}

/* --------------------------------------------------------------------------
   Contact block: tighter
   --------------------------------------------------------------------------
   The block grew by four lines when the hours were added and became the
   tallest thing on the page. Nothing is removed -- the air is.
   -------------------------------------------------------------------------- */

.footer-section .container {
    padding-top: 3rem !important;
    padding-bottom: 3rem !important;
}

/* The headset drawing was 168px and set the height of the whole band. */
.footer-section svg {
    height: 120px;
}

/*
 * A hierarchy instead of nine equally loud lines.
 *
 * site.css makes the whole column bold with 2px of letter-spacing. Labels keep
 * that; values do not. The letter-spacing in particular is what made it read as
 * a wall -- it is fine on three words and exhausting on a postcode and an IBAN.
 */
.footer-section .contact-details {
    letter-spacing: 0;
    line-height: 1.55;
}

.footer-section .contact-details .footer-label {
    font-weight: 700;
    letter-spacing: 1px;
}

.footer-section .contact-details .footer-value {
    font-weight: 400;
}

/* The paperwork: present, findable, and not competing with the phone number. */
.footer-section .contact-details .footer-registration {
    font-size: 0.92rem;
    opacity: 0.8;
}

.footer-section .contact-details ul {
    margin-bottom: 0;
}

/* The spacing between the groups of details was a full line; half is enough to
   read them as separate without the block growing another 40px. */
.footer-section .contact-details .pt-1 {
    padding-top: 0.5rem !important;
}

@media (max-width: 991.98px) {
    .footer-section svg {
        height: 96px;
    }
}

/* --------------------------------------------------------------------------
   Copyright bar: lower
   --------------------------------------------------------------------------
   Its height came from the social icons, which site.css sets at font-size: 3em.
   Four coloured discs at 48px in a bar of one line of text made it taller than
   the content it carries.
   -------------------------------------------------------------------------- */

/*
 * The bar was cut back when it held four decorative icons. It now holds the
 * only way to reach us from the foot of the page, so the buttons are the
 * content rather than trim -- big enough to be an obvious tap target on a
 * phone, and the bar grows to fit them rather than squeezing them.
 */
.copyright-section .container {
    padding-top: 1.15rem !important;
    padding-bottom: 1.15rem !important;
}

.copyright-section .socialmedia a .svg-icon {
    font-size: 2.9em;
    margin-right: 0.7rem;
}

.copyright-section .footer-menu li {
    padding-right: 1.1rem;
}

.copyright-section {
    font-size: 0.92rem;
}

/* On a phone the bar stacks anyway; the extra breathing room there is the
   difference between two rows and three. */
@media (max-width: 1199.98px) {
    .copyright-section .container {
        padding-top: 1.1rem !important;
        padding-bottom: 1.1rem !important;
    }
}

/* --------------------------------------------------------------------------
   Contact buttons
   --------------------------------------------------------------------------
   Telephone, e-mail, WhatsApp and the profile pages, in one row and one style.
   They used to be a row of social icons, a phone number elsewhere in the
   footer, an "E-mail: klik hier" link elsewhere again, and a third-party widget
   floating over the corner in a style belonging to nobody.

   The ring styling they inherit from .socialmedia is deliberate: every way of
   reaching us now looks the same, whether it dials, mails or opens a profile.
   -------------------------------------------------------------------------- */

.copyright-section .socialmedia {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0;
    padding: 0;
}

/* The line icons sit a touch tighter than the knocked-out discs do, so they
   get a little padding to match. Nothing else distinguishes the types: the
   whole point is that every way of reaching us looks the same. */
.contact-button-phone .svg-icon svg,
.contact-button-email .svg-icon svg,
.contact-button-whatsapp .svg-icon svg {
    padding: 2px;
}

/* --------------------------------------------------------------------------
   Contact form
   --------------------------------------------------------------------------
   Our own form, in our own stylesheet, in the same blue as everything else.
   What it replaces was a Freshworks widget: a script, a stylesheet and a
   webfont from someone else's server, rendering a green pill in a corner in a
   style belonging to nobody.

   A <dialog>, so the browser does the modal work -- focus trapping, Escape,
   inertness of the page behind it -- rather than a div and three hundred lines
   of JavaScript. Rendered by the server with `open` when scripting is off, in
   which case it appears as an ordinary panel and still submits.
   -------------------------------------------------------------------------- */

.contact-modal {
    /* Wide enough for the send button and Turnstile side by side -- the widget
       will not render below 300px, so the room has to come from here. The
       max-width is not redundant: a flex row that cannot shrink below its
       content will push a fixed-position box past the edge of the screen, and
       this is what stops it. */
    width: min(40rem, calc(100vw - 1.5rem));
    max-width: calc(100vw - 1.5rem);
    max-height: calc(100vh - 3rem);
    padding: 0;
    border: 0;
    border-radius: var(--ispweb-radius);
    box-shadow: 0 1.5rem 3rem rgba(2, 67, 125, 0.25);
    color: var(--ispweb-font);
    background: #fff;
}

/* The accent bar across the top: the same four colours as the cards, the news
   items and the band above the footer. */
.contact-modal::before {
    content: "";
    display: block;
    height: 4px;
    background: var(--ispweb-accent-bar);
}

.contact-modal::backdrop {
    background: rgba(2, 67, 125, 0.55);
}

.contact-modal-inner {
    padding: 1.5rem;
    /* A long form on a short screen scrolls inside the dialog rather than
       stretching it past the bottom of the viewport. */
    max-height: calc(100vh - 3rem);
    overflow-y: auto;
}

.contact-modal h2 {
    margin: 0 0 0.75rem;
    font-size: 1.5rem;
    color: var(--ispweb-primary);
}

.contact-modal-close {
    float: right;
    margin: -0.5rem -0.5rem 0 0;
    padding: 0 0.5rem;
    font-size: 1.75rem;
    line-height: 1;
    color: var(--ispweb-muted);
    background: none;
    border: 0;
    cursor: pointer;
}

.contact-modal-close:hover {
    color: var(--ispweb-primary);
}

.contact-form-intro {
    margin-bottom: 1.25rem;
    color: var(--ispweb-muted);
}

.contact-form-row {
    margin-bottom: 1rem;
}

.contact-form-row label {
    display: block;
    margin-bottom: 0.25rem;
    font-weight: 600;
    font-size: 0.95rem;
}

.contact-form-row input,
.contact-form-row select,
.contact-form-row textarea {
    width: 100%;
    padding: 0.55rem 0.75rem;
    font: inherit;
    color: inherit;
    background: #fff;
    border: 1px solid #c8d2dc;
    border-radius: var(--ispweb-radius);
}

.contact-form-row input:focus,
.contact-form-row select:focus,
.contact-form-row textarea:focus {
    outline: 2px solid var(--ispweb-secondary);
    outline-offset: 1px;
    border-color: var(--ispweb-secondary);
}

.contact-form-row [aria-invalid="true"] {
    border-color: var(--ispweb-danger);
}

.contact-form-row textarea {
    resize: vertical;
}

.contact-form-error {
    margin: 0.3rem 0 0;
    font-size: 0.9rem;
    color: var(--ispweb-danger);
}

/* A message about the form as a whole -- an expired token, a failed captcha --
   above the fields rather than beside one of them. */
.contact-modal > .contact-modal-inner > .contact-form-error {
    padding: 0.75rem 1rem;
    border-left: 4px solid var(--ispweb-danger);
    background: #fdf2f1;
}

.contact-form-sent {
    padding: 0.9rem 1rem;
    border-left: 4px solid var(--ispweb-tertiary);
    background: #f5faea;
}

/*
 * Send, captcha, cancel -- on one line, all the same height.
 *
 * The captcha used to sit on its own row above the button: a 65px panel in the
 * flow, twice the height of anything near it, which made the bottom of the form
 * look like two unrelated things stacked. Beside the button it reads as part of
 * sending, which is what it is.
 */
/*
 * Send and cancel on the left, the captcha hard against the right edge.
 *
 * Turnstile's widget is an iframe of a height Cloudflare decides (65px), and
 * nothing on our side can change what is inside it. So the button is given that
 * same height rather than the other way round: matching the one thing that
 * cannot move is what makes the row a row instead of a staircase.
 */
.contact-form-actions {
    --contact-captcha-height: 65px;
    display: flex;
    align-items: center;
    /* No wrapping: the captcha dropping to its own line is exactly what this
       layout exists to prevent. The phone gets its own rule at the bottom. */
    flex-wrap: nowrap;
    gap: 0.75rem;
    margin-top: 1.25rem;
}

.contact-form-actions .btn-primary {
    display: inline-flex;
    align-items: center;
    /* Allowed to give up width before the captcha does: a narrower "Versturen"
       is still a button, a narrower Turnstile is a broken one. */
    flex: 0 1 auto;
    min-height: var(--contact-captcha-height);
    white-space: nowrap;
}

.contact-form-actions .cf-turnstile {
    /* Hard against the right edge, and never squeezed below the 300px the
       widget itself refuses to go under. */
    margin-left: auto;
    flex: 0 0 300px;
    height: var(--contact-captcha-height);
}

.contact-form-actions .cf-turnstile iframe {
    max-width: 100%;
}

/* On a phone there is no room beside the button. Then it does wrap, full width,
   and stops being pushed anywhere. */
@media (max-width: 575.98px) {
    .contact-form-actions {
        flex-wrap: wrap;
    }

    .contact-form-actions .cf-turnstile {
        flex: 1 1 100%;
        margin-left: 0;
    }
}

/*
 * The honeypot.
 *
 * Off-screen rather than display:none or type="hidden": a bot that reads the
 * markup skips the obvious ones, and one that fills in every visible field
 * falls into this. aria-hidden and tabindex keep it away from anyone using a
 * keyboard or a screen reader, who would otherwise be asked for a "website"
 * that must stay empty.
 */
.contact-form-trap {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* --------------------------------------------------------------------------
   News archive (/nieuws)
   --------------------------------------------------------------------------
   A grid, not the homepage's scroll-snap row. That row shows a handful of items
   in a band beside other sections; here the list is the page, and it has to be
   able to grow to fifty without becoming a horizontal scroll of fifty.

   The card itself reuses .news-item, so an item looks the same wherever it is.
   -------------------------------------------------------------------------- */

.news-archive-lead {
    max-width: 60ch;
    margin-bottom: 2.5rem;
    color: var(--ispweb-muted);
}

.news-archive {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
    gap: 1.75rem;
    margin: 0;
    padding: 0;
}

/* The carousel sizes its items for a track; in a grid the cell decides. */
.news-archive-item {
    width: auto;
    min-width: 0;
    scroll-snap-align: none;
}

.news-archive-item .news-item-title {
    margin-bottom: 0.5rem;
}

/* --------------------------------------------------------------------------
   Seasons
   --------------------------------------------------------------------------
   A season repaints the four accent tokens from a <style> block in the head
   (src/Theme.php), so every banded line on the site follows without any rule
   here. What is left is the one line it may say.

   Nothing else is themed on purpose: a visitor who came to read about SSL
   should not have to scroll past clip-art, and a palette is noticeable without
   ever being in the way.
   -------------------------------------------------------------------------- */

.season-greeting {
    margin: 0.6rem 0 0;
    font-weight: 700;
    letter-spacing: 1px;
    font-size: 0.95rem;
}

/* The preview bar, for a signed ?season= link from the portal. Fixed to the
   bottom because the header is fixed to the top and the outage bar already
   owns that space, complete with a body class compensating for its height. */
.season-preview-bar {
    position: fixed;
    left: 50%;
    bottom: 1rem;
    z-index: 1080;
    transform: translateX(-50%);
    max-width: calc(100vw - 2rem);
    margin: 0;
    padding: 0.6rem 1.1rem;
    border-radius: 999px;
    background: rgba(19, 19, 19, 0.92);
    color: #fff;
    font-size: 0.88rem;
    box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.25);
}

/* --------------------------------------------------------------------------
   Seasonal effect layer
   --------------------------------------------------------------------------
   The particles built by seasons.js. Two animations run at once on each one:
   a fall from above the viewport to below it, and a sideways sway -- together
   they read as drifting rather than as dropping in a straight line.

   Both are transforms, so they run on the compositor and the page behind them
   is never re-laid-out. A JavaScript loop nudging thirty elements every frame
   is what this avoids.
   -------------------------------------------------------------------------- */

.season-layer {
    position: fixed;
    inset: 0;
    z-index: 1;
    overflow: hidden;
    /* Never eats a click, never blocks the page it decorates. */
    pointer-events: none;
}

.season-particle {
    position: absolute;
    top: -8vh;
    line-height: 1;
    user-select: none;
    will-change: transform;
    animation-name: season-fall, season-sway;
    animation-timing-function: linear, ease-in-out;
    animation-iteration-count: infinite, infinite;
}

.season-particle-spin {
    animation-name: season-fall-spin, season-sway;
}

/* Paused wholesale when the tab is hidden: nothing is visible there. */
.season-layer-paused .season-particle {
    animation-play-state: paused;
}

@keyframes season-fall {
    from { transform: translateY(0); }
    to   { transform: translateY(116vh); }
}

@keyframes season-fall-spin {
    from { transform: translateY(0) rotate(0deg); }
    to   { transform: translateY(116vh) rotate(540deg); }
}

/* The sway is on the element's own axis via margin, so it composes with the
   transform above instead of fighting it for the same property. */
@keyframes season-sway {
    0%, 100% { margin-left: -1.6rem; }
    50%      { margin-left: 1.6rem; }
}

/*
 * Someone who asks for no motion gets none.
 *
 * Not fewer particles and not slower ones: the request is usually made because
 * movement on a page causes actual nausea. seasons.js checks this too and never
 * builds the layer; this is the belt to that pair of braces.
 */
@media (prefers-reduced-motion: reduce) {
    .season-layer {
        display: none;
    }
}

/* --------------------------------------------------------------------------
   myip.ispweb.nl
   --------------------------------------------------------------------------
   The panel that answers the question in the domain name. myip is another site
   in this installation -- same code, same database, its own two hostnames -- so
   this is an ordinary block and its styling is an ordinary part of this file.

   The address is set in a large weight because it is the one thing anybody came
   for; everything below it is detail.
   -------------------------------------------------------------------------- */

.myip-heading {
    margin: 0 0 1.25rem;
    font-size: clamp(1.5rem, 3vw, 2rem);
    color: var(--ispweb-primary);
}

/*
 * Where the panel starts is decided by the alignment, not by a margin.
 *
 * The row is bottom-aligned from `lg` up (see the template), so the column sits
 * as low as the illustration is tall and the last line of text finishes level
 * with the drawing. Nothing here has to guess a distance, and it stays right
 * when the text is edited.
 *
 * The only nudge is at the bottom: the artwork has a little transparent space
 * under it, so a strictly equal bottom edge reads as the text hanging slightly
 * low.
 */
/*
 * A nudge downwards, on these two sections only.
 *
 * The illustration beside them is around eight hundred pixels tall and the text
 * is rarely half that, so vertically centred the block sits high against the
 * header. Bottom-aligned was the other extreme: with a short text it dropped to
 * the foot of the drawing and read as something that had fallen over.
 *
 * `position: relative` and a `top` rather than a margin, because a margin would
 * make the column taller and centring would then give back half of what was
 * added -- and on a long text it would push the section past the artwork. This
 * moves what you see and leaves the box where it was.
 *
 * Scoped to .myip-section and .parked-section: the hero on ispweb.nl uses
 * .intro-section .col-text as well and is deliberately untouched.
 */
@media (min-width: 992px) {
    .myip-section .col-text,
    .parked-section .col-text {
        position: relative;
        top: 3rem;
    }
}

/*
 * The illustration is left at full size on purpose.
 *
 * `.intro-section .image-right` in site.css gives it a width that deliberately
 * runs past the container to the edge of the viewport, and that bleed is the
 * whole look -- it is what the old myip page had and what the homepage hero
 * still does. Capping its height was tried and undone: it brought the artwork
 * back inside the column, where it reads as a picture that did not load
 * properly rather than as the background it is meant to be.
 *
 * The panel is centred against it, which is also what the old page did.
 */

.myip-panel {
    max-width: 46rem;
    padding: 2rem 2.25rem;
    background: var(--ispweb-light-blue);
    border-radius: var(--ispweb-radius);
    /* The house banded line across the top, exactly as on the product cards. */
    background-image: var(--ispweb-accent-bar);
    background-repeat: no-repeat;
    background-size: 100% 4px;
    background-position: top left;
}

.myip-label {
    margin: 0 0 0.15rem;
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--ispweb-muted);
}

.myip-address {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1rem;
    margin: 0 0 1.25rem;
    font-size: clamp(1.75rem, 5vw, 2.75rem);
    font-weight: 700;
    line-height: 1.1;
    color: var(--ispweb-primary);
    /* An address is read a character at a time and often typed over: tabular
       figures stop the digits from shifting around as they are scanned. */
    font-variant-numeric: tabular-nums;
    word-break: break-all;
}

.myip-copy {
    flex: 0 0 auto;
    padding: 0.35rem 0.9rem;
    font-size: 0.9rem;
    font-weight: 600;
    color: #fff;
    background: var(--ispweb-secondary);
    border: 0;
    border-radius: var(--ispweb-radius);
    cursor: pointer;
}

.myip-copy:hover,
.myip-copy:focus-visible {
    background: var(--ispweb-primary);
}

/* Hidden without scripting: a button that cannot copy is worse than no button. */
.no-js .myip-copy {
    display: none;
}

.myip-details {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    gap: 0.35rem 1.25rem;
    margin: 0 0 1.25rem;
    font-size: 0.95rem;
}

.myip-details dt {
    font-weight: 700;
    color: var(--ispweb-muted);
}

.myip-details dd {
    margin: 0;
    min-width: 0;
    word-break: break-word;
}

.myip-hint {
    margin: 0;
    font-size: 0.9rem;
    color: var(--ispweb-muted);
}

.myip-hint code {
    padding: 0.1rem 0.35rem;
    background: #fff;
    border-radius: 3px;
    color: var(--ispweb-primary);
}

@media (max-width: 575.98px) {
    .myip-panel {
        padding: 1.5rem 1.25rem;
    }

    .myip-details {
        grid-template-columns: minmax(0, 1fr);
        gap: 0 0;
    }

    .myip-details dd {
        margin-bottom: 0.6rem;
    }
}

/* --------------------------------------------------------------------------
   Contact tab
   --------------------------------------------------------------------------
   Half the pages on this site say "druk op de knop Contact". Until now that
   button was a small icon in the footer bar -- at the bottom of a page several
   screens tall, which is a strange place for the thing every other paragraph
   points at. This is the same button, kept in view.

   Left edge rather than right: the right is where a browser puts its scrollbar
   and where every support widget on the internet sits, and this is not one of
   those. Green, because that is the colour this site already uses for the one
   action it wants you to take ("Onze producten →").

   It opens the same dialog through the same link, so it works with scripting
   off as well.
   -------------------------------------------------------------------------- */

.contact-tab {
    position: fixed;
    left: 0;
    top: 50%;
    z-index: 1035;
    transform: translateY(-50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    padding: 1.1rem 0.55rem;
    background: var(--ispweb-tertiary);
    color: #fff;
    text-decoration: none;
    /* Rounded on the outer side only, so it reads as something attached to the
       edge of the window rather than as a floating pill. */
    border-radius: 0 var(--ispweb-radius) var(--ispweb-radius) 0;
    box-shadow: 0 0.25rem 1rem rgba(2, 67, 125, 0.28);
    transition: padding-left 0.15s ease, background-color 0.15s ease;
}

.contact-tab:hover,
.contact-tab:focus-visible {
    color: #fff;
    background: #8fb828;
    /* Leans out of the edge a little on hover: enough to answer "is this a
       button?" without moving anything else on the page. */
    padding-left: 0.95rem;
}

.contact-tab-label {
    /* Bottom-to-top, so the word is read from the bottom up like the spine of a
       book on a shelf -- the direction a left-hand tab is read in. */
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    font-size: 0.82rem;
    font-weight: 700;
    letter-spacing: 2px;
    text-transform: uppercase;
}

.contact-tab-icon {
    display: block;
    width: 1.15rem;
    height: 1.15rem;
    flex: 0 0 auto;
}

.contact-tab-icon svg {
    display: block;
    width: 100%;
    height: 100%;
}

.contact-tab-icon svg path,
.contact-tab-icon svg rect,
.contact-tab-icon svg circle,
.contact-tab-icon svg polygon {
    fill: currentColor !important;
}

/* Out of the way while the mobile menu is over the page: that menu covers the
   whole viewport, and a tab floating on top of it belongs to nothing. */
.active-mobile-menu .contact-tab {
    display: none;
}

/*
 * On a phone it moves to the corner and loses the word.
 *
 * A vertical tab against the left edge is fine at 1200px, where it sits in the
 * margin beside the text. At 375px there is no margin: the container starts
 * twelve pixels in, so the tab would lie across the first characters of every
 * line in the middle of the screen. Round, in the bottom corner, is where a
 * small screen expects a persistent action anyway -- and it is out of the
 * reading column entirely.
 */
@media (max-width: 575.98px) {
    .contact-tab {
        top: auto;
        bottom: 1rem;
        left: 1rem;
        transform: none;
        width: 3rem;
        height: 3rem;
        padding: 0;
        justify-content: center;
        border-radius: 50%;
    }

    .contact-tab:hover,
    .contact-tab:focus-visible {
        padding-left: 0;
    }

    .contact-tab-label {
        /* The icon carries it; the word is kept for screen readers. */
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }

    .contact-tab-icon {
        width: 1.35rem;
        height: 1.35rem;
    }
}

/*
 * Print, and anyone who asked for less movement.
 *
 * A fixed tab has no meaning on paper, and the hover shift is the only thing on
 * the page that moves without being asked.
 */
@media print {
    .contact-tab {
        display: none;
    }
}

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

    .contact-tab:hover,
    .contact-tab:focus-visible {
        padding-left: 0.55rem;
    }
}

/*
 * The explanation beside the illustration, under the panel.
 *
 * Held to a readable measure rather than the column width: the column is half a
 * wide screen, and a paragraph running the whole of that is hard to read back
 * to the start of the next line.
 */
.myip-body {
    max-width: 46rem;
    margin-top: 2.5rem;
}

.myip-body p:last-child {
    margin-bottom: 0;
}

/* IPv4 and IPv6 under each other, each with its own label and copy button. */
.myip-family + .myip-family {
    margin-top: 1.25rem;
}

/* Waiting, and not-found: both quieter than an address, because neither is the
   answer somebody came for. */
.myip-pending,
.myip-family-absent .myip-address {
    color: var(--ispweb-muted);
    font-size: clamp(1.1rem, 2.5vw, 1.5rem);
}

/* --------------------------------------------------------------------------
   Parked domain
   --------------------------------------------------------------------------
   The same shape as the hero, with the domain name as the headline. A parked
   page has one job -- confirm the visitor is in the right place -- so the name
   is the largest thing on it and everything else is a sentence or two.
   -------------------------------------------------------------------------- */

/*
 * The heading is a domain name, which behaves like nothing else on the site.
 *
 * It has no spaces to break at and can be sixty characters long, so two things
 * give way before the layout does: the size stops growing sooner than a normal
 * heading would, and the word may break anywhere. `anywhere` rather than
 * `break-word` because only the former also shrinks the element's minimum
 * width -- which is what lets it sit beside the floated illustration instead of
 * demanding its own line.
 */
.parked-heading {
    font-size: clamp(1.4rem, 3vw, 2.5rem);
    overflow-wrap: anywhere;
    hyphens: none;
}

.parked-lead {
    max-width: 46ch;
    font-size: 1.1rem;
}

.parked-body {
    max-width: 46ch;
}

.parked-body p:last-child {
    margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   Domains offered for sale
   -------------------------------------------------------------------------- */

/*
 * The offer for one domain, under the notice on its own page.
 *
 * Kept deliberately tight: this sits beside an illustration on a page that has
 * to stay one screen, and every line here is a line the bid form below it does
 * not get. The first version stacked a description, a price label and a price
 * on three rows for two short facts.
 */
.forsale-offer {
    max-width: 46rem;
    padding: 1.25rem 1.5rem;
    background: #fff;
    border-radius: var(--ispweb-radius);
    background-image: var(--ispweb-accent-bar);
    background-repeat: no-repeat;
    background-size: 100% 4px;
    background-position: top left;
}

.forsale-offer-heading {
    margin: 0 0 0.5rem;
    font-size: 1.25rem;
    color: var(--ispweb-primary);
}

/* Price and description share a line; the description takes what is left. */
.forsale-offer-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.35rem 1.5rem;
}

.forsale-offer-summary {
    flex: 1 1 12rem;
    margin: 0;
    color: var(--ispweb-muted);
    font-size: 0.95rem;
}

.forsale-price {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    margin: 0;
}

.forsale-price-label {
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--ispweb-muted);
}

.forsale-price-value {
    font-size: clamp(1.35rem, 3vw, 1.9rem);
    font-weight: 700;
    line-height: 1.1;
    color: var(--ispweb-primary);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.forsale-price-note {
    font-size: 0.8rem;
    color: var(--ispweb-muted);
    white-space: nowrap;
}

.forsale-bid {
    flex: 0 0 auto;
}

/* ---------------------------------------------------------------------------
   Seizoen: een geüpload logo en een strook in de header

   Het ingebouwde logo staat inline in de pagina als <svg> en wordt door
   site.css op hoogte gezet; een geüpload logo is een <img> en krijgt hier
   exact dezelfde maten. Niet "ongeveer even groot": de header krimpt tijdens
   het scrollen van 130 naar 60 pixels en de rest van de balk is daarop
   uitgelijnd, dus een logo dat die drie stappen niet meemaakt springt.
   --------------------------------------------------------------------------- */
.header-section .logo-container .logo-image,
.mobile-menu-section .logo-container .logo-image {
    height: 80px;
    width: auto;
    transition: 200ms all ease-in-out;
}

@media (min-width: 992px) {
    .header-section .logo-container .logo-image,
    .mobile-menu-section .logo-container .logo-image {
        height: 130px;
    }
}

.scroll .header-section .logo-container .logo-image {
    height: 60px;
}

/* De strook boven in de header: slingers, en verder eigenlijk niets.

   Herhaalt zich over de breedte, zodat één klein plaatje elk scherm dekt --
   een strook op ware breedte zou op een telefoon een postzegel zijn en op een
   breed scherm uitgerekt.

   De hoogte staat vast, net als bij de storingsbalk en om dezelfde reden: de
   header is aan de bovenkant van het venster vastgezet en de secties eronder
   compenseren zijn hoogte zelf. Een strook van onbekende hoogte duwt het logo
   over de hero heen. 28px is hoog genoeg voor slingers en laag genoeg om geen
   ruimte te kosten die iemand mist.

   contain in plaats van cover: bij cover wordt een slinger afgesneden zodra de
   verhouding niet klopt, en dan is het geen slinger meer maar een streep. */
.season-banner {
    height: 28px;
    background-repeat: repeat-x;
    background-position: center top;
    background-size: auto 28px;
    pointer-events: none;
}

/* Geef terug wat de strook innam, precies zoals body.has-alert dat doet. De
   twee tellen bij elkaar op als ze allebei staan -- een storing houdt geen
   rekening met carnaval. */
body.has-season-banner main {
    padding-top: 28px;
}

body.has-alert.has-season-banner main {
    padding-top: 72px;
}

/* De feiten onder de prijs: leeftijd en, boven de drempel, bezoekers.
   Een raster van vakjes en geen zinnen: het zijn twee losse cijfers die naast
   elkaar vergeleken worden, en een alinea eromheen zou ze laten lezen als
   verkooppraat in plaats van als gegevens. */
.forsale-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 2rem;
    margin: 1rem 0 0;
    padding: 0.9rem 0 0;
    border-top: 1px solid rgb(0 0 0 / 8%);
}

.forsale-fact dt {
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--ispweb-muted);
}

.forsale-fact dd {
    display: flex;
    align-items: baseline;
    gap: 0.45rem;
    margin: 0.1rem 0 0;
}

.forsale-fact-value {
    font-size: 1.15rem;
    font-weight: 700;
    line-height: 1.1;
    color: var(--ispweb-primary);
    font-variant-numeric: tabular-nums;
}

.forsale-fact-note {
    font-size: 0.8rem;
    color: var(--ispweb-muted);
}

/* Wát er geteld is, in kleine letters onder het getal. Klein omdat het geen
   verkoopargument is, en aanwezig omdat een bezoekcijfer van de verkoper
   zonder deze zin niets waard is. */
.forsale-facts-note {
    max-width: 46rem;
    margin: 0.6rem 0 0;
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--ispweb-muted);
}

/* In het overzicht staan er tientallen naast elkaar, dus daar is het één
   grijze regel om langs te scannen in plaats van een blok om af te lezen. */
.forsale-item-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 0.75rem;
    margin: 0.5rem 0 0;
    font-size: 0.82rem;
    color: var(--ispweb-muted);
    font-variant-numeric: tabular-nums;
}

/* Het bezoekcijfer is het zeldzame van de twee -- het staat er alleen boven de
   drempel -- en krijgt daarom het accent, zodat het opvalt tussen de regels
   waar het niet staat. */
.forsale-item-fact-visits {
    font-weight: 600;
    color: var(--ispweb-secondary);
}

/* De sorteerkeuze, tussen de telling en het zoekveld. Op een telefoon staat het
   zoekveld bovenaan: wie één naam zoekt tikt, wie bladert sorteert, en het
   eerste is waar het vaakst naar gereikt wordt. */
.forsale-sort {
    order: 2;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.forsale-sort-label {
    font-size: 0.85rem;
    color: var(--ispweb-muted);
}

.forsale-sort-select {
    padding: 0.55rem 0.75rem;
    border: 1px solid rgb(0 0 0 / 12%);
    border-radius: var(--ispweb-radius);
    background: #fff;
    font: inherit;
    font-size: 0.9rem;
}

.forsale-sort-select:focus-visible {
    outline: 2px solid var(--ispweb-secondary);
    outline-offset: 1px;
    border-color: var(--ispweb-secondary);
}

/* Gesorteerd op iets anders dan de naam is de alfabetische indeling geen
   indeling meer: de letterkoppen en de letterrij zouden dan boven een lijst
   staan die niet op die letters loopt. De groepen zelf blijven staan -- ze zijn
   leeg op één na, en een lege <ul> neemt geen ruimte in. */
.forsale-flat .forsale-group-letter,
.forsale-flat .forsale-index {
    display: none;
}

.forsale-flat .forsale-group + .forsale-group {
    margin-top: 0;
}

/* The overview on the main site. */
.forsale-list-intro {
    max-width: 60ch;
    margin-bottom: 2.5rem;
}

.forsale-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
    gap: 1.5rem;
    margin: 0 0 2rem;
    padding: 0;
}

.forsale-item {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: 1.25rem 1.4rem;
    background: var(--ispweb-light-blue);
    border-radius: var(--ispweb-radius);
    background-image: var(--ispweb-accent-bar);
    background-repeat: no-repeat;
    background-size: 100% 3px;
    background-position: top left;
}

.forsale-item-name {
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--ispweb-primary);
    text-decoration: none;
    /* Domain names have nowhere to break, so they are allowed to break
       anywhere rather than push the card wider than its column. */
    overflow-wrap: anywhere;
}

.forsale-item-name:hover,
.forsale-item-name:focus-visible {
    color: var(--ispweb-secondary);
    text-decoration: underline;
}

.forsale-item-summary {
    margin: 0;
    font-size: 0.95rem;
    color: var(--ispweb-muted);
}

/* De prijsregel staat onderaan de kaart, ook als er geen omschrijving is:
   margin-top:auto duwt hem naar beneden zodat de bedragen van kaarten naast
   elkaar op één lijn staan in plaats van elk op eigen hoogte. */
.forsale-item-price {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    margin: 0.35rem 0 0;
    padding-top: 0.6rem;
    border-top: 1px solid rgb(0 0 0 / 6%);
    margin-top: auto;
    font-variant-numeric: tabular-nums;
}

.forsale-item-amount {
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--ispweb-primary);
}

/* "Bieden" is geen bedrag, dus het krijgt ook niet het gewicht van een bedrag:
   zelfde regel, rustiger kleur, zodat een raster met en zonder prijzen niet
   uit elkaar valt. */
.forsale-item-ask {
    font-weight: 600;
    color: var(--ispweb-secondary);
}

.forsale-item-vat {
    font-size: 0.8rem;
    font-weight: 400;
    color: var(--ispweb-muted);
}

.forsale-count,
.forsale-empty {
    color: var(--ispweb-muted);
}

/* De regel boven de lijst: telling links, zoekveld rechts. Op een telefoon
   onder elkaar, met het zoekveld eerst -- dat is waar iemand naar reikt. */
.forsale-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin: 1.5rem 0 1rem;
}

.forsale-toolbar .forsale-count {
    order: 2;
    margin: 0;
}

.forsale-filter {
    order: 1;
    flex: 1 1 16rem;
    max-width: 24rem;
    margin-left: auto;
}

.forsale-filter-input {
    width: 100%;
    padding: 0.6rem 1rem;
    border: 1px solid rgb(0 0 0 / 12%);
    border-radius: var(--ispweb-radius);
    background: #fff;
    font: inherit;
}

.forsale-filter-input:focus-visible {
    outline: 2px solid var(--ispweb-secondary);
    outline-offset: 1px;
    border-color: var(--ispweb-secondary);
}

@media (min-width: 576px) {
    .forsale-toolbar .forsale-count { order: 1; }
    .forsale-sort { order: 2; }
    .forsale-filter { order: 3; }
}

/* De letterrij. Sticky onder de vaste header, zodat je hem houdt terwijl je
   door een lange lijst scrolt -- dat is het hele nut ervan. */
.forsale-index {
    position: sticky;
    /* Onder de gekrompen header. Die is 72px in de scroll-stand, en dat is de
       enige stand waarin je deze rij ooit sticky ziet staan. */
    top: 80px;
    z-index: 2;
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    padding: 0.5rem 0;
    margin-bottom: 0.5rem;
    background: inherit;
}

.forsale-index a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2rem;
    height: 2rem;
    padding: 0 0.4rem;
    border-radius: var(--ispweb-radius);
    background: var(--ispweb-light-blue);
    color: var(--ispweb-primary);
    font-weight: 700;
    font-size: 0.9rem;
    text-decoration: none;
}

.forsale-index a:hover,
.forsale-index a:focus-visible {
    background: var(--ispweb-primary);
    color: #fff;
}

/* Een letter waar na het filteren niets meer onder staat blijft staan maar
   dooft: hem weghalen laat de rij bij elke toetsaanslag verspringen. */
.forsale-index a.is-empty {
    opacity: 0.3;
    pointer-events: none;
}

.forsale-group + .forsale-group {
    margin-top: 2rem;
}

.forsale-group-letter {
    margin: 0 0 0.75rem;
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: 2px;
    color: var(--ispweb-muted);
    text-transform: uppercase;
}

/* Het contactformulier in de pagina. Zelfde velden als in de dialoog, maar in
   een paneel dat bij de rest van de banden hoort. */
.contact-block {
    padding: 1.75rem;
    background: #fff;
    border-radius: var(--ispweb-radius);
    box-shadow: 0 2px 18px rgb(0 0 0 / 6%);
    background-image: var(--ispweb-accent-bar);
    background-repeat: no-repeat;
    background-size: 100% 3px;
    background-position: top left;
}

.contact-block .contact-form-intro {
    margin-bottom: 1.25rem;
    color: var(--ispweb-muted);
}

/* Twee kolommen voor de korte velden, één voor het bericht: op een formulier
   van vier velden scheelt dat een halve schermhoogte scrollen. */
@media (min-width: 768px) {
    .contact-block .contact-form-fields {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 0 1.25rem;
    }

    .contact-block .contact-form-fields > .contact-form-row:nth-of-type(3),
    .contact-block .contact-form-fields > .contact-form-row:nth-of-type(4),
    .contact-block .contact-form-actions,
    .contact-block .contact-form-trap {
        grid-column: 1 / -1;
    }
}

/* The bid form, on the page of the domain it is about. */
.bid-form {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid rgba(2, 67, 125, 0.12);
}

.bid-form-sent {
    padding: 0.9rem 1rem;
    border: 0;
    border-left: 4px solid var(--ispweb-tertiary);
    background: #f5faea;
}

.bid-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 0.75rem;
    margin-bottom: 0.75rem;
}

.bid-field label {
    display: block;
    margin-bottom: 0.25rem;
    font-weight: 600;
    font-size: 0.95rem;
}

.bid-optional {
    font-weight: 400;
    color: var(--ispweb-muted);
}

.bid-field input,
.bid-field textarea {
    width: 100%;
    padding: 0.55rem 0.75rem;
    font: inherit;
    color: inherit;
    background: #fff;
    border: 1px solid #c8d2dc;
    border-radius: var(--ispweb-radius);
}

.bid-field input:focus,
.bid-field textarea:focus {
    outline: 2px solid var(--ispweb-secondary);
    outline-offset: 1px;
    border-color: var(--ispweb-secondary);
}

.bid-field textarea {
    resize: vertical;
}

.bid-error {
    margin: 0.3rem 0 0;
    font-size: 0.9rem;
    color: var(--ispweb-danger);
}

/* A message about the form as a whole sits above the fields, not beside one. */
.bid-form > .bid-error {
    margin: 0 0 1rem;
    padding: 0.75rem 1rem;
    border-left: 4px solid var(--ispweb-danger);
    background: #fdf2f1;
}

.bid-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1rem;
    margin-top: 0.9rem;
}

.bid-actions .cf-turnstile {
    margin-left: auto;
}

/* The way out of this page, under the form and quieter than it: it is an
   alternative to bidding, not a step in it. */
.forsale-overview-link {
    margin: 0.9rem 0 0;
    font-size: 0.95rem;
}

@media (max-width: 575.98px) {
    .bid-actions .cf-turnstile {
        margin-left: 0;
        width: 100%;
    }
}

/* ---------------------------------------------------------------------------
   De downloadpagina

   Rijen, geen kaartenraster. Een download is een regel met een naam, een
   grootte en een knop -- daar wil je langs kunnen scannen, en een raster van
   kaarten dwingt je oog steeds terug naar links. De accentbalk staat op de
   categoriekop, niet op elke regel, want anders leest de lijst als tientallen
   losse blokken.
   --------------------------------------------------------------------------- */

.file-list-intro {
    max-width: 72ch;
    color: var(--ispweb-muted);
}

.file-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin: 1.5rem 0 0.5rem;
}

.file-count {
    margin: 0;
    color: var(--ispweb-muted);
}

.file-filter {
    flex: 1 1 16rem;
    max-width: 24rem;
    margin-left: auto;
}

.file-filter-input {
    width: 100%;
    padding: 0.6rem 1rem;
    border: 1px solid rgb(0 0 0 / 12%);
    border-radius: var(--ispweb-radius);
    background: #fff;
    font: inherit;
}

.file-filter-input:focus-visible {
    outline: 2px solid var(--ispweb-secondary);
    outline-offset: 1px;
    border-color: var(--ispweb-secondary);
}

.file-group + .file-group {
    margin-top: 2.5rem;
}

.file-group-title {
    margin: 0 0 0.25rem;
    padding-top: 0.85rem;
    font-size: 1.35rem;
    font-weight: 700;
    color: var(--ispweb-primary);
    /* De vier accenten als streep boven de kop -- dezelfde band als boven de
       voettekst en op de productkaarten. */
    background-image: var(--ispweb-accent-bar);
    background-repeat: no-repeat;
    background-size: 7rem 3px;
    background-position: top left;
}

.file-subgroup {
    margin-top: 1.5rem;
    padding-left: 1rem;
    border-left: 2px solid var(--ispweb-light-blue);
}

.file-subgroup-title {
    margin: 0 0 0.5rem;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--ispweb-primary);
}

.file-group-description {
    margin: 0 0 0.75rem;
    color: var(--ispweb-muted);
    max-width: 72ch;
}

.file-list {
    margin: 0.75rem 0 0;
}

/* Alles op één lijn: de ring, de naam en de knop delen dezelfde middellijn, en
   de tekst eronder lijnt uit met de naam erboven. Met flex-start zakte de ring
   naar de bovenkant van een regel en de knop naar het midden van de kaart, wat
   bij elke rij een andere hoogte gaf. */
.file-item {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.9rem 1rem;
    border-radius: var(--ispweb-radius);
    border: 1px solid rgb(0 0 0 / 6%);
    background: #fff;
}

.file-item + .file-item {
    margin-top: 0.5rem;
}

.file-item:hover {
    border-color: rgb(0 0 0 / 14%);
    background: var(--ispweb-light-blue);
}

/* De extensie in een ring van de vier accenten -- dezelfde vorm als de
   contactknoppen: vier harde stops, want een verlopende conic-gradient wordt
   modder op drie pixels breed. Per rij een kwartslag verder, zodat de kleur
   bovenaan door de lijst heen wandelt en het raster "geordend" zegt in plaats
   van "gerangschikt". */
.file-badge {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    padding: 2px;
    box-sizing: border-box;
    border-radius: 50%;
    background: conic-gradient(
        from calc(45deg + var(--ring-rotate, 0deg)),
        var(--ispweb-accent-red) 0 25%,
        var(--ispweb-accent-yellow) 25% 50%,
        var(--ispweb-accent-green) 50% 75%,
        var(--ispweb-accent-blue) 75% 100%
    );
}

/* De witte schijf maakt er een ring van in plaats van een gevulde cirkel. */
.file-badge-inner {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    background: #fff;
    color: var(--ispweb-primary);
    font-size: .62rem;
    font-weight: 700;
    letter-spacing: .5px;
    /* PDF past, WEBP niet -- die krimpt liever dan dat hij de ring uitduwt. */
    overflow: hidden;
}

.file-item:nth-child(4n+2) .file-badge { --ring-rotate: 90deg; }
.file-item:nth-child(4n+3) .file-badge { --ring-rotate: 180deg; }
.file-item:nth-child(4n+4) .file-badge { --ring-rotate: 270deg; }

.file-body {
    flex: 1 1 auto;
    min-width: 0;
}

.file-name {
    display: block;
    font-weight: 700;
    color: var(--ispweb-primary);
    text-decoration: none;
    /* Bestandsnamen hebben geen plek om te breken en zijn soms erg lang. */
    overflow-wrap: anywhere;
}

.file-name:hover,
.file-name:focus-visible {
    color: var(--ispweb-secondary);
    text-decoration: underline;
}

.file-version {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0 0.4rem;
    border-radius: 0.6rem;
    background: var(--ispweb-light-blue);
    color: var(--ispweb-muted);
    font-size: 0.75rem;
    font-weight: 600;
    vertical-align: middle;
}

.file-description {
    display: block;
    margin-top: 0.15rem;
    color: var(--ispweb-muted);
    font-size: 0.95rem;
}

.file-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 0.35rem;
    color: var(--ispweb-muted);
    font-size: 0.85rem;
}


/* De controlesom dicht: wie hem nodig heeft weet dat hij bestaat, wie een PDF
   komt halen heeft niets aan vierenzestig tekens hex onder elke regel. */
.file-sums {
    margin-top: 0.5rem;
    font-size: 0.85rem;
}

.file-sums > summary {
    cursor: pointer;
    color: var(--ispweb-muted);
    width: max-content;
}

.file-sums dl {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.25rem 0.75rem;
    margin: 0.5rem 0 0;
}

.file-sums dt {
    color: var(--ispweb-muted);
    font-weight: 600;
}

.file-sums dd {
    margin: 0;
    min-width: 0;
}

.file-sums code {
    font-size: 0.8rem;
    color: var(--ispweb-text);
    overflow-wrap: anywhere;
}

.file-copy {
    margin-left: 0.5rem;
    padding: 0 0.5rem;
    border: 1px solid rgb(0 0 0 / 12%);
    border-radius: 0.4rem;
    background: #fff;
    color: var(--ispweb-muted);
    font-size: 0.75rem;
    cursor: pointer;
}

.file-copy:hover {
    border-color: var(--ispweb-secondary);
    color: var(--ispweb-secondary);
}

/* De downloadknop rechts, en op een telefoon eronder in plaats van erlangs:
   naast een naam van drie regels is een knop van 2,5rem een vreemde punt. */
.file-download {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    height: 2.5rem;
    padding: 0 1rem;
    border-radius: var(--ispweb-radius);
    background: var(--ispweb-green, #a0c92d);
    color: #fff;
    font-weight: 700;
    font-size: .9rem;
    text-decoration: none;
    white-space: nowrap;
}

.file-download:hover,
.file-download:focus-visible {
    background: var(--ispweb-primary);
    color: #fff;
}

.file-empty {
    color: var(--ispweb-muted);
}

@media (max-width: 575.98px) {
    .file-item {
        flex-wrap: wrap;
    }

    .file-download {
        width: 100%;
    }

    /* Op een telefoon telt de ruimte zwaarder dan het onderscheid tussen
       openen en downloaden -- de knop staat er dan over de volle breedte, en
       dat zegt genoeg. */
    .file-download-label {
        position: static;
    }
}

/* ---------------------------------------------------------------------------
   De pagina achter een verzendlink

   Eén paneel, midden op de pagina, met precies drie dingen: wat het is, hoe
   groot het is, en een knop. Alles wat er verder op zou kunnen staan leidt af
   van de enige handeling die er te doen valt.
   --------------------------------------------------------------------------- */

/* Het paneel staat verticaal in het midden van het scherm.

   Deze pagina heeft één ding te melden en één knop; hem bovenaan plakken laat
   het onderste twee derde leeg. 100dvh en niet 100vh: op een telefoon telt vh
   de adresbalk mee, waardoor het paneel er net onder wegvalt. Een min-height,
   dus een lange melding groeit gewoon door in plaats van af te kappen. */
.share-section {
    display: flex;
    align-items: center;
    min-height: calc(100dvh - 14rem);
    padding-top: 7rem;
    padding-bottom: 3rem;
}

/* De marges van de container zouden het midden weer scheeftrekken. */
.share-section > .container {
    margin-top: 0 !important;
    margin-bottom: 0 !important;
    width: 100%;
}

.share-panel {
    padding: 2rem;
    background: #fff;
    border-radius: var(--ispweb-radius);
    box-shadow: 0 2px 24px rgb(0 0 0 / 7%);
    background-image: var(--ispweb-accent-bar);
    background-repeat: no-repeat;
    background-size: 100% 3px;
    background-position: top left;
}

.share-panel h1 {
    margin: 0 0 1.25rem;
    font-size: clamp(1.5rem, 4vw, 2rem);
    color: var(--ispweb-primary);
}

.share-file {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1rem;
    border-radius: var(--ispweb-radius);
    background: var(--ispweb-light-blue);
}

/* Op deze pagina staat de ring op een lichtblauw vlak in plaats van op wit, dus
   de witte schijf erbinnen mag iets groter zijn om hem te laten uitkomen. */
.share-file .file-badge {
    width: 3rem;
    height: 3rem;
}

.share-file-body {
    min-width: 0;
}

.share-file-name {
    display: block;
    color: var(--ispweb-primary);
    /* Een bestandsnaam heeft geen plek om te breken en mag het paneel niet
       breder maken dan het scherm. */
    overflow-wrap: anywhere;
}

.share-file-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 0.2rem;
    color: var(--ispweb-muted);
    font-size: 0.85rem;
}

.share-message {
    margin: 1.25rem 0 0;
    padding-left: 1rem;
    border-left: 3px solid var(--ispweb-light-blue);
    color: var(--ispweb-text);
}

.share-download {
    margin-top: 1.5rem;
}

.share-terms {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.35rem 1.5rem;
    margin: 1.5rem 0 0;
    padding-top: 1.25rem;
    border-top: 1px solid rgb(0 0 0 / 8%);
    font-size: 0.9rem;
}

.share-terms dt {
    color: var(--ispweb-muted);
}

.share-terms dd {
    margin: 0;
    font-weight: 600;
}

.share-note {
    margin: 1rem 0 0;
    color: var(--ispweb-muted);
    font-size: 0.85rem;
}

/* Een link die niet meer werkt zegt één ding, wat er ook mis mee is. Geen
   rode alarmkleur: er is niets stuk, de link is gewoon op. */
.share-gone h1 {
    color: var(--ispweb-text);
}

/* Het keuzevakje per bestand. Verborgen tot ispweb.js hem tevoorschijn haalt --
   zonder JavaScript is elk bestand nog één klik weg, alleen samen inpakken niet. */
.file-pick {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
}

.file-pick[hidden] {
    display: none;
}

.file-pick input {
    width: 1.15rem;
    height: 1.15rem;
    accent-color: var(--ispweb-primary);
    cursor: pointer;
}

/* De balk verschijnt zodra er iets is aangevinkt en blijft onderin beeld staan:
   hij hoort bij wat je selecteert, en na een lange lijst terugscrollen om de
   knop te zoeken is waar dit soort pagina's aan lijdt. */
.file-bulk {
    position: sticky;
    bottom: 1rem;
    z-index: 3;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .75rem;
    margin: 1rem 0;
    padding: .85rem 1.1rem;
    border-radius: var(--ispweb-radius);
    background: var(--ispweb-primary);
    color: #fff;
    box-shadow: 0 .5rem 1.5rem rgb(0 0 0 / 20%);
}

.file-bulk[hidden] {
    display: none;
}

.file-bulk .btn {
    margin-left: auto;
}

.file-bulk .btn[disabled] {
    opacity: .5;
    pointer-events: none;
}

.file-bulk-size {
    opacity: .8;
}

/* Te groot om in te pakken: geel, niet rood. Er is niets stuk -- de selectie
   past gewoon niet, en de losse downloads werken nog. */
.file-bulk-size.is-over {
    color: var(--ispweb-accent-yellow);
    opacity: 1;
    font-weight: 700;
}

.file-bulk-clear {
    padding: 0;
    border: 0;
    background: none;
    color: #fff;
    text-decoration: underline;
    cursor: pointer;
    font: inherit;
}

/* De categorie als <details>: dicht bij het openen van de pagina, zodat de
   lijst een inhoudsopgave is in plaats van een rol. Geen JavaScript nodig --
   dezelfde afweging als bij de storingen op de homepage. */
.file-group-summary {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .5rem 1rem;
    padding: .85rem 0;
    cursor: pointer;
    list-style: none;
    border-bottom: 1px solid rgb(0 0 0 / 8%);
}

/* Het eigen driehoekje van de browser weg; hieronder komt er een van onszelf. */
.file-group-summary::-webkit-details-marker {
    display: none;
}

.file-group-summary::before {
    content: "";
    width: .5rem;
    height: .5rem;
    flex: 0 0 auto;
    border-right: 2px solid var(--ispweb-primary);
    border-bottom: 2px solid var(--ispweb-primary);
    transform: rotate(-45deg);
    transition: transform .15s ease;
}

details[open] > .file-group-summary::before {
    transform: rotate(45deg);
}

.file-group-summary:focus-visible {
    outline: 2px solid var(--ispweb-secondary);
    outline-offset: 2px;
}

.file-group-count {
    margin-left: auto;
    color: var(--ispweb-muted);
    font-size: .9rem;
}

details.file-group > .file-list,
details.file-group > .file-subgroup {
    margin-top: 1rem;
}

/* De accentbalk stond op de kop; die zit nu op de samenvatting, want dat is de
   regel die je ziet als alles dicht staat. */
details.file-group .file-group-title {
    padding-top: .5rem;
    font-size: 1.2rem;
    font-weight: 700;
    color: var(--ispweb-primary);
    background-image: var(--ispweb-accent-bar);
    background-repeat: no-repeat;
    background-size: 5rem 3px;
    background-position: top left;
}

/* --------------------------------------------------------------------------
   Locatiekaart

   Een getekende kaart, geen ingesloten kaartdienst. Alle kleur zit hier en niet
   in de tekening: dan volgt de kaart de huisstijl en het seizoensthema zonder
   dat er een tweede bestand bijgehouden hoeft te worden.
   -------------------------------------------------------------------------- */

.locations-map svg {
    display: block;
    width: 100%;
    height: auto;
    /* Het "water" is de achtergrond van het paneel: een aparte rechthoek
       tekenen zou een kleur zijn die niet meebeweegt met de sectie. */
    /* Zee in lichtblauw, land in tinten, met grenzen ertussen.

       Twee uitersten zijn geprobeerd en allebei fout. Alles in een lichte
       blauwgrijze tint was een waas waarin de kustlijn verdween. Een diepblauwe
       zee met wit land was wel duidelijk, maar een zwaar donker blok op een
       verder lichte pagina -- en zonder tinten zag je geen landen, alleen
       vormen.

       Dit zit ertussenin: licht genoeg voor deze site, met genoeg verschil om
       een kaart te zijn in plaats van een silhouet. */
    background: #deeef8;
    border-radius: 0.75rem;
}

/* Een landvlak, geen landkaart: alle landen dezelfde vulling en de streek in
   diezelfde kleur, zodat de grenzen tussen buurlanden verdwijnen en er een
   silhouet overblijft. De streek staat er alleen om haarlijntjes tussen twee
   veelhoeken dicht te leggen. Landsgrenzen zijn ruis op een kaart die over onze
   locaties gaat en niet over Europa. */
/* De landen.

   Dekkende kleuren, geen doorschijnende: met alpha stapelt de streek van twee
   buurlanden op elkaar en krijg je een lijn waar je hem niet wilt.

   De grens is een echte lijn en geen kleurverschil. Zes tinten in een vaste
   cyclus betekent dat twee buren dezelfde kunnen krijgen -- Duitsland en
   Tsjechie doen dat -- en dan is die lijn het enige dat ze uit elkaar houdt. Een
   tint per land met de hand toewijzen zou een lijst landnamen in deze stylesheet
   betekenen die bijgewerkt moet worden zodra het beeldvenster verschuift. */
.locations-land path {
    stroke: #9fc2dc;
    stroke-width: 1.1;
    stroke-linejoin: round;
}

.locations-tint-0 { fill: #ffffff; }
.locations-tint-1 { fill: #f7faf5; }
.locations-tint-2 { fill: #eef4f9; }
.locations-tint-3 { fill: #fcfdfa; }
.locations-tint-4 { fill: #f2f7fb; }
.locations-tint-5 { fill: #f6f8f3; }

/* De stip: een kern met een doorschijnende halo eromheen, zodat hij op het
   land opvalt zonder een gat te lijken. */
.locations-point .locations-core {
    fill: currentColor;
    /* Een donkere ring in plaats van een witte: de stippen staan op wit land,
       dus wit om wit is geen rand. Dit maakt er een speld van in plaats van een
       gat, en houdt twee stippen die elkaar raken uit elkaar. */
    stroke: var(--ispweb-primary);
    stroke-width: 3;
}

.locations-point .locations-halo {
    fill: currentColor;
    opacity: 0.28;
}

.locations-count {
    fill: #fff;
    font-size: 11px;
    font-weight: 700;
    text-anchor: middle;
}

.locations-point-blue  { color: var(--ispweb-accent-blue); }
.locations-point-green { color: var(--ispweb-accent-green); }
.locations-point-red   { color: var(--ispweb-accent-red); }

.locations-dot {
    display: inline-block;
    width: 0.7rem;
    height: 0.7rem;
    margin-right: 0.4rem;
    border-radius: 50%;
    vertical-align: baseline;
}

.locations-dot-blue  { background: var(--ispweb-accent-blue); }
.locations-dot-green { background: var(--ispweb-accent-green); }
.locations-dot-red   { background: var(--ispweb-accent-red); }

.locations-legend li {
    margin-bottom: 0.35rem;
}

/* De namen onder de kaart: hetzelfde als de <title> op elke stip, maar dan
   leesbaar op een telefoon -- waar hover niet bestaat -- en voor een
   schermlezer. */
.locations-names {
    margin-top: 0.9rem;
    font-size: 0.9rem;
    color: var(--ispweb-muted);
}

.locations-names-group {
    display: inline-block;
    margin-right: 1.25rem;
}

.locations-address {
    margin-top: 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid rgba(2, 67, 125, 0.14);
}

.locations-address-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
}

/* De kaart onder de tekst op een telefoon: een kaart van 1000 breed naast een
   kolom van 300 is geen van beide leesbaar. */
@media (max-width: 991.98px) {
    .locations-map {
        margin-top: 1.5rem;
    }
}

/* Het kaartlinkje achter het postadres: mee met de regel, niet erboven. Het is
   een aanvulling op het adres en geen tweede boodschap. */
.footer-map-link {
    margin-left: 0.4rem;
    font-weight: 400;
    text-decoration: underline;
    white-space: nowrap;
}

/* De acties onder het postadres.

   Eigen klassen en niet .btn: site.css zet `.btn { color: #fff }` zonder
   voorwaarde, dus btn-outline-primary levert daar witte tekst op een witte
   achtergrond -- een leeg kader. De echte knoppen van deze site (.btn-secondary
   en .btn-tertiary) plakken er bovendien een pijltje achter, wat op "adres
   kopieren" niet klopt. */
.locations-action {
    display: inline-block;
    padding: 0.35rem 0.9rem;
    border: 1px solid var(--ispweb-primary);
    border-radius: 0.35rem;
    background: transparent;
    color: var(--ispweb-primary);
    font-size: 0.95rem;
    font-weight: 600;
    line-height: 1.4;
    text-decoration: none;
    transition: 150ms background-color ease-in-out, 150ms color ease-in-out;
}

.locations-action:hover,
.locations-action:focus-visible {
    background: var(--ispweb-primary);
    color: #fff;
}

/* De tweede actie is een link met een randje eromheen: hij hoort erbij, maar
   kopieren is wat hier het vaakst gebeurt. */
.locations-action-quiet {
    border-color: rgba(2, 67, 125, 0.25);
    font-weight: 500;
}

/* De ruimte onder de vaste header voor het locatieblok.

   Het staat bovenaan de contactpagina en de header ligt eroverheen: die is
   onuitgeklapt ongeveer 140px hoog, dus de 128px die `pt-xl-6 my-6` oplevert is
   net te weinig en de kop begint achter het logo. Hier staat het expliciet, want
   net-aan is precies de reden dat het de vorige keer misging.

   Op de container, niet op een kolom: de rij is verticaal gecentreerd, en op een
   kolom zou centreren de helft ervan meteen teruggeven. */
@media (min-width: 992px) {
    .locations-section > .container {
        padding-top: 10rem !important;
    }
}

/* "op afspraak" achter het adres in de footer: het hoort erbij maar mag niet
   even hard roepen als het adres zelf. */
.footer-note {
    font-weight: 400;
    opacity: 0.75;
}

/* --------------------------------------------------------------------------
   De kaart bedienen

   Twee vaste standen in plaats van vrij zoomen -- zie ispweb.js voor waarom --
   plus een land dat oplicht als je eroverheen gaat, en namen die hun stip
   aanwijzen.
   -------------------------------------------------------------------------- */

.locations-map {
    position: relative;
}

/* De knoppen liggen op de kaart, rechtsboven: daar is water en dus nooit iets
   dat ze afdekken. Ze zijn `hidden` in de HTML en ispweb.js onthult ze --
   zonder JavaScript doen ze niets en dan horen ze er niet te staan. */
.locations-zoom {
    position: absolute;
    top: .75rem;
    right: .75rem;
    z-index: 1;
    display: flex;
    gap: .25rem;
    padding: .2rem;
    border-radius: .4rem;
    background: rgba(255, 255, 255, .9);
    box-shadow: 0 1px 3px rgba(2, 67, 125, .2);
}

.locations-zoom-btn {
    border: 0;
    border-radius: .3rem;
    padding: .25rem .7rem;
    background: transparent;
    color: var(--ispweb-primary);
    font-size: .85rem;
    font-weight: 600;
    line-height: 1.5;
    cursor: pointer;
}

.locations-zoom-btn.is-active {
    background: var(--ispweb-primary);
    color: #fff;
}

/* Het schuiven en schalen loopt door een overgang, anders springt de kaart en
   ben je kwijt waar je was. */
.locations-land,
.locations-point {
    transition: transform 400ms ease;
}

/* Een land aanwijzen licht het op. Puur CSS: er is niets te onthouden en niets
   te berekenen, dus daar hoort geen JavaScript bij. */
.locations-land path {
    transition: fill 150ms ease;
}

.locations-land path:hover {
    fill: #cfe3f3;
}

/* De landen waar wij staan hebben hun eigen kleur. Dat is wat de kaart iets
   laat zeggen in plaats van tonen: je ziet in een oogopslag welke landen het
   zijn, zonder de stippen af te gaan. */
.locations-land path.is-ours {
    fill: #e4f0dc;
}

.locations-land path.is-ours:hover {
    fill: #d5e8c8;
}

/* Een stip die is aangewezen moet je meteen vinden.

   Hij werd alleen iets groter, en dat is op een kaart met acht stippen te weinig
   om je oog naartoe te trekken -- zeker als je met de muis onderaan in de lijst
   staat en de stip ergens bovenin ligt. Dus pulseert hij, zolang je hem
   aanwijst.

   Kloppen mag hier, terwijl het elders op deze site niet zou mogen: dit is geen
   versiering die uit zichzelf begint, maar het antwoord op iets dat de bezoeker
   op dat moment doet. Laat je los, dan stopt het.

   Geschaald en niet met een r-animatie: `r` is als CSS-eigenschap pas laat
   ondersteund, terwijl transform overal werkt. `transform-box: fill-box` zet het
   draaipunt op de cirkel zelf -- zonder dat schaalt hij vanaf de linkerbovenhoek
   van de hele tekening en vliegt de stip weg. */
.locations-point .locations-core,
.locations-point .locations-halo {
    transform-box: fill-box;
    transform-origin: center;
    transition: transform 150ms ease, opacity 150ms ease;
}

.locations-point.is-active .locations-core {
    transform: scale(1.35);
    stroke: #fff;
    stroke-width: 4;
}

.locations-point.is-active .locations-halo {
    opacity: .5;
    animation: locations-ping 1.2s ease-out infinite;
}

@keyframes locations-ping {
    0%   { transform: scale(0.8); opacity: .55; }
    70%  { transform: scale(2.1); opacity: 0; }
    100% { transform: scale(2.1); opacity: 0; }
}

/* Wie om minder beweging vraagt, krijgt geen beweging -- maar wel het antwoord.
   De stip blijft dan permanent groter en met een dikke halo, zodat hij nog
   steeds als eerste opvalt. */
@media (prefers-reduced-motion: reduce) {
    .locations-point.is-active .locations-halo {
        animation: none;
        transform: scale(1.6);
        opacity: .55;
    }
}

/* De namen onder de kaart zijn de bediening ervan. Ze zien eruit als iets waar
   je op kunt wijzen, en met de tab-toets gebeurt hetzelfde als met de muis. */
.locations-name {
    cursor: default;
    border-radius: .2rem;
    padding: 0 .15rem;
    transition: background-color 150ms ease, color 150ms ease;
}

.locations-name:hover,
.locations-name.is-active,
.locations-name:focus-visible {
    background: var(--ispweb-primary);
    color: #fff;
    outline: none;
}

.locations-sep {
    opacity: .5;
    margin: 0 .15rem;
}

/* De landnaam bij het aanwijzen.

   Linksboven, tegenover de zoomknoppen, zodat ze elkaar nooit afdekken. Vaste
   plek en geen tooltip die de muis volgt: dan springt hij over de kaart en dekt
   hij precies het land af waar je naar kijkt. */
.locations-label {
    position: absolute;
    top: .75rem;
    left: .75rem;
    z-index: 1;
    margin: 0;
    padding: .3rem .75rem;
    border-radius: .4rem;
    background: rgba(255, 255, 255, .92);
    box-shadow: 0 1px 3px rgba(2, 67, 125, .2);
    color: var(--ispweb-primary);
    font-size: .85rem;
    font-weight: 600;
    pointer-events: none;
}

/* Aanwijsbaar land: de muisaanwijzer zegt dat er iets te zien is. */
.locations-land path {
    cursor: default;
}

/* --------------------------------------------------------------------------
   Aanmelden voor een verzendlijst

   Eén vormgeving voor beide plekken: in het storingenblok en als los blok. Het
   formulier is één regel -- adres en knop -- want dat is alles wat er gevraagd
   wordt, en elk veld erbij is een reden om het niet in te vullen.
   -------------------------------------------------------------------------- */

.subscribe {
    margin-top: 1.75rem;
    padding-top: 1.5rem;
    border-top: 1px solid rgba(2, 67, 125, 0.14);
}

.subscribe-section .subscribe {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}

.subscribe-intro {
    margin-bottom: 0.75rem;
}

.subscribe-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    max-width: 34rem;
}

.subscribe-section .subscribe-row {
    margin: 0 auto;
}

.subscribe-row input[type="email"] {
    flex: 1 1 16rem;
    min-width: 0;
    padding: 0.55rem 0.9rem;
    border: 1px solid rgba(2, 67, 125, 0.25);
    border-radius: 0.35rem;
    font-size: 1rem;
}

.subscribe-small {
    margin: 0.6rem 0 0;
    font-size: 0.85rem;
    color: var(--ispweb-muted);
}

/* Het honeypot-veld. Uit beeld en niet `display: none`: een bot dat de
   stylesheet leest, slaat een verborgen veld over -- eentje dat het niet leest,
   vult hem in, en dat is precies wat wij willen weten. */
.subscribe-trap {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.subscribe-done {
    margin: 0;
    padding: 0.75rem 1rem;
    border-radius: 0.4rem;
    background: rgba(160, 201, 45, 0.14);
    border-left: 4px solid var(--ispweb-accent-green);
}

/* De pagina na het klikken op een link uit de mail. */
.subscribe-result h1 {
    margin-bottom: 0.75rem;
}

.subscribe-result.is-problem h1 {
    color: var(--ispweb-accent-red);
}

/* Het beschikbaarheidscijfer onder de storingen. Het getal groot, de grens
   eronder klein -- maar er wél, want een percentage zonder wat het meet is een
   marketingcijfer. */
.status-uptime {
    margin: 1.5rem 0 0;
    font-size: 1.05rem;
}

.status-uptime strong {
    font-size: 1.35rem;
    color: var(--ispweb-primary);
}

.status-uptime-note {
    display: block;
    margin-top: 0.15rem;
    font-size: 0.85rem;
    color: var(--ispweb-muted);
}

/* Het aanmeldformulier onder de storingen, ingeklapt.

   Het is een aanbod en geen vraag: wie hier komt wil weten of er een storing is.
   Achter een regel waar je op klikt staat het er voor wie het zoekt, en niet in
   de weg voor wie het niet zoekt. */
/* Witruimte als scheiding, geen lijn. Boven de regel staat een uitspraak over
   beschikbaarheid, eronder een aanbod -- dat zijn twee dingen, en een lege regel
   zegt dat net zo goed als een streep, zonder er een streep bij te tekenen. */
.status-subscribe {
    margin-top: 2.25rem;
}

.status-subscribe > summary {
    cursor: pointer;
    font-weight: 600;
    color: var(--ispweb-primary);
    list-style: none;
}

.status-subscribe > summary::-webkit-details-marker {
    display: none;
}

/* Een eigen pijltje, dat meedraait: het standaarddriehoekje verschilt per
   browser en valt naast deze typografie uit de toon. */
.status-subscribe > summary::before {
    content: "";
    display: inline-block;
    width: .5rem;
    height: .5rem;
    margin-right: .55rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(-45deg);
    transition: transform 150ms ease;
    vertical-align: middle;
}

.status-subscribe[open] > summary::before {
    transform: rotate(45deg);
}

.status-subscribe .subscribe {
    margin-top: 1rem;
    padding-top: 0;
    border-top: 0;
}

/* --------------------------------------------------------------------------
   Tabbladen

   Twee radioknoppen en :checked, geen JavaScript. Werkt met scripting uit, met
   het toetsenbord (pijltjes, zoals radioknoppen horen te doen) en in een
   schermlezer, zonder dat daar iets voor bedraad hoeft te worden.

   De invoervelden zelf staan uit beeld in plaats van op display:none -- een
   verborgen radioknop is voor het toetsenbord niet meer te bereiken, en dan is
   de tabstrip alleen met de muis te bedienen.
   -------------------------------------------------------------------------- */

.tabset {
    display: flex;
    flex-wrap: wrap;
}

.tabset-input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
}

.tabset-label {
    order: -1;
    padding: .7rem 1.4rem;
    border-bottom: 3px solid transparent;
    color: var(--ispweb-primary);
    font-weight: 600;
    cursor: pointer;
    transition: border-color 150ms ease, color 150ms ease;
}

.tabset-label:hover {
    border-bottom-color: rgba(2, 67, 125, 0.25);
}

.tabset-input:checked + .tabset-label {
    border-bottom-color: var(--ispweb-accent-green);
}

/* De omranding van het toetsenbord hoort zichtbaar te zijn op het label, want
   de radioknop zelf staat uit beeld. */
.tabset-input:focus-visible + .tabset-label {
    outline: 2px solid var(--ispweb-accent-blue);
    outline-offset: 2px;
}

.tabset-panel {
    display: none;
    width: 100%;
    padding-top: 1.5rem;
    border-top: 1px solid rgba(2, 67, 125, 0.14);
}

/* Het eerste paneel hoort bij de eerste radioknop, het tweede bij de tweede.
   Vandaar de telling: ~ kijkt vooruit, dus beide panelen staan achter beide
   knoppen en alleen de volgorde onderscheidt ze. */
.tabset-input:first-of-type:checked ~ .tabset-panel:first-of-type,
.tabset-input:last-of-type:checked ~ .tabset-panel:last-of-type {
    display: block;
}

/* In een tabblad hoeft het formulier geen eigen scheiding meer: het paneel
   heeft er al een. */
.tabset-panel .subscribe {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}

/* Ruimte onder de formulierensecties.

   Het paneel liep tegen de accentbalk boven de footer aan, en dan lijkt het
   afgesneden in plaats van afgerond. Op de sectie en niet op het paneel: de
   ruimte hoort bij waar het blok eindigt, niet bij het formulier -- die staat op
   meer plekken. */
.contact-tabs-section > .container,
.subscribe-section > .container {
    padding-bottom: 3rem;
}

/* De Turnstile-widget in een aanmeldformulier.

   Staat er alleen als Cloudflare hem tekent -- bij een sitekey van het type
   Invisible gebeurt dat nooit, bij Managed altijd. Ruimte eromheen zodat hij in
   dat tweede geval niet tegen de tekst aan plakt. */
.subscribe-form .cf-turnstile {
    margin-top: .85rem;
}

/* --------------------------------------------------------------------------
   De groene knop krijgt zijn eigen tekstkleur terug
   -------------------------------------------------------------------------- */

/* PageSpeed noemde drie elementen met te weinig contrast, en alle drie waren
   het dezelfde knop: "Onze producten" boven aan de homepage, dezelfde knop in
   het productenblok, en "Check >>" onder de domeincheck.

   Het is geen ontwerpkeuze maar een ongeluk, en het ontwerp had het zelf al
   goed. site.css:3058 zet voor deze knop:

       .btn-tertiary { --bs-btn-color: #000; }

   Zwart op groen dus. Maar site.css:10728 zet daarna, zonder voorwaarde:

       .btn { color: #fff; }

   Bootstrap leest die kleur uit `var(--bs-btn-color)` op `.btn` zelf, en die
   twee regels wegen even zwaar -- dus wint de laatste, en wordt alles wit. Op
   de blauwe knop klopt dat; op de groene niet. Wit op #A0C92D is 1,9:1, waar
   4,5:1 de eis is. Het zwart dat er hoorde te staan haalt 11:1.

   Dit herstelt dus wat er stond, en verzint geen nieuwe kleur. Dezelfde
   valkuil staat hierboven al beschreven bij .locations-action, dat om deze
   reden geen .btn gebruikt.

   `.btn.btn-tertiary` is een klasse zwaarder dan de regel die het overschreef,
   en de drie toestanden staan er los bij omdat site.css ook `.btn:hover` apart
   op wit zet. */
.btn.btn-tertiary {
    color: var(--bs-btn-color);
}

.btn.btn-tertiary:hover,
.btn.btn-tertiary:focus {
    color: var(--bs-btn-hover-color);
}

.btn.btn-tertiary:active {
    color: var(--bs-btn-active-color);
}

/* Het pijltje moet mee.

   site.css tekent het als een SVG in een data-URL met `fill='%23ffffff'` er
   hard in, gedeeld met .btn-secondary -- die is blauw met witte tekst, dus
   daar klopt het wel. Hier zou een wit pijltje naast een zwart woord blijven
   staan, wat er meer uitziet als een fout dan het origineel deed.

   Dezelfde tekening met een zwarte vulling; `!important` omdat de regel in
   site.css die ook draagt. */
.btn.btn-tertiary span::after {
    content: url("data:image/svg+xml; utf8, <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 19 13.12'><path d='m18.77,5.99h0L13.19.26c-.34-.35-.9-.35-1.24,0-.34.35-.34.92,0,1.28l4.01,4.12H.88c-.49,0-.88.4-.88.9s.39.9.88.9h15.08l-4.01,4.12c-.34.35-.34.92,0,1.28.34.35.9.35,1.24,0l5.58-5.73c.31-.31.31-.82,0-1.14Z' fill='%23000000'/></svg>") !important;
}
