/* SelfAdds — stylesheet.
 *
 * Same starting conventions as saadmin's style.css: a blanket reset, the system
 * font stack, and plain ids/classes rather than a framework. Kept deliberately
 * small; add sections as screens arrive rather than up front. */

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    background: white;
    color: #333;
}

/* Shared helpers, here because the first screen will want them and they are the
   two saadmin reaches for most. */
.hidden {
    display: none !important;
}

.muted {
    color: #888;
    font-size: 14px;
}

/* ---- Buttons --------------------------------------------------------------
   A shared .btn with two variants, UNLIKE saadmin — which styles every button
   by id and has paid for it twice (a renamed id silently drops its appearance,
   and a new button renders as a browser default with nothing failing). Starting
   with a class here means a new button is styled by default. */
.btn {
    padding: 8px 16px;
    border-radius: 6px;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    border: 1px solid transparent;
}

/* SelfAdds blue — the same #295A94 the apps use as their brand colour. */
.btn-primary {
    background: #295A94;
    color: #fff;
}

.btn-primary:hover {
    background: #1f4775;
}

.btn-secondary {
    background: #fff;
    color: #295A94;
    border-color: #295A94;
}

.btn-secondary:hover {
    background: #f2f6fb;
}

/* ---- Header --------------------------------------------------------------
   Pinned to the top while everything below it scrolls.

   `sticky`, not `fixed`, and the difference matters here: a fixed header leaves
   the normal flow, so the banner would slide up underneath it and the page
   would need a padding-top equal to the header's height — a magic number that
   silently breaks the first time the header gains a row or the font changes.
   Sticky keeps the header in flow, so the content below starts beneath it on
   its own and stays correct at any header height.

   An opaque background is REQUIRED, not cosmetic: the default is transparent,
   so the banner photo would scroll through the header and make the brand and
   buttons unreadable. */
#app-header {
    position: sticky;
    top: 0;
    /* Above page content, below the cookie banner's 100 — the consent dialog
       must never end up behind the header. */
    z-index: 50;
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 16px 24px;
    border-bottom: 1px solid #e0e0e0;
}

#app-header h1 {
    font-size: 20px;
    font-weight: 600;
}

#auth-actions {
    display: flex;
    gap: 10px;
}

/* ---- Row 2: sub-bar ------------------------------------------------------ */
#app-subbar {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    padding: 10px 24px;
    border-bottom: 1px solid #efefef;
}

/* Quiet on purpose. It answers one question — "is this the build that just went
   out, or am I looking at a cached one" — and should never compete with the
   page for attention. Tabular figures so the digits do not shift width as the
   clock ticks past the next stamp. */
.app-version {
    font-size: 12px;
    color: #aaa;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ---- Row 3: banner -------------------------------------------------------
   Source: Unsplash photo 1441986300917-64674bd600d8, served from images/
   rather than the Unsplash CDN. Self-hosted on purpose: no third-party request
   on first paint, nothing to break if the CDN changes a URL, and no external
   host to allow once this page gets a Content-Security-Policy. Unsplash
   License — free for commercial use, attribution not required.

   10cm is the height that was asked for and CSS means it literally: 1cm is
   defined as 96px/2.54, so this is ~378px on every display regardless of the
   physical screen. */
#app-banner {
    width: 100%;
    height: 10cm;
    overflow: hidden;
}

#app-banner img {
    width: 100%;
    height: 100%;
    /* The photo is 1920x1281 (3:2) and this box is far wider than it is tall,
       so the image MUST be cropped rather than squashed. `cover` fills the box
       and keeps the aspect ratio; the width/height attributes on the tag give
       the browser that ratio up front so the banner does not jump into place
       once the file arrives. */
    object-fit: cover;
    /* Crop towards the middle: the shelves and the display table are the
       subject, and a top or bottom crop loses one of them. */
    object-position: center;
    display: block;
}

/* A fixed 10cm is most of a short phone screen. Scaled back rather than left
   to dominate the fold, since nothing above it is tall. */
@media (max-width: 520px) {
    #app-banner {
        height: 6cm;
    }
}

/* <main> itself is full width so a section inside it can run edge to edge —
   the stats ribbon does. The reading width lives on each SECTION instead, which
   is what lets one be contained and the next be full-bleed without any
   negative-margin tricks. */
#app-main {
    padding: 0;
}

/* ---- Our solutions ------------------------------------------------------- */
#solutions {
    /* The banner runs edge to edge, so the content below it needs a width of
       its own — three cards stretched across a 4K monitor read as three
       posters, not a row of options. */
    max-width: 1100px;
    margin: 0 auto;
    padding: 40px 24px;
}
.section-title {
    font-size: 24px;
    font-weight: 600;
    margin-bottom: 24px;
    text-align: center;
}

/* `1fr` three times, not `auto`: fr distributes the SPACE rather than fitting
   the content, so the columns stay equal however long one description grows.
   `auto` would let the wordiest card claim the most room, which is the usual
   way an "equidistant" row stops being equidistant after a copy change. */
.solution-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
}

.solution-card {
    border: 1px solid #e0e0e0;
    border-radius: 10px;
    padding: 24px;
    /* Equal heights come free from grid stretching the row; this only matters
       once the cards gain a button or link that should sit at the bottom. */
    display: flex;
    flex-direction: column;
    /* Both, and they do different jobs. `text-align` centres the lines WITHIN
       the heading and paragraph; `align-items` centres those boxes inside the
       card — which is what will keep a future button centred rather than
       stretched to the card's full width, since that is a flex column's
       default. */
    text-align: center;
    align-items: center;
}

.solution-card h3 {
    font-size: 20px;
    font-weight: 600;
    color: #295A94;
    margin-bottom: 10px;
}

.solution-card p {
    font-size: 15px;
    line-height: 1.6;
    color: #555;
}

/* One column well before the text gets cramped — three 15px paragraphs in a
   third of a tablet width is unreadable long before the layout actually
   breaks. */
@media (max-width: 760px) {
    .solution-grid {
        grid-template-columns: 1fr;
    }
}

/* ---- Stats ---------------------------------------------------------------
   Full-bleed, so the ribbon reaches both window edges. */
#stats {
    width: 100%;
}

/* The ribbon: a grey band separating the solutions above from this section,
   in the footer's grey so the page has one grey rather than two.
   A ::before rather than an element — it carries no meaning, and decoration
   that exists only in CSS is decoration a screen reader never has to skip. */
#stats::before {
    content: "";
    display: block;
    height: 44px;
    background: #f2f2f2;
    border-top: 1px solid #e3e3e3;
    border-bottom: 1px solid #e3e3e3;
}

.stats-inner {
    max-width: 1100px;
    margin: 0 auto;
    padding: 40px 24px 48px;
}

/* Three across, so the six fall into two rows. Same `1fr` reasoning as the
   solutions grid: the labels are different lengths ("employees" against "best
   in class products") and `auto` would size the columns to them.
   Three also lines the stats up with the three cards above, which is most of
   why the two sections read as one page rather than two. */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
}

.stat {
    border: 1px solid #e0e0e0;
    border-radius: 10px;
    padding: 32px 20px;
    text-align: center;
    /* Column + centred so the number and its label stack and stay centred
       whatever the label wraps to. */
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

.stat-number {
    font-size: 40px;
    font-weight: 700;
    color: #295A94;
    line-height: 1.1;
}

.stat-label {
    font-size: 15px;
    line-height: 1.4;
    color: #555;
}

/* Matches the solutions grid's breakpoint, so the two sections reflow at the
   same width instead of the page changing shape twice on the way down. */
@media (max-width: 760px) {
    .stats-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 460px) {
    .stats-grid {
        grid-template-columns: 1fr;
    }
}

/* ---- Footer --------------------------------------------------------------
   Full-bleed grey band, with the rows inside sharing #app-main's max-width so
   the footer text lines up with the content above it rather than drifting to
   the window edge on a wide screen. */
/* No margin-top: the stats section above ends with its own bottom padding, and
   adding a margin here too stacked the two into a gap wider than either. Each
   section owns the space below itself. */
#app-footer {
    background: #f2f2f2;
    border-top: 1px solid #e3e3e3;
    padding: 32px 24px 24px;
}

.footer-row {
    max-width: 1100px;
    margin: 0 auto;
}

.footer-row-main {
    display: flex;
    /* Brand hard left, follow-us block hard right. `align-items: flex-start`
       so the two align at the TOP — the right side is two stacked lines, and
       centring would float the brand down against its middle. */
    justify-content: space-between;
    align-items: flex-start;
    gap: 24px;
}

.footer-brand {
    font-size: 18px;
    font-weight: 600;
    color: #295A94;
}

.footer-social-label {
    display: block;
    font-size: 14px;
    font-weight: 600;
    color: #555;
    margin-bottom: 10px;
    /* The label sits over the icons, so it is right-aligned to match them. */
    text-align: right;
}

.footer-social-icons {
    display: flex;
    gap: 14px;
    justify-content: flex-end;
}

.footer-social-icons a {
    /* currentColor in the SVG means this one declaration colours all four. */
    color: #295A94;
    display: inline-flex;
    /* A 24px glyph is a small tap target; the padding lifts the hit area
       closer to the 44px minimum without changing how big the icon looks. */
    padding: 6px;
    border-radius: 6px;
}

.footer-social-icons a:hover {
    color: #1f4775;
    background: #e7ebf1;
}

.footer-row-legal {
    margin-top: 24px;
    padding-top: 16px;
    border-top: 1px solid #e3e3e3;
}

.footer-row-legal small {
    font-size: 13px;
    color: #777;
}

@media (max-width: 520px) {
    /* Stacked and left-aligned: a right-aligned block looks stranded once it
       is the full width of a phone. */
    .footer-row-main {
        flex-direction: column;
        gap: 20px;
    }

    .footer-social-label,
    .footer-social-icons {
        text-align: left;
        justify-content: flex-start;
    }
}

/* ---- Dialogs (sign in, register) -----------------------------------------
   Styled by CLASS, not by id. The sign-in dialog was added later and rendered
   on browser defaults — small and badly placed — because every rule here named
   #register-dialog. A class means the next dialog is styled the moment it
   exists, which is the same reason the buttons in this file use .btn rather
   than saadmin's per-id approach.

   A native <dialog>, so the browser supplies the top layer, the focus trap and
   Escape. The UA stylesheet gives it a border and `max-width: calc(100% - 6px)`
   that look nothing like the rest of the page, so most of this is replacing
   those defaults rather than adding to them. */
.app-dialog {
    width: min(620px, calc(100vw - 32px));
    max-width: none;
    padding: 0;
    border: 1px solid #d8d8d8;
    border-radius: 12px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.18);
    color: #333;

    /* Centred both ways, with a guaranteed gap above and below.
       `margin: auto` is what centres a modal dialog, and the UA stylesheet
       already sets it — but it is restated here because it only works while
       the dialog is SHORTER than the viewport. An eight-field form is tall
       enough that on a laptop screen it grows past that, and an overflowing
       dialog does not centre: it pins to the top and the part above the fold
       becomes unreachable, because the overflow goes off the top of the
       window rather than into a scrollbar.

       So the height is capped and the dialog scrolls inside itself. The 96px
       is the space left over — roughly 48px above and below once the auto
       margins split it.

       `dvh` rather than `vh` on mobile: `vh` is measured against the viewport
       with the browser chrome HIDDEN, so a dialog sized in vh is taller than
       the screen until the user scrolls the address bar away. The vh line
       stays as the fallback for anything that does not know dvh. */
    margin: auto;
    max-height: calc(100vh - 96px);
    max-height: calc(100dvh - 96px);
    overflow-y: auto;
}

/* Only applies while the dialog was opened with showModal(); a non-modal
   dialog has no backdrop to style. */
.app-dialog::backdrop {
    background: rgba(0, 0, 0, 0.45);
}

.dialog-form {
    padding: 20px 24px 24px;
}

.dialog-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 20px;
}

.dialog-head h2 {
    font-size: 20px;
    font-weight: 600;
}

.dialog-close {
    background: none;
    border: none;
    font-size: 26px;
    line-height: 1;
    color: #888;
    cursor: pointer;
    padding: 0 4px;
}

.dialog-close:hover {
    color: #333;
}

/* Two columns, with .field-wide spanning both. Names and city/country pair up
   naturally; email, password, business and phone are long enough that a half
   width would make them look truncated before anything was typed. */
.form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px 16px;
}

.field {
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.field-wide {
    grid-column: 1 / -1;
}

.field label {
    font-size: 13px;
    font-weight: 600;
    color: #555;
}

/* input AND select together: the country field is a select and has to line up
   with the city input beside it, which means matching the padding, border and
   font exactly rather than approximately. */
.field input,
.field select {
    font: inherit;
    font-size: 14px;
    padding: 9px 11px;
    border: 1px solid #ccc;
    border-radius: 6px;
    /* Without this an input ignores the box model set at the top of the file
       in some engines once a width is involved, and the two columns stop
       matching. */
    box-sizing: border-box;
    width: 100%;
    background: #fff;
    /* A select is a touch shorter than an input at the same padding, because
       its text sits in an inner box. Matching the height explicitly keeps City
       and Country level. */
    height: 38px;
}

.field input:focus,
.field select:focus {
    outline: none;
    border-color: #295A94;
    box-shadow: 0 0 0 3px rgba(41, 90, 148, 0.15);
}

/* Styled only AFTER the field has been marked invalid by the submit handler,
   never on :invalid alone — an empty required field is invalid from the moment
   the form opens, so :invalid would paint every box red before the user has
   typed a character. */
.field input.is-invalid,
.field select.is-invalid {
    border-color: #c0392b;
    background: #fdf3f2;
}

/* The unchosen placeholder reads as greyed-out prompt text rather than as a
   real selection; :invalid is safe HERE because a select with a value is
   valid the moment one is picked, unlike an empty text input. */
.field select:invalid {
    color: #999;
}

.field-hint {
    font-size: 12px;
    color: #888;
}

/* ---- Password reveal -----------------------------------------------------
   The button sits INSIDE the field box rather than beside it, so the password
   input stays the same width as every other input in the grid. */
.input-with-toggle {
    position: relative;
    display: flex;
}

/* Room for the button, so a long password does not run underneath it. */
.input-with-toggle input {
    padding-right: 40px;
}

.password-toggle {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    color: #888;
    border-radius: 0 6px 6px 0;
}

.password-toggle:hover {
    color: #295A94;
}

/* Keyboard users need to see where they are; the input's own focus ring is on
   the field, not on this. */
.password-toggle:focus-visible {
    outline: 2px solid #295A94;
    outline-offset: -2px;
}

/* One icon at a time. `is-revealed` is set by app.js alongside the type swap,
   so the icon always agrees with what the field is actually doing — the eye
   shows while the password is hidden (press to reveal) and the struck-through
   eye shows while it is visible (press to hide). */
.password-toggle .icon-eye-off,
.password-toggle.is-revealed .icon-eye {
    display: none;
}

.password-toggle.is-revealed .icon-eye-off {
    display: block;
}

.form-message {
    margin-top: 16px;
    font-size: 13px;
    line-height: 1.5;
    color: #555;
    /* Empty until something is said, and it must not reserve space when empty
       or the buttons sit oddly low on first open. */
}

.form-message:empty {
    display: none;
}

.form-message.is-error {
    color: #c0392b;
}

.dialog-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 20px;
}

@media (max-width: 480px) {
    .form-grid {
        grid-template-columns: 1fr;
    }
}

/* Sign in carries two fields where register carries nine, so the shared box
   would otherwise read as mostly empty. A bit more breathing room inside, and
   a slightly narrower cap, keeps the two looking like the same component
   without the short one feeling hollow. */
#signin-dialog {
    width: min(480px, calc(100vw - 32px));
}

#signin-form {
    padding: 24px 28px 28px;
}

#signin-form .form-grid {
    gap: 18px;
}

/* ---- Cookie consent ------------------------------------------------------
   Bottom-anchored rather than a centred modal with a scrim. A visitor's first
   impression of the site should be the site, and a banner that leaves the page
   readable behind it is both the convention and the less hostile choice. */
#cookie-banner {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    display: flex;
    justify-content: center;
    padding: 16px;
    /* Above any page content that arrives later. */
    z-index: 100;
    /* Clicks pass through the gutter either side of the card, so the banner
       does not swallow taps on page content it is not covering. */
    pointer-events: none;
}

/* REQUIRED, not belt-and-braces. The `hidden` attribute hides an element
   through the UA stylesheet's `[hidden] { display: none }`, and ANY author
   `display` declaration outranks the UA sheet — so the `display: flex` above
   silently defeated it and the banner could never be hidden, neither by
   `banner.hidden = true` after a choice nor by the attribute already in the
   markup for a returning visitor.
   `#cookie-banner[hidden]` is id + attribute, so it outranks the plain id rule
   above on specificity and needs no `!important`. Any element that both sets
   `display` and relies on `hidden` needs a pair like this. */
#cookie-banner[hidden] {
    display: none;
}

#cookie-card {
    pointer-events: auto;
    max-width: 720px;
    background: #fff;
    border: 1px solid #d8d8d8;
    border-radius: 10px;
    padding: 20px 24px;
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.14);
    /* A long policy paragraph must not push the buttons off a short screen. */
    max-height: 70vh;
    overflow-y: auto;
}

#cookie-card h2 {
    font-size: 17px;
    font-weight: 600;
    margin-bottom: 10px;
}

#cookie-card p {
    font-size: 13px;
    line-height: 1.55;
    color: #555;
}

#cookie-card p + p {
    margin-top: 10px;
}

/* Both buttons at the same size and weight — see the note in index.html for
   why Reject must not be made the quieter one. */
#cookie-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 16px;
}

@media (max-width: 520px) {
    /* Stacked, full width, and Reject still first in the DOM so it is also
       first for a keyboard and a screen reader. */
    #cookie-actions {
        flex-direction: column;
    }

    #cookie-actions .btn {
        width: 100%;
    }
}

/* ---- Signed-in header state ----------------------------------------------
   Both halves of #auth-actions are flex rows; only one is ever visible. */
#auth-signed-out,
#auth-signed-in {
    display: flex;
    gap: 10px;
    align-items: center;
}

/* Same pairing trap as the cookie banner: the `hidden` attribute hides through
   the UA stylesheet's `[hidden] { display: none }`, and ANY author `display`
   outranks it — so these rules would defeat the attribute without the pair
   below. Id + attribute beats the plain id on specificity, no !important. */
#auth-signed-out[hidden],
#auth-signed-in[hidden] {
    display: none;
}

.auth-email {
    font-size: 13px;
    color: #555;
    /* A long address must not push the buttons off the header. */
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ---- Post-signup welcome -------------------------------------------------
   #page-content sets no `display`, so the plain `hidden` attribute works on it
   with no companion rule. #welcome does set one, so it needs the pair — the UA
   stylesheet's `[hidden] { display: none }` loses to ANY author `display`, and
   id+attribute beats the plain id without !important. Third time this has come
   up in this file; the rule is: set `display`, write the `[hidden]` partner. */
#welcome {
    display: flex;
    justify-content: center;
    padding: 64px 24px;
}

#welcome[hidden] {
    display: none;
}

.welcome-card {
    max-width: 560px;
    text-align: center;
}

.welcome-card h2 {
    font-size: 26px;
    font-weight: 600;
    margin-bottom: 14px;
    color: #295A94;
}

.welcome-card p {
    font-size: 15px;
    line-height: 1.65;
    color: #555;
}

.welcome-card p + p {
    margin-top: 10px;
}

/* ---- Busy spinner --------------------------------------------------------
   Sized, so it needs `display: inline-block` — which means it also needs the
   `[hidden]` partner below, for the reason noted three times already in this
   file: an author `display` outranks the UA stylesheet's `[hidden]` rule. */
.spinner {
    display: inline-block;
    width: 18px;
    height: 18px;
    border: 2px solid #c9d4e3;
    border-top-color: #295A94;
    border-radius: 50%;
    animation: spinner-rotate 0.7s linear infinite;
    /* Pushes the buttons to the right of a justify-end row, so the spinner
       sits at the far left rather than crowding Cancel. */
    margin-right: auto;
}

.spinner[hidden] {
    display: none;
}

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

/* Slowed rather than stopped. The spinner is the only thing telling the user
   the request is still running, so removing the motion entirely would remove
   the information; a gentle rotation respects the preference without doing
   that. */
@media (prefers-reduced-motion: reduce) {
    .spinner {
        animation-duration: 2.4s;
    }
}

/* ---- Paywall -------------------------------------------------------------
   Same `[hidden]` pairing as #welcome: this sets `display`, so it needs the
   partner rule or the attribute does nothing. */
#paywall {
    display: flex;
    justify-content: center;
    padding: 64px 24px;
}

#paywall[hidden] {
    display: none;
}

.paywall-card {
    max-width: 480px;
    width: 100%;
    text-align: center;
    border: 1px solid #e0e0e0;
    border-radius: 12px;
    padding: 36px 32px;
}

.paywall-card h2 {
    font-size: 24px;
    font-weight: 600;
    margin-bottom: 18px;
}

.paywall-price {
    font-size: 40px;
    font-weight: 700;
    color: #295A94;
    line-height: 1.1;
    /* Tabular figures so the amount does not reflow if it ever becomes
       dynamic and the digit count changes. */
    font-variant-numeric: tabular-nums;
}

.paywall-period {
    font-size: 15px;
    font-weight: 500;
    color: #888;
}

.paywall-card > p {
    font-size: 15px;
    line-height: 1.6;
    color: #555;
    margin-top: 10px;
}

/* Centred, unlike the dialogs' right-aligned rows — there is one action here
   and it is the point of the screen. */
.paywall-actions {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin-top: 22px;
}

/* The fifth time this partner rule has been needed in this file, so to be
   explicit about the rule: `[hidden]` is a UA stylesheet `display: none`, and
   ANY author `display` beats it. Every selector here that sets display and is
   also toggled by the hidden attribute needs this, or the attribute does
   nothing at all. */
.paywall-actions[hidden] {
    display: none;
}

/* The spinner's margin-right:auto is for a justify-end row; here it would
   shove the button off-centre. */
.paywall-actions .spinner {
    margin-right: 0;
}

/* Where Stripe mounts the embedded payment form. Deliberately almost nothing:
   the iframe Stripe inserts measures and sizes ITSELF, and height or overflow
   set here is how an embedded Checkout ends up with its own inner scrollbar or
   a clipped card field.
   margin-top matches .paywall-actions so the form sits where the button it
   replaces was, and text-align is reset because #paywall centres its children
   and the form inside is left-aligned content. */
#checkout-mount {
    margin-top: 22px;
    text-align: left;
}

/* The partner rule again — see .paywall-actions[hidden] above. Not strictly
   needed while nothing here sets `display`, but this element is toggled by the
   hidden attribute and the next person to add a `display: flex` for a loading
   state would otherwise find the attribute silently dead. */
#checkout-mount[hidden] {
    display: none;
}

/* A button that reads as a link. A <button> rather than an <a> because it
   performs an action and has no href — an anchor with href="#" would be a
   broken target for keyboard and screen-reader users, and a real URL is
   impossible here since the destination is minted per click. */
.link-button {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: #295A94;
    text-decoration: underline;
    cursor: pointer;
}

.link-button:hover {
    color: #1d4370;
}

.link-button:disabled {
    color: #999;
    cursor: default;
}

.paywall-manage {
    display: block;
    font-size: 14px;
    margin-top: 18px;
}

/* The partner rule, again — see .paywall-actions[hidden]. */
.paywall-manage[hidden] {
    display: none;
}

/* ---- Subscriber content ---------------------------------------------------
   The catalogue grid and the player. Only ever on screen for an account with a
   live subscription — the section is revealed by loadContent, which only runs
   once the server has said yes. */

/* The same reading width #solutions sets, and for the same reason: <main> is
   full-bleed so each section owns its own width, and a grid left unconstrained
   runs from one edge of a 4K monitor to the other. */
#content {
    max-width: 1100px;
    margin: 0 auto;
    padding: 40px 24px 0;
}

/* The partner rule — `[hidden]` is a UA `display: none` and any author
   `display` beats it. #content sets none itself, but the grid inside it does,
   and this section is toggled by the attribute. */
#content[hidden] {
    display: none;
}

/* auto-fill with a minmax track rather than a fixed column count, which is what
   .solution-grid uses. The difference is the content: three solution cards are
   a designed row of exactly three, while this holds however many videos a
   country has, so the column count has to follow the viewport instead of the
   markup. 200px is about the narrowest a 9:16 thumbnail plus two lines of text
   stays readable at. */
.content-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 16px;
}

/* An <article>, not a button — nothing happens when you press it, because
   playback was removed (see functions/corporate/content.js). So there is no
   `cursor: pointer`, no hover shift and no focus ring: all three would promise
   an interaction the card cannot deliver, and the hover one is the cruellest
   because it only appears once you have already aimed at it.
   The UA-reset lines a button needed (`font: inherit`, `text-align`,
   `background`) went with it; an <article> inherits all of that.
   `overflow: hidden` is what keeps the thumbnail's square corners inside the
   card's rounded ones. */
.content-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    border: 1px solid #e0e0e0;
    border-radius: 10px;
    overflow: hidden;
}

/* 9:16, because the app records portrait video. aspect-ratio rather than a
   fixed height so the box follows the column width, and object-fit: cover so a
   thumbnail that is not exactly 9:16 fills the frame instead of letterboxing
   inside it. `display: block` kills the inline-image baseline gap under it. */
.content-thumb {
    display: block;
    width: 100%;
    aspect-ratio: 9 / 16;
    object-fit: cover;
    background: #f0f0f3;
}

.content-brand {
    font-weight: 600;
    font-size: 15px;
    padding: 0 12px;
}

/* The brand/product/city/date line. Clamped to two lines: these are
   user-authored values of no fixed length, and one verbose brand name would
   otherwise make its card taller than every other card in the row. */
.content-sub {
    font-size: 13px;
    color: #666;
    padding: 0 12px 12px;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
