/* The Customer Portal's own layer, on top of nexus.css.

   Two jobs, and they are separate.

   One: make this portal look like Shelli. A shop owner meets Shelli every day at the counter
   and Nexus perhaps twice a year, and the two are the same product family. Shelli's own interface
   is built on a purple primary (#845ADF), soft neutral greys, half-rem corners and quiet borders;
   Nexus was blue on square-ish cards. The tokens below retune this portal to that palette, which
   is a handful of variables because nexus.css was written against variables. The Admin Portal is
   deliberately untouched — staff are not the audience being made to feel at home, and changing
   the shared file would have moved both.

   Two: the pieces the Admin Portal has no use for. A shop's own page, a tile of numbers, a
   meter with a bar under it. Those belong here rather than in the shared file, exactly as the note
   at the top of nexus.css says.

   Nothing here restyles a shared component into something different — a button is still a button.
   A portal that redefined the design system would be a second design system. */

/* The drawer is dark next door because staff work in it all day and the contrast helps them find
   it. A customer opens this twice a year, and a black column is heavier than the thing it holds. */
.shell-customer {
    --product-mini: #b02e8a;
}

.shell-customer .drawer {
    background: #fff;
    border-right: 1px solid var(--line);
    color: var(--ink-soft);
}

.shell-customer .drawer .brand {
    color: var(--ink);
}

.shell-customer .nav-link {
    color: var(--ink-soft);
    font-weight: 500;
    margin-bottom: 0.15rem;
    padding: 0.5rem 0.6rem;
}

.shell-customer .nav-link:hover {
    background: var(--paper);
    color: var(--ink);
}

/* The active item is a tint rather than the brand gradient. Five links with one of them carrying a
   full-strength gradient makes the menu the loudest thing on a page whose job is numbers. */
.shell-customer .nav-link.active {
    background: rgb(132 90 223 / 10%);
    color: var(--accent-dark);
}

.nav-icon {
    flex-shrink: 0;
    opacity: 0.8;
}

/* Whose company this is, sitting above the links. */
.nav-scope {
    border-bottom: 1px solid var(--line);
    border-top: 1px solid var(--line);
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    margin-bottom: 0.9rem;
    padding: 0.6rem 0.6rem 0.7rem;
}

.nav-scope-label {
    color: var(--ink-faint);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
}

.nav-scope-name {
    color: var(--ink);
    font-weight: 600;
    overflow-wrap: anywhere;
}

/* What they are in this company, under its name. Quiet on purpose — it is only consulted when a
   screen offers less than the last one did, which is exactly what happens to somebody who owns
   one company and was invited into another. Without it the thinner menu reads as a fault. */
.nav-scope-role {
    color: var(--ink-faint);
    display: block;
    font-size: 0.78rem;
    margin-top: 0.15rem;
}

/* "Yours" and "you were invited to", inside the switcher. Drawn only where both kinds exist —
   an owner with two shops gets the plain list they always had. */
.nav-switch-heading {
    color: var(--ink-faint);
    display: block;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    padding: 0.5rem 0.6rem 0.2rem;
    text-transform: uppercase;
}

/* The role, on the entry rather than only after arriving. This is the list somebody scans to
   decide where to go, and "why does that one show fewer pages" is answered here. */
.nav-switch-item .nav-switch-role {
    color: var(--ink-faint);
    display: block;
    font-size: 0.75rem;
}

.nav-switch {
    margin: -0.6rem 0 0.9rem;
    padding: 0 0.6rem;
}

.nav-switch summary {
    color: var(--accent);
    cursor: pointer;
    font-size: 0.85rem;
    padding: 0.25rem 0;
}

.nav-switch-list {
    display: flex;
    flex-direction: column;
    padding: 0.25rem 0 0.5rem;
}

.nav-switch-item {
    border-radius: 6px;
    color: var(--ink-soft);
    font-size: 0.9rem;
    padding: 0.35rem 0.5rem;
    text-decoration: none;
}

.nav-switch-item:hover {
    background: var(--paper);
}

.nav-switch-item.is-current {
    color: var(--ink);
    font-weight: 600;
}

/* --- The page above the cards ------------------------------------------------ */

/* A heading, what it is about, and whatever acts on it — one row that becomes two on a phone. */
.page-head {
    align-items: flex-start;
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    justify-content: space-between;
    margin-bottom: 1.1rem;
}

.page-head h1 {
    margin-bottom: 0.15rem;
}

.page-head .lede {
    margin: 0;
}

/* Where a page came from, above its title. A shop's page is reached from three places and the
   browser's back button is not an answer on a phone that arrived by a link. */
.crumb {
    color: var(--ink-faint);
    display: inline-flex;
    font-size: 0.85rem;
    gap: 0.35rem;
    margin-bottom: 0.35rem;
    text-decoration: none;
}

.crumb:hover {
    color: var(--accent);
}

/* --- Tiles ------------------------------------------------------------------- */

/* The numbers a dashboard leads with. auto-fit rather than a column count, so four tiles become
   two rows on a tablet and one column on a phone without a breakpoint per layout. */
.tile-grid {
    display: grid;
    gap: 0.85rem;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    margin-bottom: 1.1rem;
}

.tile {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    padding: 0.9rem 1rem;
}

/* Tinted towards the accent rather than grey. It is the smallest amount of colour that makes a
   row of figures read as part of this product and not as a table dropped onto a white page. */
.tile-label {
    color: color-mix(in srgb, var(--accent) 45%, var(--ink-faint));
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.tile-value {
    font-size: 1.6rem;
    font-weight: 600;
    letter-spacing: -0.02em;
    line-height: 1.2;
}

/* The state is on the card, not only on the number. Four identical white boxes is what made a
   dashboard with something wrong on it look like a dashboard with nothing wrong on it — the
   colour was there, three characters wide, in the middle of the row. Tinted at a weight that
   still leaves the figure the loudest thing in the tile. */
.tile-good {
    background: color-mix(in srgb, var(--good) 5%, var(--surface));
    border-color: color-mix(in srgb, var(--good) 26%, var(--line));
}

.tile-warn {
    background: color-mix(in srgb, var(--warn) 6%, var(--surface));
    border-color: color-mix(in srgb, var(--warn) 28%, var(--line));
}

.tile-bad {
    background: color-mix(in srgb, var(--bad) 5%, var(--surface));
    border-color: color-mix(in srgb, var(--bad) 26%, var(--line));
}

.tile-good .tile-value {
    color: var(--good);
}

.tile-warn .tile-value {
    color: var(--warn);
}

.tile-bad .tile-value {
    color: var(--bad);
}

.tile-good .tile-label {
    color: color-mix(in srgb, var(--good) 70%, var(--ink-faint));
}

.tile-warn .tile-label {
    color: color-mix(in srgb, var(--warn) 70%, var(--ink-faint));
}

.tile-bad .tile-label {
    color: color-mix(in srgb, var(--bad) 70%, var(--ink-faint));
}

/* A term that has run out, counted on past its end. Red, and nothing more: how long the shop goes
   on selling after this is not said anywhere a customer reads. */
.countdown-over {
    color: var(--bad);
    font-weight: 600;
}

/* A tile that is also a link. The whole card, not a word inside it — a 40px target on a phone
   beats an underlined phrase. */
.tile-link {
    color: inherit;
    text-decoration: none;
    transition: border-color 0.12s ease;
}

.tile-link:hover {
    border-color: var(--accent-bright);
}

/* --- A shop, as a card ------------------------------------------------------- */

.store-grid {
    display: grid;
    gap: 0.85rem;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

/* The shops list is cards rather than a table on purpose. A table of shops answers "which of these
   is which"; an owner with four branches is asking "is anything wrong with one of them", and the
   answer is a state per shop rather than a row of columns to read across. */
.store-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    color: inherit;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    padding: 1rem;
    text-decoration: none;
    transition: border-color 0.12s ease;
}

.store-card:hover {
    border-color: var(--accent-bright);
}

.store-card-head {
    align-items: baseline;
    display: flex;
    gap: 0.5rem;
    justify-content: space-between;
}

.store-card-name {
    font-size: 1.02rem;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.store-card-where {
    color: var(--ink-faint);
    font-size: 0.85rem;
}

.store-card-facts {
    border-top: 1px solid var(--line);
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 1.1rem;
    padding-top: 0.55rem;
}

.store-card-fact {
    display: flex;
    flex-direction: column;
}

.store-card-fact-label {
    color: var(--ink-faint);
    font-size: 0.7rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.store-card-fact-value {
    font-weight: 600;
}

/* A shop that is closed, or one nobody has quoted for. Dimmed rather than hidden — it is still
   theirs, and hiding it is how a branch goes six months without a licence. */
.store-card-quiet {
    opacity: 0.72;
}

/* --- Connected, or not ------------------------------------------------------- */

/* A dot beside a word. Colour alone would say nothing to a colour-blind owner, so the word is
   always there and the dot is the thing you find at a glance. */
.live {
    align-items: center;
    color: var(--ink-soft);
    display: inline-flex;
    font-size: 0.85rem;
    gap: 0.35rem;
    white-space: nowrap;
}

.live::before {
    background: var(--ink-faint);
    border-radius: 50%;
    content: "";
    height: 8px;
    width: 8px;
}

.live-on::before {
    background: var(--good);
}

.live-off::before {
    background: color-mix(in srgb, var(--ink-faint) 55%, var(--line));
}

/* --- Label and value, down a page -------------------------------------------- */

/* The company's details, a shop's details, an installation's. Two columns on a desktop, stacked on
   a phone, and the label never wraps away from its value. */
.kv {
    display: grid;
    gap: 0.15rem 1rem;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

.kv-row {
    border-bottom: 1px solid var(--line);
    display: flex;
    flex-direction: column;
    gap: 0.05rem;
    padding: 0.5rem 0;
}

.kv-label {
    color: var(--ink-faint);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.kv-value {
    overflow-wrap: anywhere;
}

/* --- A meter, with what is left under it ------------------------------------- */

.meter-grid {
    display: grid;
    gap: 0.85rem;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

/* Charts side by side rather than stacked. A chart per meter is right — documents and email are
   counted in the same unit and mean nothing alike — but two full-width ones pushed the table that
   explains them off the bottom of the screen. Wider than the meter cards, because a trend needs
   horizontal room before it says anything; below that it falls to one column and stays readable. */
.chart-grid {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
    margin-bottom: 1.1rem;
}

.meter {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding: 0.9rem 1rem;
}

.meter-head {
    align-items: baseline;
    display: flex;
    gap: 0.5rem;
    justify-content: space-between;
}

.meter-name {
    font-weight: 600;
}

.meter-left {
    font-size: 1.3rem;
    font-weight: 600;
    letter-spacing: -0.02em;
}

/* The track is always full width; the fill is what is left, not what is spent. An owner reads a
   fuel gauge, not a progress bar — a bar that filled up as credits ran out would be read backwards
   by everybody who glanced at it. */
.meter-bar {
    background: color-mix(in srgb, var(--accent) 8%, var(--paper));
    border-radius: 999px;
    height: 6px;
    overflow: hidden;
}

.meter-fill {
    background: var(--accent-bright);
    border-radius: 999px;
    display: block;
    height: 100%;
}

.meter-fill-warn {
    background: var(--warn);
}

.meter-fill-bad {
    background: var(--bad);
}

.meter-foot {
    color: var(--ink-faint);
    display: flex;
    font-size: 0.82rem;
    gap: 0.5rem;
    justify-content: space-between;
}

/* --- What is on sale --------------------------------------------------------- */

/* The credits page is laid out as a shop: one section per kind of credit, holding that kind's gauge
   and that kind's packages.

   A section a company cannot use is never rendered, so there is no rule here for hiding one. */

/* The line above the sections that says the whole shop is a request rather than a checkout, with
   the market it is priced for on the other end. */
.shelf-head {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
    justify-content: space-between;
    margin-bottom: 1rem;
}

.shelf-head p {
    margin: 0;
}

.shelf {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
}

/* The card's own children carry the rhythm now, so the paragraph margins that used to do it
   would double every gap. */
.shelf > p {
    margin: 0;
}

/* What is left, beside the heading rather than under it: the question "have I got any" is asked
   at the same moment as "what does more cost", and they belong on one line. */
.shelf-left {
    font-size: 0.9rem;
    font-weight: 600;
}

/* --- Nothing here yet -------------------------------------------------------- */

/* A real state, drawn deliberately rather than left as an empty region. Every one of these says
   what would put something here. */
.empty {
    background: color-mix(in srgb, var(--accent) 4%, var(--surface));
    border: 1px dashed color-mix(in srgb, var(--accent) 35%, var(--line));
    border-radius: var(--radius);
    color: var(--ink-soft);
    padding: 1.6rem 1rem;
    text-align: center;
}

.empty p {
    margin: 0 0 0.6rem;
}

.empty p:last-child {
    margin-bottom: 0;
}

/* --- Narrow screens ---------------------------------------------------------- */

@media (max-width: 640px) {
    .tile-value {
        font-size: 1.4rem;
    }

    /* One column, because two 190px tiles plus a gap is wider than a phone and the grid would
       otherwise let them shrink until the numbers wrapped mid-figure. */
    .tile-grid,
    .store-grid,
    .meter-grid {
        grid-template-columns: 1fr;
    }
}

/* --- Waiting for a shop to answer --------------------------------------------

   A card rather than an overlay. The reader has just pressed a button and is
   waiting on a machine in a shop, which can take twenty seconds and can fail —
   covering the page would hide the window picker and the sentence explaining
   what is happening. */
.report-waiting {
    align-items: center;
    display: flex;
    gap: 1.1rem;
}

.report-waiting h2 {
    margin-top: 0;
}

/* Its own element rather than a border on the card: a spinner drawn on the
   panel would rotate the text with it. Sized in rem so it grows with the
   reader's own type size. */
.report-spinner {
    animation: report-spin 900ms linear infinite;
    block-size: 2rem;
    border: 3px solid var(--line);
    border-radius: 50%;
    border-top-color: var(--accent);
    flex-shrink: 0;
    inline-size: 2rem;
}

@keyframes report-spin {
    to {
        transform: rotate(360deg);
    }
}

/* Somebody who has asked not to be shown motion still needs to know the page is
   doing something, so the ring stays and stops turning. A spinner removed
   entirely leaves a card that looks finished and is not. */
@media (prefers-reduced-motion: reduce) {
    .report-spinner {
        animation: none;
    }
}

/* When it was built, when it goes, and how many asks are left — one line above
   the numbers rather than three sentences under them. */
.report-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem 1.1rem;
    margin: 0 0 1rem;
}

/* The button and what it will cost, side by side. The cost has to be readable
   before the button is pressed, not after it is refused. */
.report-ask {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 1rem;
}

.report-ask p {
    margin: 0;
}

/* --- the shop window --------------------------------------------------------

   The one page somebody reads before they have an account.

   Every colour is a token from nexus.css, so the page follows the theme rather
   than carrying a palette of its own - a landing page with its own colours is
   the page that stops matching the product the first time the brand moves.

   Motion is entrance only, and every rule of it is inside the reduced-motion
   guard at the bottom. A shop owner on a slow phone gets the page, not the
   performance. */

/* The shell caps .content at 64rem, which is the reading column a dashboard wants and the
   wrong measure for the one page in this portal that is not one. On a wide monitor the hero
   became a small card adrift in the middle of the screen.

   Widened here rather than in the shared layer, and by the same means the work layout already
   uses - .content:has(.work-layout) sets 90rem. The landing goes further and takes the whole
   width, because its sections carry background of their own that has to reach both edges.

   The pricing page needs it every bit as much and was missing from this list, which is the kind
   of thing that only shows up on a monitor. Every section on it already asks for the full-bleed
   padding below - .pricing-head, .pricing-plans, .compare, .closing - and every one of them was
   resolving it against 64rem instead of the viewport, so the gutter computed to its 1.5rem floor
   and the band was 1024px wide on a 1440px screen. Three plan cards could not fit in it, Pro
   wrapped onto a row of its own, and it read as a two-plan product with an orphan underneath. */
.content:has(.hero),
.content:has(.pricing-head) {
    max-width: none;
    padding-inline: 0;
    padding-top: 0;
}

/* Full-bleed background, centred content, and no wrapper element to do it.

   The padding resolves to whatever is left over once 78rem is taken out of the viewport, or
   the gutter on a narrow one - so every section paints edge to edge while its words stay in a
   column somebody can read. A wrapper div per section would do the same and put six of them in
   the markup. */
.hero,
.industries,
.steps,
.capabilities,
.assistant,
.screens,
.planes,
.assurances,
.faq,
.closing,
.compare,
.who,
.office,
.ia,
.promise,
.mini-block {
    --landing-gap: clamp(3rem, 7vw, 5.5rem);

    padding-inline: max(1.5rem, calc((100% - 78rem) / 2));
}

/* A tinted field behind the fold rather than a photograph. It reads as designed
   at every width and costs no request, which a hero image does. */
.hero {
    align-items: center;
    background:
        radial-gradient(60% 80% at 15% 0%,
            color-mix(in srgb, var(--accent) 16%, transparent) 0%, transparent 70%),
        radial-gradient(50% 70% at 95% 20%,
            color-mix(in srgb, var(--maui) 18%, transparent) 0%, transparent 70%);
    display: grid;
    gap: clamp(1.5rem, 5vw, 4rem);
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    margin-bottom: var(--landing-gap);

    /* Deeper than a card. A hero that is only as tall as its words reads as a banner; this is
       the first thing anybody sees and it has to hold the screen. */
    padding-block: clamp(3rem, 9vw, 7rem);
}

@media (max-width: 60rem) {
    .hero {
        grid-template-columns: minmax(0, 1fr);
        text-align: center;
    }

    .hero-actions {
        justify-content: center;
    }
}

.hero-eyebrow {
    color: var(--accent);
    font-size: 0.82rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    margin: 0 0 0.9rem;
    text-transform: uppercase;
}

/* clamp rather than breakpoints: the headline is the one thing that has to look
   deliberate at every width, and two breakpoints never quite manage it. */
.hero-title {
    font-size: clamp(2.1rem, 4.6vw, 4rem);
    letter-spacing: -0.02em;
    line-height: 1.08;
    margin: 0 0 1rem;
    max-width: 15ch;
}

@media (max-width: 60rem) {
    .hero-title {
        margin-inline: auto;
    }
}

.hero-lede {
    color: var(--ink-soft);
    font-size: clamp(1rem, 1.6vw, 1.15rem);
    line-height: 1.65;
    margin: 0 0 1.75rem;
    max-width: 48ch;
}

@media (max-width: 60rem) {
    .hero-lede {
        margin-inline: auto;
    }
}

.hero-actions {
    /* Centred, so a taller button does not drag a shorter one to the top of the row. */
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
}

/* Never narrower than their own words.

   Flex items shrink by default, and these were being squeezed until the label wrapped: "Ver los
   planes" came out 153px wide and 70px tall beside a 50px primary, which read as a card that had
   landed in the button row rather than as a second button. */
.hero-actions .button {
    flex: none;

    /* .button-primary carries a 1.2rem top margin globally, so a primary hangs below the form
       above it. In a row of buttons it drops that one 19px below its neighbour and stretches the
       row to 70px around two 50px buttons. Zeroed here the way .action-row already does it -
       scoped, because zeroing it on the button itself once lost the gap for every form in the
       portal at once. */
    margin-top: 0;
    white-space: nowrap;
}

.button-large {
    font-size: 1.02rem;
    padding: 0.72rem 1.65rem;
}

.hero-note {
    color: var(--ink-faint);
    font-size: 0.88rem;
    margin: 1.15rem 0 0;
}

/* --- the drawn devices ------------------------------------------------------

   A till and a phone, in the page's own colours. Schematic on purpose: a
   fabricated screenshot would be a picture of a product that does not look
   like that, and it would go stale the first time the real one changed. */

.hero-art {
    display: flex;
    justify-content: center;
}

/* --- Screens, and the things they stand in -------------------------------------

   The chrome is drawn here rather than shipped as artwork: three PNG bezels would be three more
   files to keep in step with the palette, and a device wanted at another size would be a new
   export instead of a number. */

.device {
    display: flex;
    flex-direction: column;
    margin: 0;
    max-width: 100%;
}

/* The bezel. The screen inside it is whatever it is handed - a capture keeps its own
   proportions, and a drawn one takes an aspect ratio from the device below. */
.device-screen {
    background: var(--ink);
    overflow: hidden;
    position: relative;
}

.device-shot {
    display: block;
    height: auto;
    width: 100%;
}

/* A monitor: a thin dark bezel, a neck and a foot. The neck and the foot are pseudo-elements
   on the figure rather than boxes in the markup, because they are decoration and a screen
   reader meeting two empty divs has to wonder what it missed. */
.device-desktop .device-screen {
    border: 10px solid var(--ink);
    border-radius: 12px;
    box-shadow: 0 18px 48px rgb(20 23 28 / 18%);
    order: 1;
}

/* Every part numbered, because a ::before is drawn before the content by default and put the
   monitor's neck above its screen - a stand growing out of the top of a monitor, which is
   exactly the kind of thing that only shows up when somebody looks. */
.device-desktop::before {
    order: 2;
}

.device-desktop::after {
    order: 3;
}

.device-desktop .device-caption {
    order: 4;
}

.device-desktop::before {
    background: linear-gradient(
        to bottom, color-mix(in srgb, var(--ink) 70%, transparent), var(--ink));
    border-radius: 0 0 3px 3px;
    content: "";
    height: 14px;
    margin: 0 auto;
    width: 22%;
}

.device-desktop::after {
    background: var(--ink);
    border-radius: 4px;
    content: "";
    height: 7px;
    margin: 0 auto;
    width: 38%;
}

/* A self-service kiosk: a portrait screen on a floor column. Built like the monitor and
   numbered like it, because a ::before is drawn before the content and a kiosk growing out of
   the top of its own pedestal is the same bug wearing a different shape. */
.device-kiosk .device-screen {
    border: 12px solid var(--ink);
    border-radius: 14px;
    box-shadow: 0 18px 48px rgb(20 23 28 / 18%);
    order: 1;
}

.device-kiosk::before {
    background: linear-gradient(
        to bottom, color-mix(in srgb, var(--ink) 70%, transparent), var(--ink));
    content: "";
    height: 26px;
    margin: 0 auto;
    order: 2;
    width: 30%;
}

.device-kiosk::after {
    background: var(--ink);
    border-radius: 5px;
    content: "";
    height: 9px;
    margin: 0 auto;
    order: 3;
    width: 55%;
}

.device-kiosk .device-caption {
    order: 4;
}

/* A tablet: rounder, an even bezel all the way round, landscape. */
.device-tablet .device-screen {
    border: 14px solid var(--ink);
    border-radius: 20px;
    box-shadow: 0 14px 36px rgb(20 23 28 / 16%);
}

/* A phone: rounder still, a thinner bezel so the screen is most of it, and portrait. */
.device-phone .device-screen {
    border: 9px solid var(--ink);
    border-radius: 26px;
    box-shadow: 0 14px 36px rgb(20 23 28 / 16%);
}

/* The bar across the top of a phone. Not a notch - a shape borrowed off one manufacturer's
   handset is that manufacturer's handset. */
.device-phone .device-screen::after {
    background: var(--ink);
    border-radius: 0 0 6px 6px;
    content: "";
    height: 10px;
    left: 50%;
    position: absolute;
    top: 0;
    transform: translateX(-50%);
    width: 34%;
    z-index: 1;
}

/* The drawn screen: blocks in the shape of a till, obviously a drawing rather than a capture.
   A page waiting on a real screenshot must not carry something a reader could take for one. */
.device-placeholder {
    background: var(--paper);
    display: flex;
    flex-direction: column;
    gap: 6%;
    height: 100%;
    padding: 7%;
}

.device-tablet .device-placeholder {
    aspect-ratio: 4 / 3;
}

.device-phone .device-placeholder {
    aspect-ratio: 9 / 16;
}

.device-kiosk .device-placeholder {
    aspect-ratio: 3 / 4;
}

.ph-bar {
    background: color-mix(in srgb, var(--accent) 24%, transparent);
    border-radius: 4px;
    display: block;
    flex: none;
    height: 7%;
}

.ph-body {
    display: flex;
    flex: 1;
    gap: 6%;
    min-height: 0;
}

.ph-tiles {
    display: grid;
    flex: 2;
    gap: 6%;
    grid-template-columns: repeat(3, 1fr);
}

.device-phone .ph-tiles,
.device-kiosk .ph-tiles {
    grid-template-columns: repeat(2, 1fr);
}

.ph-tile {
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    border-radius: 5px;
}

.ph-tile:nth-child(3n + 2) {
    background: color-mix(in srgb, var(--ink) 8%, transparent);
}

.ph-panel {
    background: color-mix(in srgb, var(--ink) 5%, transparent);
    border-radius: 5px;
    display: flex;
    flex: 1;
    flex-direction: column;
    gap: 8%;
    padding: 7%;
}

/* The panel is the ticket, so its lines are a list and the last block is the total. On a
   phone there is no room for it beside the products, so it goes under them. */
.device-phone .ph-body,
.device-kiosk .ph-body {
    flex-direction: column;
}

.ph-line {
    background: color-mix(in srgb, var(--ink) 16%, transparent);
    border-radius: 3px;
    flex: none;
    height: 6px;
}

.ph-total {
    background: color-mix(in srgb, var(--accent) 38%, transparent);
    border-radius: 4px;
    flex: none;
    height: 14px;
    margin-top: auto;
}

.device-caption {
    margin-top: 1rem;
    text-align: center;
}

.device-title {
    display: block;
    font-weight: 700;
}

.device-note {
    color: var(--ink-soft);
    display: block;
    font-size: 0.92rem;
    line-height: 1.5;
    margin-top: 0.35rem;
}

/* --- the hero stage ---------------------------------------------------------

   Four devices, one composition. The capture takes the top row whole because it is the only
   thing on this page a reader can check against what they will get; the three still waiting on
   screenshots stand under it at the sizes they really are next to a monitor.

   Capped rather than stretched: past this the capture is being enlarged beyond its own
   resolution and starts to look soft. */
.hero-art .hero-stage {
    max-width: 40rem;
    width: 100%;
}

.hero-stage {
    display: grid;

    /* Not three equal columns. A kiosk, a tablet and a phone given a third each come out the
       same width, which is the one thing a picture of three different devices must not say. */
    grid-template-columns: 0.85fr 1.35fr 0.6fr;

    align-items: end;
    gap: clamp(0.6rem, 2vw, 1.4rem);
}

.hero-stage .device-desktop {
    grid-column: 1 / -1;
    margin-bottom: clamp(0.5rem, 2vw, 1.25rem);
}

/* Their bezels shrink with them. A 14px border on a tablet drawn nine rem wide is a picture of
   a frame with a screen somewhere inside it. */
.hero-stage .device-tablet .device-screen {
    border-width: 6px;
    border-radius: 10px;
}

.hero-stage .device-phone .device-screen {
    border-width: 5px;
    border-radius: 13px;
}

.hero-stage .device-kiosk .device-screen {
    border-width: 6px;
    border-radius: 8px;
}

.hero-stage .device-kiosk::before {
    height: 16px;
}

/* Two lines' worth whether the caption fills them or not.

   The row bottom-aligns the figures, and a figure is its device plus its caption - so in Spanish,
   where "Tomando el pedido" wraps and the other two do not, the handheld floated a line above its
   neighbours. Reserving the space levels the devices instead of the captions.

   The subgrid the Shelli Mini row uses would be the better answer and cannot be used here: the
   kiosk's column and base are pseudo-elements ordered inside a flex column, and turning that
   figure into a grid puts them in cells of their own. */
.hero-stage .device-caption {
    margin-top: 0.6rem;
    min-height: 2.6em;
}

.hero-stage .device-title {
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.01em;
}

/* Under the copy rather than beside it, the stage has the whole width and the small devices
   stop being thumbnails. */
@media (max-width: 60rem) {
    .hero-art .hero-stage {
        max-width: 34rem;
    }
}

.screens {
    margin-bottom: var(--landing-gap);
}

/* Devices of two different shapes on one row, sized by what they are rather than by an equal
   share of the width. A tablet and a phone given a column each come out the same height, which
   is the one thing a picture of a tablet and a phone must not say.

   Two rows rather than one, and each device borrows them: the screens line up along the first,
   the captions start together on the second. On a flex row the figures bottom-aligned instead,
   so the device with the shorter caption floated a caption's worth above the other and the two
   read as a pair somebody had failed to line up. */
.screen-row {
    align-items: end;
    display: grid;
    gap: clamp(2rem, 5vw, 4rem);
    grid-template-columns: minmax(0, 26rem) minmax(0, 13rem);
    grid-template-rows: auto auto;
    justify-content: center;
    margin-top: 2.5rem;
}

.screen-row .device {
    display: grid;
    grid-row: span 2;
    grid-template-rows: subgrid;
}

/* Back to a column once they stack, where there is no row left to share. */
@media (max-width: 44rem) {
    .screen-row {
        grid-template-columns: minmax(0, 22rem);
        justify-items: center;
    }

    .screen-row .device {
        display: flex;
        grid-row: auto;
    }

    .screen-row .device-phone {
        max-width: 13rem;
    }
}

.screen-row .device-caption {
    max-width: 26rem;
}

.screens-more {
    margin: 2.25rem 0 0;
    text-align: center;
}

.screens-more a {
    color: var(--accent);
    font-weight: 600;
}

/* --- the sections -----------------------------------------------------------*/

.grow,
.industries,
.steps,
.capabilities,
.assistant,
.ladder,
.ecosystem,
.nexus-band,
.modules,
.planes,
.assurances,
.faq {
    margin-bottom: var(--landing-gap);
}

/* The sections added with the growth story sit on the same full-bleed field as the rest. Set
   here rather than on each one: a section that forgets it is a column of text in the middle of
   a page whose every other band runs edge to edge. */
.grow,
.ladder,
.ecosystem,
.nexus-band,
.modules {
    padding-inline: max(1.5rem, calc((100% - 78rem) / 2));
}

/* Rhythm. Every section on a flat field reads as one long page; a tinted band every other one
   gives the eye somewhere to rest and makes the page feel composed rather than stacked. The
   tint is faint on purpose - it is punctuation, not decoration. */
.steps,
.planes,
.ladder,
.nexus-band {
    background: color-mix(in srgb, var(--ink) 3%, transparent);
    padding-block: var(--landing-gap);
}

/* --- how somebody starts ---------------------------------------------------*/

.step-list {
    counter-reset: none;
    display: grid;
    gap: 1.25rem;
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    list-style: none;
    margin: 0;
    padding: 0;
}

.step {
    border-top: 2px solid color-mix(in srgb, var(--accent) 45%, transparent);
    padding-top: 1.1rem;
}

.step-number {
    color: var(--accent);
    display: block;
    font-size: 1.6rem;
    font-weight: 700;
    line-height: 1;
    margin-bottom: 0.5rem;
}

.step h3 {
    font-size: 1.05rem;
    margin: 0 0 0.4rem;
}

.step p {
    color: var(--ink-soft);
    line-height: 1.6;
    margin: 0;
}

/* --- the questions ---------------------------------------------------------*/

.faq-list {
    margin-inline: auto;
    max-width: 52rem;
}

.faq-item {
    border-bottom: 1px solid var(--line);
    padding: 1.1rem 0;
}

.faq-item summary {
    cursor: pointer;
    font-size: 1.02rem;
    font-weight: 600;
    list-style: none;
    padding-right: 2rem;
    position: relative;
}

.faq-item summary::-webkit-details-marker {
    display: none;
}

/* Drawn rather than a character, so it turns rather than swapping glyph. */
.faq-item summary::after {
    border-bottom: 2px solid var(--ink-faint);
    border-right: 2px solid var(--ink-faint);
    content: "";
    height: 0.5rem;
    position: absolute;
    right: 0.35rem;
    top: 0.35rem;
    transform: rotate(45deg);
    transition: transform 0.15s ease;
    width: 0.5rem;
}

.faq-item[open] summary::after {
    transform: rotate(-135deg);
}

.faq-item p {
    color: var(--ink-soft);
    line-height: 1.7;
    margin: 0.75rem 0 0;
    max-width: 60ch;
}

.section-title {
    font-size: clamp(1.5rem, 3vw, 2.1rem);
    letter-spacing: -0.015em;
    margin: 0 0 0.6rem;
    text-align: center;
}

.section-lede {
    color: var(--ink-soft);
    line-height: 1.6;
    margin: 0 auto 2.25rem;
    max-width: 56ch;
    text-align: center;
}

.industry-grid,
.capability-grid {
    display: grid;
    gap: 1rem;
}

.industry-grid {
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

.capability-grid {
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}

.industry,
.capability {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 1.5rem;
    transition: border-color 0.15s ease, transform 0.15s ease;
}

/* Lifts under the pointer. Small enough to read as responsive rather than as
   something having gone wrong. */
.industry:hover,
.capability:hover {
    border-color: color-mix(in srgb, var(--accent) 55%, var(--line));
    transform: translateY(-2px);
}

.industry-glyph {
    display: block;
    font-size: 1.9rem;
    line-height: 1;
    margin-bottom: 0.75rem;
}

.industry h3,
.capability h3 {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    font-size: 1.08rem;
    gap: 0.5rem;
    margin: 0 0 0.45rem;
}

.industry p,
.capability p {
    color: var(--ink-soft);
    line-height: 1.6;
    margin: 0;
}

/* Not built yet, said on the card. Muted rather than bright: it is a caveat,
   and a badge that competes with the heading reads as a feature. */
.badge-soon {
    background: color-mix(in srgb, var(--warn) 20%, transparent);
    border-radius: 999px;
    color: var(--ink-soft);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    padding: 0.15rem 0.5rem;
    text-transform: uppercase;
}

/* --- plans ------------------------------------------------------------------*/

.product {
    margin-bottom: 2.5rem;
}

.product-head {
    margin-bottom: 1.15rem;
}

.product-head h3 {
    font-size: 1.35rem;
    margin: 0 0 0.35rem;
}

.market-note {
    color: var(--ink-faint);
    font-size: 0.88rem;
    margin: 0;
    text-align: center;
}

/* The device price under the cards: part of the offer, not the small print below it. */
.device-note {
    color: var(--ink-soft);
    margin: 0 0 0.35rem;
    text-align: center;
}

/* --- the closing ------------------------------------------------------------*/

.assurances {
    display: grid;
    gap: 1.5rem;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

.assurance h3 {
    font-size: 1.02rem;
    margin: 0 0 0.4rem;
}

.assurance p {
    color: var(--ink-soft);
    line-height: 1.6;
    margin: 0;
}

.closing {
    background:
        radial-gradient(70% 120% at 50% 0%,
            color-mix(in srgb, var(--accent) 15%, transparent) 0%, transparent 70%);
    padding-block: clamp(3rem, 8vw, 5.5rem);
    text-align: center;
}

.closing h2 {
    font-size: clamp(1.5rem, 3vw, 2.1rem);
    margin: 0 0 0.7rem;
}

.closing p {
    color: var(--ink-soft);
    margin: 0 auto 1.75rem;
    max-width: 46ch;
}

.closing .hero-actions {
    justify-content: center;
}

/* --- motion -----------------------------------------------------------------

   Entrance only, and staggered so the page assembles rather than flashing. The
   default is the finished state, so anything that stops the animation running -
   an old browser, a blocked stylesheet - leaves the page readable rather than
   invisible, which is the failure mode of animating from opacity nought. */

@media (prefers-reduced-motion: no-preference) {
    .hero-copy,
    .hero-art,
    .industry,
    .capability,
    .assurance,
    .grow-stage,
    .rung,
    .eco-node,
    .nexus-point,
    .pricing-plans .offer {
        animation: landing-rise 0.5s ease-out both;
    }

    .hero-art { animation-delay: 0.08s; }

    .industry:nth-child(2),
    .capability:nth-child(2),
    .grow-stage:nth-child(2),
    .rung:nth-child(2),
    .nexus-point:nth-child(2) { animation-delay: 0.05s; }

    .industry:nth-child(3),
    .capability:nth-child(3),
    .grow-stage:nth-child(3),
    .rung:nth-child(3),
    .nexus-point:nth-child(3) { animation-delay: 0.1s; }

    .industry:nth-child(n+4),
    .rung:nth-child(4) { animation-delay: 0.15s; }

    .capability:nth-child(4) { animation-delay: 0.15s; }
    .capability:nth-child(n+5) { animation-delay: 0.2s; }

    .pricing-plans .offer:nth-child(2) { animation-delay: 0.05s; }
    .pricing-plans .offer:nth-child(3) { animation-delay: 0.1s; }

    /* The comparison arrives as one card rather than forty rows. Staggering the lines would
       animate the thing somebody is trying to read, and a table that assembles itself under the
       eye is slower to read than one that is simply there. */
    .compare-card {
        animation: landing-rise 0.5s ease-out both;
        animation-delay: 0.08s;
    }

    /* The two switches settle after the cards they change, so the eye lands on the prices first
       and then notices there is something to press. */
    .market-switch { animation: landing-rise 0.45s ease-out both; }

    .term-switch {
        animation: landing-rise 0.45s ease-out both;
        animation-delay: 0.05s;
    }

    /* A tick that draws itself as its row arrives. Scale rather than opacity, because a tick
       fading in reads as a page still loading and a tick popping in reads as an answer. */
    .compare-yes {
        animation: tick-in 0.32s cubic-bezier(0.34, 1.56, 0.64, 1) both;
        display: inline-block;
    }

    /* Six down the column, then the rest together. Enough for the eye to read it as a sweep
       rather than a flash, and short enough that nobody is waiting on row forty. */
    .compare-table tr:nth-child(2) .compare-yes { animation-delay: 0.10s; }
    .compare-table tr:nth-child(3) .compare-yes { animation-delay: 0.14s; }
    .compare-table tr:nth-child(4) .compare-yes { animation-delay: 0.18s; }
    .compare-table tr:nth-child(5) .compare-yes { animation-delay: 0.22s; }
    .compare-table tr:nth-child(n+6) .compare-yes { animation-delay: 0.26s; }

    /* The recommended plan lifts a little as it settles, so the card the business is pointing at
       is the one that moves last and finishes highest. */
    .pricing-plans .offer-featured {
        animation: featured-rise 0.6s ease-out both;
        animation-delay: 0.12s;
    }
}

@keyframes tick-in {
    from {
        opacity: 0;
        transform: scale(0.4);
    }

    to {
        opacity: 1;
        transform: scale(1);
    }
}

@keyframes featured-rise {
    from {
        opacity: 0;
        transform: translateY(1.1rem);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes landing-rise {
    from {
        opacity: 0;
        transform: translateY(0.75rem);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* --- signing in and opening an account --------------------------------------

   Two panels: the argument on the left, the form on the right. The shared .account-card is a
   card alone in the middle of the screen, which is right for the Admin Portal - a staff sign-in
   has nothing to sell and the person opening it has already decided - and was the one place on
   this site where a visitor stopped being sold to.

   All of it scoped under .account-split, so the Admin Portal keeps its card untouched. */

.account-split {
    display: grid;
    gap: clamp(2rem, 5vw, 4.5rem);

    /* Both tracks capped, and that is what centres the pair. The panel column used to be 1fr:
       a free-space track eats everything the window has spare, so justify-content was handed
       nothing to centre and the two panels sat hard against the right of a wide monitor with
       half a screen of nothing beside them. minmax keeps them free to shrink. */
    grid-template-columns: minmax(0, 30rem) minmax(0, 26rem);
    justify-content: center;
    padding: clamp(2rem, 6vw, 4rem) clamp(1rem, 5vw, 3rem);
}

/* The panel is not read on a phone, and a column of reassurance above the fields is a column
   somebody scrolls past to reach them. Below the breakpoint the form is the whole job. */
@media (max-width: 60rem) {
    .account-split {
        grid-template-columns: minmax(0, 26rem);
    }

    .account-pitch {
        display: none;
    }
}

.account-pitch {
    align-self: center;
    max-width: 30rem;

    /* Right-aligned against the gutter, so the two panels read as one composition rather than
       as a column of text that happens to sit beside a box. */
    justify-self: end;
}

.account-logo {
    display: inline-block;
    margin-bottom: 2rem;
}

.account-logo img {
    display: block;
    height: 46px;
    width: auto;
}

/* Only on the narrow layout, where the panel that carries the other one is gone. */
.account-logo-small {
    display: none;
}

@media (max-width: 60rem) {
    .account-logo-small {
        align-self: center;
        display: inline-block;
        margin-bottom: 1.5rem;
    }

    .account-logo-small img {
        height: 38px;
    }
}

.account-headline {
    font-size: clamp(1.6rem, 3vw, 2.3rem);
    font-weight: 700;
    line-height: 1.2;
    margin: 0 0 1.75rem;
}

.account-points {
    display: grid;
    gap: 0.85rem;
    list-style: none;
    margin: 0 0 2rem;
    padding: 0;
}

/* A tick before each, drawn rather than typed, so the list reads as things you get rather than
   as three sentences that happen to be near each other. */
.account-points li {
    color: var(--ink-soft);
    display: grid;
    gap: 0.7rem;
    grid-template-columns: 1.35rem 1fr;
    line-height: 1.5;
}

.account-points li::before {
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    border-radius: 999px;
    color: var(--accent-dark);
    content: "\2713";
    font-size: 0.75rem;
    font-weight: 700;
    height: 1.35rem;
    line-height: 1.35rem;
    text-align: center;
    width: 1.35rem;
}

.account-quote {
    border-left: 3px solid var(--accent);
    color: var(--ink-faint);
    font-size: 0.9rem;
    margin: 0;
    padding-left: 0.9rem;
}

.account-column {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
}

/* Roomier than the shared card and without the gradient band across the top. The band is Nexus
   introducing itself, which is what a staff portal does; here the reader already knows whose
   site this is - the lockup is right above it. */
.account-split .account-card {
    border-radius: calc(var(--radius) * 1.75);
    box-shadow: 0 24px 60px -40px rgba(15, 23, 42, 0.4);
    max-width: none;
    padding: clamp(1.75rem, 3vw, 2.4rem);
}

.account-split .account-card::before {
    content: none;
}

.account-split .account-card h1 {
    font-size: 1.55rem;
    margin: 0 0 0.4rem;
}

.account-split .account-lede {
    margin-bottom: 1.6rem;
}

/* Centred under the card rather than tucked against its left edge, which is where a footer and
   a language picker belong when the thing above them is the whole page. */
.account-split .account-tools,
.account-split .account-footer {
    align-self: center;
    text-align: center;
}

.account-split .account-aside {
    text-align: center;
}

/* Full width, which is what a sign-in button is everywhere. A 92px button under a 337px field
   reads as one more control on the form rather than as the thing the form is for. */
.account-split .account-form > button[type="submit"],
.account-split .account-form > .button-primary {
    padding-block: 0.62rem;
    width: 100%;
}

/* Room to breathe between the last field and the button. The global 1.2rem on .button-primary
   does this already; saying it here as well keeps it true if the button is ever a plain one. */
.account-split .account-form > button[type="submit"] {
    margin-top: 1.35rem;
}

/* The shape of a way in that is not a password. Same size and same place whichever provider it
   is, so a page offering two does not read as one real button and one afterthought. */
.provider-button {
    align-items: center;
    display: flex;
    font-weight: 600;
    gap: 0.65rem;
    justify-content: center;
    padding-block: 0.62rem;
    width: 100%;
}

/* Drawn to the provider's brand rather than ours. These are the controls on the site that are
   not allowed to look like Shelli: people recognise them by their shape, and a purple one
   carrying our own wordmark reads as a phishing page. */
.google-button {
    background: var(--surface);
    color: var(--ink);
}

/* ⚠ Black, white text, and no mark yet - Apple's guidelines cover the button down to their
   artwork, and it goes in with the browser lane that would draw it. Nothing lists Apple as a
   browser provider until then, so this styles a button the page cannot yet render. */
.apple-button {
    background: #000;
    border-color: #000;
    color: #fff;
}

.apple-button:hover {
    background: #1a1a1a;
    border-color: #1a1a1a;
}

/* --- the dot field ----------------------------------------------------------

   A grid of faint dots, the way Shelli's own sign-in stands on one. What is here is the grid at
   rest: a mask rather than elements, so the whole layer is one box and costs no DOM at all.

   The dots move, and that is dot-field.js on a canvas - CSS can tint a dot and cannot shift one,
   and a wave is dots leaving their places. The stylesheet did once animate a colour across this
   as a fallback, and it had to go: what it drew was four gradient lobes over a fixed grid, which
   looks nothing like the wave and turned up whenever the script had not taken over yet.

   Two places, and no more. The sign-in page, where it is the background and is also what the
   empty half of a wide monitor is made of; and the foot of the public site, as a band under the
   last line. A texture that turns up on every screen stops being one.

   Decoration, and said so in both directions: aria-hidden in the markup, nothing hit-testable
   here. */

.dot-field {
    inset: 0;
    pointer-events: none;
    position: absolute;

    /* Read by dot-field.js, which draws the same two colours onto a canvas. In channels because
       that is the one form canvas can take: it cannot resolve a var() or a color-mix(), and a
       palette written into the script would be a palette in two places. --dot-live is the
       accent this portal is retuned to, and a test holds the two spellings together. */
    --dot-rest: 140 144 151;
    --dot-live: 107 63 212;
}

/* What the script draws on, once it has. Sized by the host rather than by its own attributes,
   which are in device pixels and would otherwise be read as CSS ones. */
.dot-canvas {
    display: block;
    height: 100%;
    width: 100%;
}

/* And with the canvas there, this grid steps aside: the two draw the same dots in the same
   places, and both at once is one grid drawn twice. */
:root.dots-drawn .dot-field::before {
    display: none;
}

/* The grid at rest: grey, and barely there. Anything stronger competes with the words in front
   of it. This is the whole of what somebody without the script sees, and it is meant to look
   deliberate rather than unfinished. */
.dot-field::before {
    background: color-mix(in srgb, var(--ink-faint) 30%, transparent);
    content: "";
    inset: 0;
    position: absolute;

    -webkit-mask-image: radial-gradient(circle at center, #000 1.4px, transparent 1.6px);
    -webkit-mask-size: 26px 26px;
    mask-image: radial-gradient(circle at center, #000 1.4px, transparent 1.6px);
    mask-size: 26px 26px;
}

/* Fixed rather than absolute: on this page the grid belongs to the window, not to the form, and
   on a wide monitor it is the thing either side of the two panels. */
.sign-in-dots {
    position: fixed;
    z-index: 0;
}

/* Both panels above it. Neither is positioned otherwise, and an unpositioned card paints its
   background underneath anything that is - which would leave the card behind the dots. */
.account-split > .account-pitch,
.account-split > .account-column {
    position: relative;
    z-index: 1;
}

.provider-button svg {
    flex: none;
}

/* --- the public frame -------------------------------------------------------

   The header a visitor gets, and the footer under every public page. Both are
   drawn only when nobody is signed in - somebody with an account has all of it
   in the drawer already. */

.public-nav {
    align-items: center;
    display: flex;

    /* Takes the rest of the bar rather than being pushed to its end, so the links start beside
       the lockup and the one control on the header can hold the far edge on its own. */
    flex: 1;
    gap: 0.35rem;
    min-width: 0;
}

.nav-link,
.nav-summary {
    align-items: center;
    border-radius: var(--radius);
    color: var(--ink-soft);
    cursor: pointer;
    display: flex;
    font-size: 0.94rem;
    font-weight: 600;
    gap: 0.3rem;
    list-style: none;
    padding: 0.45rem 0.7rem;
    text-decoration: none;
    transition: background-color 0.12s ease, color 0.12s ease;
}

.nav-link:hover,
.nav-summary:hover {
    background: color-mix(in srgb, var(--ink) 5%, transparent);
    color: var(--ink);
}

.nav-summary::-webkit-details-marker {
    display: none;
}

.nav-menu {
    position: relative;
}

.nav-menu[open] .chevron {
    transform: rotate(180deg);
}

.nav-menu .chevron {
    transition: transform 0.15s ease;
}

.nav-panel {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    display: flex;
    flex-direction: column;
    left: 0;
    min-width: 13rem;
    padding: 0.4rem;
    position: absolute;
    top: calc(100% + 0.4rem);
    z-index: 20;
}

/* Tighter than a button on the page. The bar is 3.5rem tall and a full-sized
   button in it pushes the brand mark off centre. */
/* Shelli's own lockup, left of everything. The artwork is a wordmark with its own padding,
   so it is sized on height and left to keep its ratio. */
.site-brand {
    align-items: center;
    display: flex;
    flex: none;
    text-decoration: none;
}

.site-brand img {
    display: block;
    height: 30px;
    width: auto;
}

/* Centred on its own text, not just as a box.

   .button is inline-block, so the nav row centred the button while its label sat on a
   baseline the flex-centred links beside it do not share - which reads as the one control on
   the header being a pixel low. A flex box with a matched height puts all four on one line. */
.nav-cta {
    align-items: center;
    display: inline-flex;
    justify-content: center;
    min-height: 2.25rem;
    padding: 0.42rem 1rem;
    white-space: nowrap;
}

/* The one control on the bar sat 11px below the words beside it. .page-header .button adds a
   top margin - right for a heading row where the button hangs off a block of text, wrong inside
   a nav that has already centred everything. Cleared here, and the row is one band again. */
.public-nav .nav-cta {
    align-self: center;

    /* The far edge. Everything before it is the site map and sits where reading starts. */
    margin-left: auto;
    margin-top: 0;
}

/* The links get the same height, so the row is one band rather than four boxes of three
   different heights that happen to share a centre. */
.public-nav .nav-link,
.public-nav .nav-summary {
    min-height: 2.25rem;
}

/* The narrow bar keeps the two buttons and drops the words. A visitor on a
   phone needs the way in, not the site map - the footer carries that. */
@media (max-width: 48rem) {
    .nav-link-quiet,
    .nav-menu {
        display: none;
    }
}

/* The band of dots sits in the space the bottom padding opens up. It is padding rather than a
   margin on the mark above it because the band is positioned against this box, and a margin
   would leave it outside. */
.public-footer {
    border-top: 1px solid var(--line);
    margin-top: 2rem;
    padding: clamp(2.5rem, 5vw, 3.5rem) max(1.5rem, calc((100% - 78rem) / 2)) 8.5rem;
    position: relative;
}

/* Under the last line, never behind it. The dots are the end of the page rather than a texture
   the links have to be read through. */
.footer-dots {
    height: 7rem;
    inset: auto 0 0;
}

.footer-columns {
    display: grid;
    gap: 2rem;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
}

.footer-column h2 {
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    margin: 0 0 0.75rem;
    text-transform: uppercase;
}

.footer-column a {
    color: var(--ink-soft);
    display: block;
    margin-bottom: 0.45rem;
    text-decoration: none;
}

.footer-column a:hover {
    color: var(--accent);
}

.footer-mark {
    border-top: 1px solid var(--line);
    color: var(--ink-faint);
    font-size: 0.85rem;
    margin: 2rem 0 0;
    padding-top: 1.25rem;
}

/* --- the products, and the pricing page -------------------------------------*/

.products {
    margin-bottom: var(--landing-gap);
    padding-inline: max(1.5rem, calc((100% - 78rem) / 2));
}

.product-grid {
    display: grid;
    gap: 1.25rem;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
}

.product-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    display: flex;
    flex-direction: column;
    padding: 1.75rem;
    transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

.product-card:hover {
    border-color: color-mix(in srgb, var(--accent) 55%, var(--line));
    box-shadow: var(--shadow);
    transform: translateY(-2px);
}

.product-card h3 {
    font-size: 1.3rem;
    margin: 0 0 0.25rem;
}

.product-for {
    color: var(--accent);
    font-size: 0.88rem;
    font-weight: 600;
    margin: 0 0 0.9rem;
}

.product-card p {
    color: var(--ink-soft);
    line-height: 1.6;
    margin: 0 0 1rem;
}

.product-from {
    color: var(--ink);
    font-weight: 700;
    margin-top: auto;
}

.product-card .button {
    margin-top: 0.75rem;
    text-align: center;
}

.pricing-head {
    padding: clamp(2rem, 5vw, 3.25rem) max(1.5rem, calc((100% - 78rem) / 2)) 0;
    text-align: center;
}

.pricing-head h1 {
    font-size: clamp(1.9rem, 4vw, 2.75rem);
    margin: 0 0 0.5rem;
}

/* What Shelli is, above the terms: reading width, darker than the line under it. */
.pricing-what {
    color: var(--ink);
    font-size: 1.05rem;
    margin: 0 auto 0.6rem;
    max-width: 48rem;
}

/* Two products, one of which is the page you are on. A pair of tabs rather
   than a dropdown: with two choices a menu is a click that hides one word. */
.pricing-switch {
    display: flex;
    gap: 0.4rem;
    justify-content: center;
    margin: 1.5rem 0 0;
}

.switch-link {
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--ink-soft);
    font-weight: 600;
    padding: 0.4rem 1.1rem;
    text-decoration: none;
}

.switch-current {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

.compare {
    margin-bottom: var(--landing-gap);
    padding-inline: max(1.5rem, calc((100% - 78rem) / 2));
}

/* A line of the comparison is a sentence, not a column heading, and it has to say so loudly
   because `.data-table th` does not know the difference. That rule is written for the row across
   the top - 0.72rem, faint, uppercase, wide-tracked - and every name here is marked up as `th`
   for the screen reader rather than for the look of it.

   It survived while the table held nine two-word capabilities: small caps read as a style. It
   stopped surviving the moment the lines grew a sentence underneath, because the half that
   actually sells was rendering as forty lines of tiny faint shouting. */
.compare-table th[scope="row"] {
    color: var(--ink);
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: normal;
    text-align: left;
    text-transform: none;
}

.compare-about {
    color: var(--ink-soft);
    display: block;
    font-size: 0.85rem;
    font-weight: 400;
    letter-spacing: normal;
    margin-top: 0.2rem;
    max-width: 46ch;
    text-transform: none;
}

.compare-table td {
    text-align: center;
}

.compare-yes {
    color: var(--good);
    font-size: 1.2rem;
    font-weight: 700;
}

.compare-no {
    color: var(--ink-faint);
}

/* --- the price list ---------------------------------------------------------

   The cards themselves are the portal's, from nexus.css, so what follows is only what a shop
   window needs and a shop's own page does not: room for three of them, a term switch, and the
   trial. */

/* Wider than the rest of the page: four cards on 78rem came out tall and narrow. */
.pricing-plans {
    padding-inline: max(1.5rem, calc((100% - 88rem) / 2));

    /* Room under the band. On a market with no published prices the cards are short, and without
       this the tinted panel stopped a few pixels under the last line of them - which reads as the
       section having been cut off rather than as it having finished. */
    padding-bottom: clamp(2rem, 4vw, 3.25rem);
}

/* Wider than the portal's minimum. Inside a shop the cards sit in a column beside the
   navigation; here they have the page, and three 230px cards on a 78rem field read as a row of
   receipts. */
.pricing-plans .offer-grid {
    gap: 1.25rem;

    /* Three across before the cards get narrow enough to read as receipts, and two below that
       rather than three cramped ones. 20rem is the width at which a plan card still holds its
       price on one line with the feature list underneath it legible. */

    /* One row of equal rows. Grid stretches items to the tallest by default, but only within
       a row it has decided on - and a card that wrapped onto a second row was sized against
       whatever landed beside it rather than against the plan everybody is comparing to. */
    grid-auto-rows: 1fr;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    margin-bottom: 1.5rem;
}

/* Roomier than the portal's. Inside an account these sit beside a navigation drawer and are
   read by somebody who has already bought; here they are the page, and the whole decision is
   made off them. */
.pricing-plans .offer {
    padding: 2rem 1.6rem;
}

.pricing-plans .offer-name {
    font-size: 1.35rem;
}

.pricing-plans .offer-price {
    font-size: 2.1rem;
}

/* margin-top auto, so every button in a row sits on one line however much wording the card
   above it carries. */
.pricing-plans .offer .button {
    margin-top: auto;
    text-align: center;
}

/* The trial is a different offer, not a cheaper plan. Marked rather than coloured: a filled card
   beside two plain ones reads as the recommended one, which is the opposite of what it is.

   It keeps the surface. Transparent was fine on a white page and wrong the moment the plans got
   a tinted band behind them - the card stopped having a face and read as an empty slot between
   two real ones. The dashed edge is the whole of the mark. */
.offer-trial {
    background: var(--surface);
    border-style: dashed;
    border-width: 2px;
}

/* The plan's own headline facts - how many tills, how many more. Darker than the feature lists
   under them, because this is what the price buys rather than what the shop can do with it. */
.offer-plain li {
    color: var(--ink);
    justify-content: flex-start;
}

/* Month or year. The same pair of tabs the products wear above, so a reader meets one control
   twice rather than two controls once. */
/* Which price list. Above the term switch, because it decides the currency the term is then
   counted in, and a reader who meets the months before the money has to go back up. Quieter than
   the product tabs: it answers a question most readers never ask, and only the one who is in the
   wrong currency is looking for it. */
/* Space under it as well as over it.

   The gap below these switches was on .term-switch alone, and that one is only drawn where a
   market publishes a yearly price. On the worldwide list, which publishes none, the price-list
   tabs sat directly on top of the Basic card with nothing between them. Adjacent margins
   collapse, so putting it here costs nothing on a market that draws both. */
.market-switch {
    flex-wrap: wrap;
    margin-bottom: 2.25rem;
    margin-top: 1.75rem;
}

.market-switch .switch-link {
    font-size: 0.9rem;
    font-weight: 500;
    padding: 0.3rem 0.9rem;
}

/* Nine editions in one row pushed the phone page sideways. */
.edition-switch {
    align-items: center;
    flex-wrap: wrap;
}

.edition-switch .field-hint {
    color: var(--ink);
    flex-basis: 100%;
    font-weight: 600;
    text-align: center;
}

/* The kinds of business in Shelli's purple, tinted even when not chosen, so they read as one set
   and apart from the ink-coloured filter under them. */
.edition-switch .switch-link {
    font-size: 0.9rem;
    font-weight: 500;
    padding: 0.3rem 0.9rem;
}

.edition-switch .switch-link:not(.switch-current) {
    background: color-mix(in srgb, var(--accent) 6%, var(--surface));
    border-color: color-mix(in srgb, var(--accent) 30%, var(--line));
    color: var(--accent-dark);
}

/* The card reads in the order the decision is made: who it is for, the price, the button, then
   a few lines and the way to the whole list. Every line used to be on the card, and Pro's ran
   the length of the page. The description keeps two lines so the prices sit level. */
.pricing-plans .offer-about {
    min-height: 2.9em;
}

.pricing-plans .offer .offer-cta {
    margin-top: 0;
}

.offer-devices {
    color: var(--ink);
    font-size: 0.85rem;
    margin: 0;
}

.offer-add {
    color: var(--ink-soft);
    font-size: 0.8rem;
    margin: 0;
}

.offer-more {
    color: var(--accent-dark);
    font-size: 0.85rem;
    font-weight: 600;
    margin-top: auto;
    padding-top: 0.5rem;
    text-decoration: none;
}

.offer-more::after {
    content: " \2192";
}

.offer-featured .offer-devices,
.offer-featured .offer-add {
    color: color-mix(in srgb, #ffffff 85%, transparent);
}

.offer-featured .offer-more {
    color: #ffffff;
}

/* The kinds of business on the editions card, each a way to its own card on the editions page. */
.offer-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.offer-chips a {
    border: 1px solid color-mix(in srgb, #ffffff 35%, transparent);
    border-radius: 999px;
    color: #ffffff;
    font-size: 0.75rem;
    padding: 0.15rem 0.6rem;
    text-decoration: none;
}

.offer-chips a:hover {
    background: color-mix(in srgb, #ffffff 15%, transparent);
}

/* Four plans in one row on a wide screen. At 20rem the fourth wrapped onto a row of its own. */
.pricing-plans .offer-grid-four {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15.5rem), 1fr));
}

.pricing-plans .offer-grid-four .offer {
    padding: 1.75rem 1.25rem;
}

.pricing-plans .offer-grid-four .offer-price {
    font-size: 1.8rem;
}

/* One card a row on a phone, where equal rows gave every card Pro's height and left the editions
   card half empty. */
@media (max-width: 40rem) {
    .pricing-plans .offer-grid {
        grid-auto-rows: auto;
    }
}

/* The editions are what is sold first, so the card wears the recommended plan's colour. The
   dashed outline it had read as unfinished. */
.offer-editions {
    border: 1px solid var(--accent-dark);
}

/* On the editions page the band holds only the switches, so it is a strip rather than a stage. */
.pricing-plans.pricing-plans-bare {
    padding-block: 1.25rem;
}

.pricing-plans-bare .term-switch {
    margin-bottom: 1rem;
}

/* A link to one edition lands on its card below the sticky header rather than under it. */
.who-card[id] {
    scroll-margin-top: 5.5rem;
}

.pricing-back {
    margin-top: 1rem;
    text-align: center;
}

.vs-form {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem 0.9rem;
    justify-content: center;
    margin: 0 auto 1.75rem;
    max-width: 52rem;
}

.vs-form select {
    flex: 1 1 14rem;
    min-width: 0;
}

.vs-grid {
    display: grid;
    gap: 1.25rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
    margin-bottom: 1.25rem;
}

.vs-both {
    margin-bottom: 1.5rem;
}

.vs-both .who-gets {
    columns: 2 14rem;
}

/* The currency beside the country, because "Colombia" and "COP" answer different halves of the
   same question: a reader recognises the country and checks the currency. */
.switch-currency {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    margin-left: 0.35rem;
    opacity: 0.65;
}

.term-switch {
    margin-bottom: 2.25rem;
}

/* The reason to press it, on the tab itself. A discount announced only after the tab is chosen
   is a discount most readers never find. */
.switch-flag {
    background: var(--good);
    border-radius: 999px;
    color: #ffffff;
    font-size: 0.7rem;
    font-weight: 700;
    margin-left: 0.4rem;
    padding: 0.1rem 0.45rem;
    white-space: nowrap;
}

.switch-current .switch-flag {
    background: #ffffff;
    color: var(--accent-dark);
}

/* --- what each plan includes ------------------------------------------------

   The table in a card, bordered in the same purple the recommended plan wears. It is the thing
   this page exists for and it was the flattest block on it: a grid of ticks with no edge, under
   two rows of coloured cards. */

.compare-card {
    background: var(--surface);
    border: 2px solid var(--accent);
    border-radius: calc(var(--radius) * 1.75);

    /* Tinted with the border's own colour rather than black, so the card reads as lit from the
       accent instead of merely raised. */
    box-shadow: 0 20px 45px -30px color-mix(in srgb, var(--accent) 75%, transparent);
    padding: 1.5rem;
}

/* The recommended plan's column, tinted down its whole length. Without it the header cell was
   the only thing marking which column a reader had come to look at, and it scrolls away. */
.compare-featured {
    background: color-mix(in srgb, var(--accent) 7%, transparent);
}

.compare-table th[scope="col"] {
    font-size: 0.95rem;
    text-align: center;
}

/* Except the first, which heads the column of capability names rather than a column of ticks.
   Centred, it floated over left-aligned rows with no relationship to any of them. */
.compare-table th[scope="col"]:first-child {
    text-align: left;
}

.compare-table th[scope="col"].compare-featured {
    color: var(--accent-dark);
}

/* Bought on top rather than included, and it must not look like either a tick or a dash. Worded
   and boxed, because "Extra" set as plain text in a column of symbols reads as a missing icon. */
.compare-extra {
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    border-radius: 999px;
    color: var(--accent-dark);
    display: inline-block;
    font-size: 0.75rem;
    font-weight: 700;
    padding: 0.1rem 0.55rem;
    white-space: nowrap;
}

/* A heading across the table, opening each group.

   The comparison is forty rows now that it can say what both plans do, and read as one
   undifferentiated list a shopkeeper looking for the counter had to scan past the reports to
   find it. Full width and sticky-looking rather than boxed: it is a divider, not a row with an
   answer.

   Named through the table and the row, because `.shell-customer .data-table th` tints every
   heading cell in the portal and outranked a plain `.compare-group th`: the bands only ever
   showed that lavender. */
.compare-table tr.compare-group th {
    background: color-mix(in srgb, var(--tone) 11%, transparent);
    border-left: 4px solid var(--tone);
    border-radius: var(--radius);
    color: var(--tone);
    font-size: 0.8rem;
    letter-spacing: 0.06em;
    padding: 0.6rem 0.75rem;
    text-align: left;
    text-transform: uppercase;
}

.compare-group .glyph {
    margin-right: 0.45rem;
    vertical-align: -0.25em;
}

/* Each group in its own colour: the band across it and the names under it. A table of forty
   rows in one purple read as one long list; in colours it reads as sections. A group with no
   tone keeps the accent. */
.compare-table tbody {
    --tone: var(--accent-dark);
}

.compare-table tbody.tone-violet { --tone: #5a33b8; }
.compare-table tbody.tone-orange { --tone: #c2410c; }
.compare-table tbody.tone-teal { --tone: #0f766e; }
.compare-table tbody.tone-slate { --tone: #3f5873; }
.compare-table tbody.tone-pink { --tone: #b02e8a; }
.compare-table tbody.tone-amber { --tone: #a35c00; }
.compare-table tbody.tone-blue { --tone: #1d58c9; }
.compare-table tbody.tone-green { --tone: #1d7a46; }

.compare-table tbody th[scope="row"] {
    border-left: 3px solid color-mix(in srgb, var(--tone) 35%, transparent);
    color: var(--tone);
}

/* On a phone the portal turns every table into a stack of cards, each cell under its own label.
   A comparison read that way put Basic and Pro one above the other with the name boxed above
   them; here the name runs across the card and the two plans sit side by side under it. */
@media (width <= 860px) {
    .compare-card {
        padding: 0.9rem;
    }

    .compare-table tr.compare-group {
        background: none;
        border: 0;
        margin: 1.1rem 0 0.6rem;
        padding: 0;
    }

    .compare-table tbody:first-of-type tr.compare-group {
        margin-top: 0;
    }

    .compare-table tr.compare-group th {
        display: block;
        width: 100%;
    }

    .compare-table tbody th[scope="row"] {
        background: none;
        border-left: 0;
        display: block;
        padding: 0 0 0.55rem;
        width: 100%;
    }

    .compare-table tbody tr:not(.compare-group) {
        border-left: 4px solid var(--tone);
    }

    .compare-table td {
        align-items: center;
        border-radius: var(--radius);
        display: inline-flex;
        gap: 0.6rem;
        padding: 0.35rem 0.6rem;
        width: 50%;
    }

    .compare-table td::before {
        font-size: 0.72rem;
    }
}

/* The first group heading sits directly under the column titles, where the border above it
   would read as a rule the header already draws. */
.compare-table tbody:first-of-type .compare-group th {
    padding-top: 0.35rem;
}

/* A count or a phrase instead of a tick - "one per licence", "up to 3". Set as words rather
   than as a symbol, because it is the half of the table that stops the cheaper plan reading as
   poor: a column of ticks and dashes says Basic lacks things, and a number says Basic is a
   size. */
.compare-count {
    font-size: 0.85rem;
    font-weight: 600;
    white-space: nowrap;
}

/* Inside the card, so it is left with the table rather than centred under the page. */
.compare-note {
    margin-top: 1.25rem;
    max-width: 60ch;
    text-align: left;
}

/* --- what the page is arguing ------------------------------------------------

   Everything below carries the same claim in a different form: a business should not have to
   leave when it grows. Three stages, four rungs, one diagram. */

.hero-trust {
    color: var(--ink-faint);
    font-size: 0.82rem;
    letter-spacing: 0.04em;
    margin: 1.25rem 0 0;
    text-transform: uppercase;
}

.grow-list {
    counter-reset: none;
    display: grid;
    gap: 1.5rem;
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    list-style: none;
    margin: 0;
    padding: 0;
}

.grow-stage h3 {
    font-size: 1.1rem;
    margin: 0 0 0.4rem;
}

.grow-stage p {
    color: var(--ink-soft);
    line-height: 1.6;
    margin: 0;
}

/* The bar is the argument, and it is why these are three cards rather than three paragraphs:
   the mark gets longer before anybody has read a word. */
.grow-bar {
    background: linear-gradient(90deg, var(--accent), var(--accent-bright));
    border-radius: 999px;
    display: block;
    height: 5px;
    margin-bottom: 1rem;
}

.grow-bar-1 { width: 30%; }
.grow-bar-2 { width: 65%; }
.grow-bar-3 { width: 100%; }

/* --- one shop to a chain ---------------------------------------------------- */

/* Four across, two by two, or one above another. Never three and one: the last rung alone on its
   own row reads as an afterthought rather than the top of the ladder. */
.rungs {
    counter-reset: none;
    display: grid;
    gap: 1.25rem;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    list-style: none;
    margin: 0;
    padding: 0;
}

@media (max-width: 60rem) {
    .rungs {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 40rem) {
    .rungs {
        grid-template-columns: minmax(0, 1fr);
    }
}

.rung {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: calc(var(--radius) * 1.5);
    padding: 1.4rem 1.25rem;
    transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

.rung:hover {
    border-color: color-mix(in srgb, var(--accent) 55%, var(--line));
    box-shadow: var(--shadow);
    transform: translateY(-2px);
}

.rung h3 {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    font-size: 1.05rem;
    gap: 0.5rem;
    margin: 0 0 0.4rem;
}

.rung p {
    color: var(--ink-soft);
    font-size: 0.92rem;
    line-height: 1.6;
    margin: 0;
}

/* A gauge rather than a number. The rungs are not steps somebody takes in order - most
   businesses stop at the second - so a numbered list would read as a course to complete. */
.rung-mark {
    background: color-mix(in srgb, var(--ink) 10%, transparent);
    border-radius: 999px;
    display: block;
    height: 5px;
    margin-bottom: 1rem;
    overflow: hidden;
}

.rung-fill {
    background: linear-gradient(90deg, var(--accent), var(--accent-bright));
    border-radius: 999px;
    display: block;
    height: 100%;
}

.rung-fill-1 { width: 25%; }
.rung-fill-2 { width: 50%; }
.rung-fill-3 { width: 75%; }
.rung-fill-4 { width: 100%; }

/* --- the ecosystem -----------------------------------------------------------

   A three by three grid with the hub in the middle cell. Built from the page's own elements
   rather than drawn in SVG, because the labels are translated and SVG text does not wrap. */

.eco {
    display: grid;
    gap: 0.85rem;
    grid-template-areas:
        "nw n  ne"
        "w  hub e"
        "sw s  se";
    grid-template-columns: repeat(3, 1fr);
}

.eco-hub {
    align-items: center;
    background: linear-gradient(160deg, var(--accent) 0%, var(--accent-dark) 100%);
    border-radius: calc(var(--radius) * 1.5);
    color: #ffffff;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    grid-area: hub;
    justify-content: center;
    padding: 1.75rem 1.25rem;
    text-align: center;
}

.eco-hub-name {
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -0.01em;
}

.eco-hub-note {
    color: color-mix(in srgb, #ffffff 78%, transparent);
    font-size: 0.85rem;
}

.eco-node {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: calc(var(--radius) * 1.5);
    display: flex;
    flex-direction: column;
    font-size: 0.92rem;
    font-weight: 600;
    gap: 0.35rem;
    justify-content: center;
    min-height: 4.5rem;
    padding: 1rem;
    text-align: center;
    transition: border-color 0.15s ease, transform 0.15s ease;
}

.eco-node:hover {
    border-color: var(--accent-bright);
    transform: translateY(-2px);
}

.eco-nw { grid-area: nw; }
.eco-n  { grid-area: n; }
.eco-ne { grid-area: ne; }
.eco-w  { grid-area: w; }
.eco-e  { grid-area: e; }
.eco-sw { grid-area: sw; }
.eco-s  { grid-area: s; }
.eco-se { grid-area: se; }

/* The two products either side of the middle, marked as the two things a customer actually
   chooses between. Everything else on this diagram is something they add to one of them. */
.eco-n,
.eco-ne {
    border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
}

/* Below the grid it is a list, and the hub reads first - which is the reading order the
   markup already has, so nothing needs reordering. */
@media (max-width: 47rem) {
    .eco {
        grid-template-areas: none;
        grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    }

    .eco-hub {
        grid-column: 1 / -1;
    }

    .eco-nw, .eco-n, .eco-ne, .eco-w, .eco-e, .eco-sw, .eco-s, .eco-se {
        grid-area: auto;
    }
}

/* --- the portal, and the one claim worth being careful with ------------------ */

.nexus-grid {
    display: grid;
    gap: 1.5rem;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    margin-bottom: 1.75rem;
}

.nexus-point h3 {
    font-size: 1.05rem;
    margin: 0 0 0.4rem;
}

.nexus-point p {
    color: var(--ink-soft);
    line-height: 1.6;
    margin: 0;
}

/* Set apart from the three points above it, because it is the answer to the question those
   three provoke: if the reports are here, where is my business? */
.privacy-note {
    background: var(--surface);
    border: 1px solid var(--line);
    border-left: 3px solid var(--good);
    border-radius: var(--radius);
    padding: 1.25rem 1.5rem;
}

.privacy-note h3 {
    font-size: 1rem;
    margin: 0 0 0.4rem;
}

.privacy-note p {
    color: var(--ink-soft);
    line-height: 1.6;
    margin: 0;
    max-width: 68ch;
}

/* --- bought on top ---------------------------------------------------------- */

.module-grid {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    margin-bottom: 1.25rem;
}

.module {
    background: var(--surface);
    border: 1px dashed var(--line);
    border-radius: calc(var(--radius) * 1.5);
    padding: 1.25rem;
}

.module h3 {
    font-size: 1.02rem;
    margin: 0 0 0.35rem;
}

.module p {
    color: var(--ink-soft);
    font-size: 0.92rem;
    line-height: 1.55;
    margin: 0;
}

/* --- the chat panel ---------------------------------------------------------

   Fixed to the bottom right, over everything, on the public pages only. The number and the
   opening message come from Admin, so the panel exists on the page exactly when somebody has
   set one - there is no dead state to style.

   Green rather than the brand purple: this is the one control on the page that leaves for
   another application, and every reader already knows what that green means. It is the only
   place on the site that borrows a colour it did not choose.

   It opens on a checkbox and shuts on the same one, which is why there is no script behind any
   of this. The order in the markup is the order the rules need - the input first, then what it
   switches - because a sibling combinator only ever looks forward. */

.chat {
    bottom: 1.5rem;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.85rem;
    position: fixed;
    right: 1.5rem;

    /* Above the page and below a dialog. A chat button floating over a confirmation is a
       button covering the thing somebody is being asked to confirm. */
    z-index: 40;
}

/* The switch itself never draws. `.visually-hidden` in the markup takes it out of the layout
   without taking it out of the tab order, so this stays operable from a keyboard. */
.chat-toggle {
    position: absolute;
}

/* --- the button ----------------------------------------------------------- */

.chat-fab {
    align-items: center;
    background: var(--wa-green);
    border-radius: 50%;
    box-shadow: 0 10px 30px -8px rgb(20 23 28 / 45%);
    color: #ffffff;
    cursor: pointer;
    display: inline-flex;
    height: 3.6rem;
    justify-content: center;
    order: 2;

    /* So the ring below has something to sit behind. */
    position: relative;

    transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
    width: 3.6rem;
}

.chat-fab:hover {
    box-shadow: 0 14px 34px -8px rgb(20 23 28 / 55%);
    transform: translateY(-2px) scale(1.04);
}

/* The focus ring belongs to the checkbox and has to be drawn on what stands in for it. */
.chat-toggle:focus-visible + .chat-panel + .chat-fab {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

/* Shut, the mark. Open, the cross. One or the other, never both. */
.chat-fab-close,
.chat-toggle:checked + .chat-panel + .chat-fab .chat-fab-open {
    display: none;
}

.chat-toggle:checked + .chat-panel + .chat-fab {
    background: var(--ink);
}

.chat-toggle:checked + .chat-panel + .chat-fab .chat-fab-close {
    display: block;
}

/* A ring that leaves the button and fades, once every few seconds, so a reader who has scrolled
   past four sections still notices there is somebody to ask. Drawn on a pseudo-element rather
   than the button, so the button's own hover is not fighting it. */
.chat-fab::after {
    animation: chat-pulse 3.4s ease-out infinite;
    background: var(--wa-green);
    border-radius: 50%;
    content: "";
    inset: 0;
    position: absolute;
    z-index: -1;
}

@keyframes chat-pulse {
    0% { opacity: 0.55; transform: scale(1); }
    70% { opacity: 0; transform: scale(1.75); }
    100% { opacity: 0; transform: scale(1.75); }
}

/* Nothing pulses once the panel is open: the reader has already answered it. */
.chat-toggle:checked + .chat-panel + .chat-fab::after {
    animation: none;
    opacity: 0;
}

/* --- the panel ------------------------------------------------------------ */

.chat-panel {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 1.1rem;
    box-shadow: 0 24px 60px -20px rgb(20 23 28 / 45%);
    display: none;
    order: 1;
    overflow: hidden;
    width: min(21rem, calc(100vw - 3rem));
}

.chat-toggle:checked + .chat-panel {
    animation: chat-open 0.18s ease-out;
    display: block;
}

@keyframes chat-open {
    from { opacity: 0; transform: translateY(0.75rem); }
    to { opacity: 1; transform: translateY(0); }
}

.chat-panel-head {
    align-items: center;
    background: var(--wa-green);
    color: #ffffff;
    display: flex;
    gap: 0.7rem;
    padding: 0.95rem 1.1rem;
}

.chat-panel-mark {
    align-items: center;
    background: rgb(255 255 255 / 22%);
    border-radius: 50%;
    display: inline-flex;
    flex: none;
    height: 2.4rem;
    justify-content: center;
    width: 2.4rem;
}

.chat-panel-words {
    display: flex;
    flex-direction: column;
    line-height: 1.3;
}

.chat-panel-words strong {
    font-size: 1rem;
}

.chat-panel-words span {
    font-size: 0.82rem;
    opacity: 0.92;
}

.chat-form {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    padding: 1rem;
}

.chat-form input,
.chat-form textarea {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 0.6rem;
    color: var(--ink);
    font: inherit;
    font-size: 0.9rem;
    padding: 0.6rem 0.7rem;
    width: 100%;
}

.chat-form textarea {
    min-height: 5rem;
    resize: vertical;
}

.chat-form input:focus-visible,
.chat-form textarea:focus-visible {
    border-color: var(--wa-green);
    outline: 2px solid var(--wa-green);
    outline-offset: 1px;
}

/* Green, not the brand purple, for the same reason the button is: it is the control that
   leaves. `.button` gives it the shape and this gives it the colour.

   `display: flex` is the load-bearing line. `.button` is inline-block, so `gap` and
   `justify-content` are inert on it and a `display: block` mark inside becomes a box of its
   own - which is what put WhatsApp's glyph on a line above its own label, hard against the
   left edge, while the words sat centred underneath. The Google button beside the sign-in
   form has said this for months; this is the same three lines. */
.chat-send {
    align-items: center;
    background: var(--wa-green);
    border-color: var(--wa-green);
    color: #ffffff;
    display: flex;
    gap: 0.5rem;
    justify-content: center;
    margin-top: 0.2rem;
    padding-block: 0.62rem;
    width: 100%;
}

.chat-send:hover {
    background: var(--wa-green-dark);
    border-color: var(--wa-green-dark);
    color: #ffffff;
}

/* The panel fills the width it is given on a phone and the button sits under it, which is the
   only arrangement where both fit over a page somebody is reading. */
@media (max-width: 40rem) {
    .chat {
        bottom: 1rem;
        left: 1rem;
        right: 1rem;
    }

    .chat-panel {
        width: 100%;
    }
}

/* Somebody who asked for less movement gets a button that does not lift, and no ring at all. */
@media (prefers-reduced-motion: reduce) {
    .chat-fab,
    .chat-panel {
        transition: none;
    }

    .chat-fab::after,
    .chat-toggle:checked + .chat-panel {
        animation: none;
    }

    .chat-fab:hover {
        transform: none;
    }
}

/* --- The tools ---------------------------------------------------------------- */

/* A colour for each tool, in one block, spelled with the key that names everything else
   about it. Anything wearing `.tool-tint-<key>` - a card, a page head, the drawing itself -
   takes that colour, and a tool added without a line here draws in the ink grey rather than
   drawing nothing, which is the failure that would ship unnoticed.

   Borrowed from nowhere: five hues far enough apart to be told apart at the size of a card,
   and each dark enough to carry a line drawing on white. WhatsApp's is WhatsApp's, because
   that tool builds a link into WhatsApp and a purple one would be a drawing of the wrong
   application. */
.tool-tint-CheckDigit {
    --tool-tint: #6d4aff;
}

.tool-tint-Tax {
    --tool-tint: #0f9d6b;
}

.tool-tint-Margin {
    --tool-tint: #d97008;
}

.tool-tint-Discount {
    --tool-tint: #d8385a;
}

.tool-tint-WhatsApp {
    --tool-tint: var(--wa-green);
}

/* The drawing, on a disc of its own colour. The fallback is what a tool with no tint gets:
   grey, visible, and obviously not finished. */
.tool-glyph {
    align-items: center;
    background: color-mix(in srgb, var(--tool-tint, var(--ink-faint)) 12%, transparent);
    border-radius: 30%;
    color: var(--tool-tint, var(--ink-faint));
    display: inline-flex;
    flex: none;
    height: 3.4rem;
    justify-content: center;
    transition: transform 0.2s ease, background 0.2s ease;
    width: 3.4rem;
}

/* --- the list ------------------------------------------------------------- */

.tool-cards {
    margin-top: 2.5rem;
}

/* The drawing above the name rather than beside it: the names are two and three words long
   in Spanish and a row that wraps under its own icon reads as a card that broke. */
.tool-card {
    align-items: flex-start;
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

.tool-card h3,
.tool-card p {
    margin: 0;
}

.tool-card:hover,
.tool-card:focus-visible {
    border-color: color-mix(in srgb, var(--tool-tint, var(--accent)) 45%, var(--line));
    box-shadow: 0 16px 34px -18px color-mix(
        in srgb, var(--tool-tint, var(--accent)) 70%, transparent);
    transform: translateY(-3px);
}

/* The drawing answers the hover as well as the card does. Small: a glyph that jumps is a
   glyph somebody notices instead of reading the name under it. */
.tool-card:hover .tool-glyph,
.tool-card:focus-visible .tool-glyph {
    background: color-mix(in srgb, var(--tool-tint, var(--ink-faint)) 20%, transparent);
    transform: scale(1.08) rotate(-4deg);
}

/* --- one tool ------------------------------------------------------------- */

/* Carries the tint and nothing else. No box, so the sections it wraps stay where the page
   column put them - and the property still inherits, which is the whole reason it is here. */
.tool-page {
    display: contents;
}

/* Centred like the pricing head. A glow rather than a band: the head sits inside the ordinary
   page column, and a rectangle of colour that stops at the column edge reads as a block
   somebody forgot to make full width. This fades out before it gets there. */
.tool-head {
    background: radial-gradient(
        70% 130% at 50% 0%,
        color-mix(in srgb, var(--tool-tint, var(--accent)) 15%, transparent) 0%,
        transparent 72%);
    padding-bottom: 1.25rem;
    padding-top: clamp(1.5rem, 4vw, 2.75rem);
    text-align: center;
}

/* The drawing sits between the way back and the title, so the page opens the same colour as
   the card it was reached from. */
.tool-head .tool-glyph {
    height: 4rem;
    margin-bottom: 0.9rem;
    width: 4rem;
}

/* A tool is one short form and its answer, and `.card-wide` is the full width of the page
   column - so every calculator drew a card with its fields down the left and half a metre of
   nothing to the right of them. Narrowed to what a form is, and centred under a heading that
   was already centred.

   On the tool pages only. `.card-wide` elsewhere holds tables and grids that want the room. */
.tool-page .card-wide {
    margin-inline: auto;
    max-width: 40rem;
}

.tool-head h1 {
    font-size: clamp(1.6rem, 3.5vw, 2.25rem);
    margin: 0 0 0.5rem;
}

.tool-back {
    margin: 0 0 0.75rem;
}

.tool-back a {
    color: var(--ink-faint);
    font-size: 0.9rem;
    text-decoration: none;
}

.tool-back a:hover,
.tool-back a:focus-visible {
    color: var(--accent);
    text-decoration: underline;
}

/* The answer, marked off from the form above it. A rule rather than a second card:
   the numbers belong to the form that produced them, and a card of their own would
   read as a separate thing that happened to appear. */
.tool-answer {
    border-top: 1px solid var(--line);
    margin-top: 1.5rem;
    padding-top: 1.25rem;
}

/* dl overrides sit here because .card dl is in nexus.css, which loads first. Stated
   above it they would lose to it and the grid would stay two narrow columns. */
dl.tool-answer {
    display: grid;
    gap: 0.6rem 1.5rem;
    grid-template-columns: 1fr auto;
    margin: 1.5rem 0 0;
}

dl.tool-answer dt {
    align-self: baseline;
}

dl.tool-answer dd {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    text-align: right;
}

/* The one figure somebody came for, in the colour of the tool that worked it out. */
dl.tool-answer .tool-figure {
    color: var(--tool-tint, var(--accent));
    font-size: 1.35rem;
}

/* The answer arrives rather than appearing, because on a page that reloads to compute there
   is otherwise nothing to say the numbers below are new ones. */
.tool-answer {
    animation: tool-answer-in 0.25s ease-out;
}

@keyframes tool-answer-in {
    from { opacity: 0; transform: translateY(0.4rem); }
    to { opacity: 1; transform: translateY(0); }
}

/* Nothing moves for a reader who asked for less of it. The card still marks itself out on
   hover - that is the border and the shadow, and both stay. */
@media (prefers-reduced-motion: reduce) {
    .tool-card,
    .tool-glyph {
        transition: none;
    }

    .tool-answer {
        animation: none;
    }

    .tool-card:hover,
    .tool-card:focus-visible {
        transform: none;
    }

    .tool-card:hover .tool-glyph,
    .tool-card:focus-visible .tool-glyph {
        transform: none;
    }
}

/* Quieter than the card above it: this is the explanation, and it is under the tool
   because somebody arrives wanting the answer rather than the method. */
.tool-about {
    color: var(--ink-soft);
    font-size: 0.92rem;
    line-height: 1.65;
}

.tool-about h2 {
    font-size: 0.95rem;
    margin: 0 0 0.75rem;
}

.tool-about p {
    margin: 0 0 0.75rem;
}

.tool-about p:last-child {
    margin-bottom: 0;
}

/* The link that gives way when the header runs out of room.

   Measured, not guessed, and measured in Spanish - which is the measurement that decides
   it. In English the header fits: "Tools" is 54px and the row comes to 375px exactly. In
   Spanish "Herramientas" is 111px and "Iniciar sesion" is 122px, and the same row is 404px
   on a 375px phone, so the whole page scrolls sideways. Checking this in English alone
   would have shipped it broken for almost every reader the site has.

   The footer lists every tool by name, so the link is what gives way rather than the
   button somebody came to press. */
@media (max-width: 40rem) {
    .public-nav .nav-link-extra {
        display: none;
    }
}

/* The language, beside the one button on the header. Pushed to the right with it, so the links
   keep their place beside the lockup. */
.nav-language {
    margin-left: auto;
}

.nav-language ~ .nav-cta {
    margin-left: 0;
}

/* On a phone the globe and two letters, not the name: the row has no room for "Português". */
@media (max-width: 40rem) {
    .nav-language .culture-current,
    .nav-language .chevron {
        display: none;
    }

    .nav-language .culture-code {
        display: inline;
    }

    .nav-language .culture-button {
        gap: 0.25rem;
        padding: 0.4rem 0.45rem;
    }
}

/* The sign-in pages carry it in the top right corner, above the dots and both columns. */
.account-language {
    position: absolute;
    right: 1rem;
    top: max(0.75rem, env(safe-area-inset-top));
    z-index: 5;
}

.account-language .culture-button {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 999px;
    box-shadow: var(--shadow);
}

/* One name in the header, not two.

   .topbar .brand is off by default and switched on under 860px, because down there the
   drawer has slid off-canvas and taken the lockup with it. A visitor has no drawer at all
   - it is only rendered for somebody signed in - and already carries Shelli's own lockup
   in .site-brand, so under 860px they were getting both: the Nexus "N" mark and the Shelli
   wordmark beside it, on the one screen that is meant to introduce one product.

   Not scoped to a width. Whenever the header carries Shelli's lockup the Nexus mark is
   redundant, and this rule outranks the one inside the media query on specificity rather
   than on order, so it cannot be undone by somebody moving the breakpoint. */
.topbar:has(.site-brand) .brand {
    display: none;
}

/* The outcome of a payment attempt.

   Centred and large because this is the one page a payer reaches from somebody else's site,
   usually on a phone, wanting one thing: did it work. A heading and a paragraph make that
   something to read; a mark makes it something to see from arm's length.

   The glyph is a stroke over currentColor, so one tone class colours the ring, the drawing
   and the pill together. */
.checkout-outcome {
    text-align: center;
}

.outcome-mark {
    width: 76px;
    height: 76px;
    margin: 0.25rem auto 1.25rem;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.outcome-good {
    background: var(--good-soft);
    color: var(--good);
}

.outcome-warn {
    background: var(--warn-soft);
    color: var(--warn);
}

.outcome-bad {
    background: var(--bad-soft);
    color: var(--bad);
}

.outcome-muted {
    background: var(--paper);
    color: var(--ink-faint);
}

/* Which rail took the money. Quieter than the sentence above it and louder than a footnote:
   it is the line somebody comes back for when they are matching a statement. */
.outcome-rail {
    color: var(--ink-soft);
    margin: 0.35rem 0 0.9rem;
}

/* The buttons follow the text rather than sitting hard left under a centred column. */
.checkout-outcome .offer-foot {
    justify-content: center;
}

/* The moment between choosing a rail and the gateway's own page.

   A spinner rather than a button. The page leaves on its own within a second, and a button
   sitting there in the meantime is a thing to press that then gets pressed by somebody else -
   which is how a payer ends up unsure whether they did that or we did.

   One element, drawn in CSS. No script, because this page must still hand somebody over with
   scripting switched off, and no image, because a spinner that has to be fetched is blank for
   exactly as long as the page exists. */
.leaving {
    text-align: center;
}

.leaving-spinner {
    width: 34px;
    height: 34px;
    margin: 0.5rem auto 1rem;
    border-radius: 50%;
    border: 3px solid var(--line);
    border-top-color: var(--accent);
    animation: leaving-turn 0.7s linear infinite;
}

.leaving-said {
    margin: 0 0 0.35rem;
    font-weight: 600;
}

@keyframes leaving-turn {
    to {
        transform: rotate(360deg);
    }
}

/* Somebody who asked not to be moved is not moved. The wait is the same and the page still
   leaves; it just stops turning, which is what the setting is for. */
@media (prefers-reduced-motion: reduce) {
    .leaving-spinner {
        animation: none;
    }
}

/* The figure somebody is about to pay, at the foot of the cart.

   It was the same size as the quantity column above it. This is the one number on the page
   that decides whether they press the button, and it should be findable without reading. */
.cart-total th {
    font-size: 1.15rem;
    padding-top: 1rem;
}

.cart-total .cart-total-figure {
    font-size: 1.35rem;
    white-space: nowrap;
}

/* --- connecting a device: only the fields the chosen option uses ---------------
   Every field on the pairing form is rendered whichever option is selected, because an
   unrendered one posts nothing and binds to its default - a shop opened with no name. Rendered
   is not the same as shown, though, and the page used to conflate them: somebody connecting a
   device to a shop they already had was asked to name a new one and give it an address.

   So the sets are hidden rather than left out, and a hidden field still posts. The route reads
   only the fields the chosen option names, so what the others carry is never looked at.

   :has on the form, not a class on a parent, because nothing here runs script - the radio is the
   only thing that knows which option is live. Without :has every set is visible, which is the
   behaviour this replaced: confusing, and complete. */
.account-form:not(:has(input[name="Approval.Choice"][value="new-store"]:checked,
                       input[name="Approval.Choice"][value="new-business"]:checked))
    .proposal-store,

/* Which business it goes under. Only when opening a store on a business that already exists -
   opening a business creates the one it belongs to. */
.account-form:not(:has(input[name="Approval.Choice"][value="new-store"]:checked))
    .proposal-owner,

.account-form:not(:has(input[name="Approval.Choice"][value="new-business"]:checked))
    .proposal-business {
    display: none;
}

/* What has to be filled in before this can connect. Only ever beside a field the route refuses
   without, so it is a fact rather than decoration. */
.required-mark {
    color: var(--danger, #b3261e);
    margin-left: 0.15rem;
}

/* --- the welcome -------------------------------------------------------------

   The first thing an account with nothing in it sees, and the one screen here allowed to be more
   than a working page. It is still built from the portal's own pieces - a card, a pill, a button,
   the drawer's glyphs - and only arranges them on a wider stage.

   Glass cards over a soft wash of the accent, because that is what a Mac visitor's own system
   looks like, and a shop owner setting up on one should not feel they have left it. The wash is
   two gradients, blurred: no picture, nothing that needs a second version for a dark theme.

   ⚠ Every movement is inside the no-preference block at the end. Outside it the page is complete
   and still: the tick is drawn, the cards are where they belong, the radar is a dot. */

.welcome {
    margin: 0 auto;
    max-width: 62rem;
    padding: 0.25rem 0 3rem;
}

.welcome-top {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 1rem;
    justify-content: space-between;
    margin-bottom: 1rem;
}

/* Leaving stays on the right, including on the tour, where there is no rail beside it. */
.welcome-top > form {
    margin-left: auto;
}

.welcome-rail {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.welcome-milestone {
    align-items: center;
    color: var(--ink-faint);
    display: flex;
    font-size: 0.85rem;
    font-weight: 600;
    gap: 0.45rem;
}

.welcome-milestone + .welcome-milestone::before {
    background: var(--line);
    border-radius: 2px;
    content: "";
    height: 2px;
    width: 2rem;
}

.welcome-milestone.is-done + .welcome-milestone::before {
    background: color-mix(in srgb, var(--accent) 55%, var(--line));
}

.welcome-milestone-dot {
    align-items: center;
    background: var(--surface);
    border: 1.5px solid var(--line);
    border-radius: 999px;
    display: inline-flex;
    font-size: 0.75rem;
    height: 1.6rem;
    justify-content: center;
    width: 1.6rem;
}

.welcome-milestone.is-current {
    color: var(--ink);
}

.welcome-milestone.is-current .welcome-milestone-dot {
    border-color: var(--accent);
    box-shadow: 0 0 0 4px var(--ring);
    color: var(--accent);
}

.welcome-milestone.is-done .welcome-milestone-dot {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

/* On a phone the numbers carry the rail, and three labels would wrap it onto two lines. */
@media (max-width: 36rem) {
    .welcome-milestone-label {
        display: none;
    }
}

.welcome-stage {
    isolation: isolate;
    padding: 2.25rem 0.5rem 1rem;
    position: relative;
    text-align: center;
}

.welcome-aura {
    background:
        radial-gradient(34% 55% at 34% 38%,
            color-mix(in srgb, var(--accent-bright) 18%, transparent), transparent 70%),
        radial-gradient(30% 50% at 68% 30%,
            color-mix(in srgb, var(--accent) 10%, transparent), transparent 70%);
    filter: blur(28px);
    height: 24rem;
    inset: -1rem 0 auto;
    pointer-events: none;
    position: absolute;
    z-index: -1;
}

.welcome-eyebrow {
    color: var(--accent);
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    margin: 0 0 0.6rem;
    text-transform: uppercase;
}

.welcome-headline {
    font-size: clamp(1.8rem, 1.2rem + 2.4vw, 2.85rem);
    letter-spacing: -0.02em;
    line-height: 1.1;
    margin: 0 auto 0.8rem;
    max-width: 22ch;
    text-wrap: balance;
}

.welcome-lede {
    color: var(--ink-soft);
    font-size: 1.08rem;
    line-height: 1.55;
    margin: 0 auto 2rem;
    max-width: 40rem;
    text-wrap: pretty;
}

.welcome-actions {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    justify-content: center;
    margin-top: 2rem;
}

/* The global top margin on .button-primary drops it below its neighbour in a row, the trap the
   landing page's buttons met first. Zeroed here, scoped, for the same reason. */
.welcome-actions .button {
    margin-top: 0;
}

.welcome-preview,
.welcome-apps {
    display: grid;
    gap: 1rem;
    list-style: none;
    margin: 0 auto;
    padding: 0;
    text-align: left;
}

.welcome-preview {
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    max-width: 54rem;
}

.welcome-apps {
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    max-width: 46rem;
}

.welcome-preview-item,
.welcome-app,
.welcome-card {
    -webkit-backdrop-filter: blur(14px) saturate(1.2);
    backdrop-filter: blur(14px) saturate(1.2);
    background: color-mix(in srgb, var(--surface) 84%, transparent);
    border: 1px solid var(--line);
    border-radius: calc(var(--radius) * 1.4);
    box-shadow: var(--shadow);
}

.welcome-preview-item,
.welcome-app {
    align-items: flex-start;
    display: flex;
    gap: 0.9rem;
    padding: 1rem 1.1rem;
}

.welcome-preview-item strong,
.welcome-app strong,
.welcome-choice-body strong {
    display: block;
    margin-bottom: 0.15rem;
}

.welcome-app .pill,
.welcome-choice .pill {
    display: inline-block;
    margin-top: 0.45rem;
}

.welcome-glyph {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    border-radius: 12px;
    color: var(--accent);
    display: inline-flex;
    flex-shrink: 0;
    height: 2.6rem;
    justify-content: center;
    width: 2.6rem;
}

.welcome-glyph-large {
    border-radius: 22px;
    height: 4.6rem;
    margin-bottom: 1.2rem;
    width: 4.6rem;
}

.welcome-tour-card {
    margin: 0 auto;
    max-width: 38rem;
    min-height: 15rem;
}

.welcome-dots {
    display: flex;
    gap: 0.4rem;
    justify-content: center;
}

.welcome-dot {
    background: var(--line);
    border-radius: 999px;
    height: 0.45rem;
    width: 0.45rem;
}

.welcome-dot.is-current {
    background: var(--accent);
    width: 1.4rem;
}

.welcome-link-grid {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    text-align: left;
}

.welcome-card {
    padding: 1.4rem 1.4rem 1.5rem;
    position: relative;
}

.welcome-card h2 {
    font-size: 1.1rem;
    margin: 0 0 1rem;
}

.welcome-step-number {
    align-items: center;
    background: var(--paper);
    border-radius: 999px;
    color: var(--ink-soft);
    display: inline-flex;
    font-size: 0.8rem;
    font-weight: 700;
    height: 1.6rem;
    justify-content: center;
    margin-bottom: 0.75rem;
    width: 1.6rem;
}

.welcome-download {
    align-items: center;
    display: flex;
    gap: 0.85rem;
    margin-bottom: 1rem;
}

.welcome-download strong,
.welcome-download .muted,
.welcome-waiting strong,
.welcome-waiting .muted {
    display: block;
}

.welcome-download-mark {
    align-items: center;
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: 14px;
    display: inline-flex;
    flex-shrink: 0;
    height: 3rem;
    justify-content: center;
    width: 3rem;
}

.welcome-platforms {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    font-size: 0.88rem;
    gap: 0.4rem 0.9rem;
    margin: 1.1rem 0 0;
}

.welcome-platform {
    align-items: center;
    color: var(--ink-soft);
    display: inline-flex;
    gap: 0.3rem;
    text-decoration: none;
}

.welcome-platform:hover {
    color: var(--accent);
}

.welcome-or {
    align-items: center;
    color: var(--ink-faint);
    display: flex;
    font-size: 0.78rem;
    gap: 0.75rem;
    letter-spacing: 0.06em;
    margin: 1rem 0;
    text-transform: uppercase;
}

.welcome-or::before,
.welcome-or::after {
    background: var(--line);
    content: "";
    flex: 1;
    height: 1px;
}

/* A code is read off another screen a few characters at a time, and spaced capitals are how the
   device shows it. */
.welcome-code input[name="code"] {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

.welcome-waiting {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 4%, transparent);
    border: 1px dashed color-mix(in srgb, var(--accent) 40%, var(--line));
    border-radius: calc(var(--radius) * 1.4);
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 1rem;
    margin-top: 1rem;
    padding: 0.9rem 1.2rem;
    text-align: left;
}

.welcome-check {
    font-size: 0.9rem;
    margin-left: auto;
    white-space: nowrap;
}

.welcome-radar {
    flex-shrink: 0;
    height: 2.2rem;
    position: relative;
    width: 2.2rem;
}

.welcome-radar::before,
.welcome-radar span {
    border-radius: 999px;
    content: "";
    position: absolute;
}

.welcome-radar::before {
    background: var(--accent);
    inset: 0.72rem;
}

.welcome-radar span {
    border: 2px solid var(--accent);
    inset: 0;
    opacity: 0;
}

.welcome-tick {
    display: inline-block;
    margin-bottom: 1rem;
}

.welcome-tick-ring {
    fill: var(--good-soft);
    stroke: var(--good);
    stroke-width: 3;
}

.welcome-tick-mark {
    fill: none;
    stroke: var(--good);
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 4.5;
}

.welcome-cover {
    margin: 0 auto;
    max-width: 34rem;
}

.welcome-cover.notice {
    align-items: flex-start;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    text-align: left;
}

.welcome-choices {
    display: grid;
    gap: 0.6rem;
    margin: 0.35rem 0 0.75rem;
}

.welcome-choice {
    cursor: pointer;
    display: block;
    position: relative;
}

/* The radio is still the control - it posts, and the keyboard reaches it - and the card is what
   shows which one is chosen. */
.welcome-choice input {
    opacity: 0;
    pointer-events: none;
    position: absolute;
}

.welcome-choice-body {
    align-items: flex-start;
    border: 1.5px solid var(--line);
    border-radius: calc(var(--radius) * 1.2);
    display: flex;
    gap: 0.85rem;
    padding: 0.85rem 1rem;
}

.welcome-choice input:checked + .welcome-choice-body {
    background: color-mix(in srgb, var(--accent) 5%, var(--surface));
    border-color: var(--accent);
    box-shadow: 0 0 0 4px var(--ring);
}

.welcome-choice input:focus-visible + .welcome-choice-body {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.welcome-modal .modal-backdrop {
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    background: rgb(20 23 28 / 38%);
}

@media (prefers-reduced-motion: no-preference) {
    .welcome-stage > *,
    .welcome-preview-item,
    .welcome-app {
        animation: welcome-in 0.7s cubic-bezier(0.2, 0.8, 0.2, 1) both;
    }

    .welcome-stage > :nth-child(2) {
        animation-delay: 0.05s;
    }

    .welcome-stage > :nth-child(3) {
        animation-delay: 0.1s;
    }

    .welcome-stage > :nth-child(4) {
        animation-delay: 0.16s;
    }

    .welcome-stage > :nth-child(5) {
        animation-delay: 0.22s;
    }

    .welcome-stage > :nth-child(n + 6) {
        animation-delay: 0.28s;
    }

    .welcome-preview-item:nth-child(1),
    .welcome-app:nth-child(1) {
        animation-delay: 0.24s;
    }

    .welcome-preview-item:nth-child(2),
    .welcome-app:nth-child(2) {
        animation-delay: 0.32s;
    }

    .welcome-preview-item:nth-child(3),
    .welcome-app:nth-child(3) {
        animation-delay: 0.4s;
    }

    /* The wash drifts, slowly enough to read as light rather than as something to watch. */
    .welcome-stage > .welcome-aura {
        animation:
            welcome-fade 1.2s ease-out both,
            welcome-drift 18s ease-in-out 1.2s infinite alternate;
    }

    .welcome-glyph-large {
        animation: welcome-pop 0.7s cubic-bezier(0.3, 1.4, 0.5, 1) 0.1s both;
    }

    .welcome-preview-item,
    .welcome-app,
    .welcome-choice-body {
        transition: border-color 0.15s ease, box-shadow 0.2s ease, transform 0.2s ease,
            background 0.15s ease;
    }

    .welcome-preview-item:hover,
    .welcome-app:hover {
        transform: translateY(-2px);
    }

    .welcome-dot {
        transition: width 0.3s ease, background 0.3s ease;
    }

    .welcome-radar span {
        animation: welcome-ping 1.8s cubic-bezier(0, 0, 0.2, 1) infinite;
    }

    /* Nothing to announce once the script has stopped asking. */
    [data-welcome-waiting-stopped] .welcome-radar span {
        animation: none;
    }

    .welcome-tick {
        animation: welcome-pop 0.6s cubic-bezier(0.3, 1.4, 0.5, 1) both;
    }

    .welcome-tick-ring {
        animation: welcome-draw 0.7s ease-out 0.15s forwards;
        stroke-dasharray: 190;
        stroke-dashoffset: 190;
    }

    .welcome-tick-mark {
        animation: welcome-draw 0.45s ease-out 0.75s forwards;
        stroke-dasharray: 44;
        stroke-dashoffset: 44;
    }

    .welcome-modal .modal-backdrop {
        animation: welcome-fade 0.25s ease-out both;
    }

    .welcome-modal .modal-panel {
        animation: welcome-rise 0.35s cubic-bezier(0.2, 0.8, 0.2, 1) both;
    }
}

@keyframes welcome-in {
    from {
        filter: blur(6px);
        opacity: 0;
        transform: translateY(14px);
    }

    to {
        filter: none;
        opacity: 1;
        transform: none;
    }
}

@keyframes welcome-fade {
    from {
        opacity: 0;
    }
}

@keyframes welcome-rise {
    from {
        opacity: 0;
        transform: translateY(12px) scale(0.98);
    }
}

@keyframes welcome-pop {
    from {
        opacity: 0;
        transform: scale(0.6);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes welcome-ping {
    0% {
        opacity: 0.8;
        transform: scale(0.35);
    }

    100% {
        opacity: 0;
        transform: scale(1.15);
    }
}

@keyframes welcome-draw {
    to {
        stroke-dashoffset: 0;
    }
}

@keyframes welcome-drift {
    from {
        transform: translate3d(-2%, 0, 0) scale(1);
    }

    to {
        transform: translate3d(2%, 1.5%, 0) scale(1.06);
    }
}

/* --- a signing certificate: two ways to start ------------------------------*/

/* The two paths as equal cards, the free one marked with the accent so an owner who already holds
   a certificate does not ask us to make and charge for another. */
.cert-path {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    padding: 1.15rem 1.25rem;
}

.cert-path-main {
    border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 10%, transparent);
}

.cert-path .pill {
    align-self: flex-start;
}

.cert-path h3 {
    font-size: 1.1rem;
    margin: 0;
}

.cert-path p {
    color: var(--ink-soft);
    line-height: 1.55;
    margin: 0;
}

.cert-path-steps {
    color: var(--ink-soft);
    display: grid;
    gap: 0.3rem;
    margin: 0;
    padding-left: 1.25rem;
}

/* Pushed to the bottom, so the two buttons line up however long either description runs. */
.cert-path .button {
    align-self: flex-start;
    margin-top: auto;
}

/* The step a field belongs to, in a form long enough to need the count. */
.form-step {
    background: var(--accent);
    border-radius: 999px;
    color: #fff;
    display: inline-grid;
    font-size: 0.72rem;
    font-weight: 700;
    height: 1.3rem;
    margin-right: 0.4rem;
    place-items: center;
    vertical-align: 0.1em;
    width: 1.3rem;
}

/* The steps above a dialog's form, spaced off the fields they introduce. */
.cert-dialog-steps {
    margin: 0.35rem 0 1rem;
}

/* Rows carrying buttons read level when the text sits beside the button, not above it. */
.cert-table td {
    vertical-align: middle;
}

/* The shop a row's button chose, shown as settled rather than as one tick among many. */
.chosen-shop {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 8%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
    border-radius: var(--radius);
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
    justify-content: space-between;
    margin-bottom: 0.75rem;
    padding: 0.65rem 0.9rem;
}

/* Every shop, in a box that scrolls, so a company with twenty does not push the file and the
   PIN off the bottom of the dialog. */
.shop-picklist {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    margin-bottom: 0.75rem;
    max-height: 14rem;
    overflow-y: auto;
    padding: 0.35rem 0.8rem;
}

/* The dashboard band names three things and offers the rest in a dialog. */
.attention-more {
    margin: 0.35rem 0 0;
}

.attention-more a {
    font-weight: 600;
    margin-left: 0.35rem;
}

.attention-list {
    max-height: 22rem;
    overflow-y: auto;
    margin: 0 0 1rem;
    padding-left: 1.2rem;
}

.attention-list li + li {
    margin-top: 0.4rem;
}

.attention-act {
    font-weight: 600;
    margin-left: 0.35rem;
}

/* --- Where the colour goes --------------------------------------------------- */

/*
   The feedback was that the panel looks plain, and it was: one accent, used on buttons and links,
   and every other surface in greys. These rules put the product's colour on the furniture a
   customer reads all day — the title of a panel, the head of a table, the label beside a value —
   and nowhere near the numbers themselves, which have to stay black to be read.

   Scoped to this portal. The same classes dress the Admin Portal, where staff are working down
   lists all day and a tinted header on every panel is noise.
*/
.shell-customer .card > h2,
.shell-customer .card-header h2 {
    color: var(--accent-dark);
}

/* A hairline of the brand under a panel's title. Enough to group the header with the panel it
   belongs to; not a coloured band, which is what turns a money screen into a brochure. */
.shell-customer .card-header {
    border-bottom: 1px solid color-mix(in srgb, var(--accent) 16%, var(--line));
    margin-bottom: 0.9rem;
    padding-bottom: 0.6rem;
}

.shell-customer .data-table th {
    background: color-mix(in srgb, var(--accent) 5%, var(--surface));
    color: color-mix(in srgb, var(--accent) 55%, var(--ink-faint));
}

.shell-customer .kv-label {
    color: color-mix(in srgb, var(--accent) 40%, var(--ink-faint));
}

/* Four figures, two by two, on a phone. One per row turned the top of the dashboard into four
   screens of scrolling before the shops appeared, which is most of what "it looks weird on my
   phone" was. */
@media (width <= 620px) {
    .tile-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .tile-value {
        font-size: 1.4rem;
    }
}

/* The page's own title, dark and heavy. It wore the brand gradient for a while; the drawings
   keep the colour for the controls and leave the title to be read. Both headings, because four
   pages here still wear the Admin Portal's .page-header. */
.shell-customer .page-head h1,
.shell-customer .page-header h1 {
    color: var(--ink);
    font-size: 1.7rem;
    font-weight: 700;
    letter-spacing: -0.02em;
}

/* --- The assistant band ------------------------------------------------------ */

/* Tinted, because it is the one claim on this page that is new and the eye has to find it
   between two white bands. The same radial the hero uses, at a quarter of the weight. */
.assistant {
    background:
        radial-gradient(70% 90% at 85% 0%,
            color-mix(in srgb, var(--accent) 10%, transparent) 0%, transparent 60%),
        color-mix(in srgb, var(--accent) 4%, var(--surface));
    padding-block: var(--landing-gap);
}

.assistant-grid {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}

.assistant-card {
    background: var(--surface);
    border: 1px solid color-mix(in srgb, var(--accent) 18%, var(--line));
    border-radius: var(--radius);
    padding: 1.5rem;
}

.assistant-card h3 {
    margin: 0 0 0.5rem;
}

.assistant-card p {
    color: var(--ink-soft);
    margin: 0;
}

.assistant-card .pill {
    margin-top: 0.8rem;
}

/* What each platform actually needs, in the same band as the claim it qualifies. Somebody
   deciding whether to buy a till for this reads both or neither. */
.assistant-devices {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    margin-top: 1.5rem;
    padding: 1.5rem;
}

.assistant-devices h3 {
    margin: 0;
}

.assistant-devices-lede {
    color: var(--ink-soft);
    margin: 0.3rem 0 1rem;
}

.assistant-devices-list {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    margin: 0;
}

.assistant-device dt {
    border-left: 3px solid var(--accent);
    font-weight: 600;
    padding-left: 0.6rem;
}

.assistant-device dd {
    color: var(--ink-soft);
    margin: 0.35rem 0 0 0.6rem;
}

.assistant-note {
    border-top: 1px solid var(--line);
    color: var(--ink-faint);
    font-size: 0.88rem;
    margin: 1.2rem 0 0;
    padding-top: 0.9rem;
}

/* --- The remake ---------------------------------------------------------------

   A set of screens was drawn for this portal, and the rules from here down are what they asked
   for: colour on the furniture rather than on the numbers, a glyph beside every figure so a row
   of four tiles can be told apart at a glance, a warning band that says what to press, and
   panels with rounder corners sitting on a faintly tinted page rather than a grey one. Every
   colour is still a token, so the sign-in pages and the plan cards moved with it. */

/* Rounder than a button. The corners a panel wears and the corners a control wears used to be
   one token, and the drawings want a card at twelve where a field stays at eight. */
.shell-customer .card,
.shell-customer .tile,
.shell-customer .store-card,
.shell-customer .meter,
.shell-customer .empty {
    border-radius: var(--radius-panel);
}

/* The day, and a line under it, on the far side of a greeting. A dashboard read every morning
   wants to say which morning. */
.page-head-aside {
    align-items: flex-end;
    color: var(--ink-soft);
    display: flex;
    flex-direction: column;
    font-size: 0.9rem;
    gap: 0.1rem;
    text-align: right;
}

.page-head-motto {
    color: var(--ink-faint);
    font-size: 0.82rem;
}

/* A second action beside the primary one wears the brand's outline rather than grey. Grey
   read as disabled next to a filled purple button. */
.shell-customer .page-head .button:not(.button-primary),
.shell-customer .card-header .button:not(.button-primary) {
    border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
    color: var(--accent-dark);
}

.shell-customer .page-head .button:not(.button-primary):hover,
.shell-customer .card-header .button:not(.button-primary):hover {
    background: color-mix(in srgb, var(--accent) 6%, var(--surface));
    border-color: var(--accent);
}

/* Level with the title. .button-primary carries a top margin for the case it was written for,
   a button under a column form, and up here it dropped the one control below the heading. The
   same zeroing .action-row and the top bar already do. */
.page-head .button-primary {
    margin-top: 0;
}

/* A glyph inside a button, on the words' own line. The button stays inline-block, so this is
   alignment rather than a flex row - see the guard on flex rules for why. */
.button .glyph {
    margin-right: 0.1rem;
    vertical-align: -0.2em;
}

.button .glyph:last-child {
    margin-left: 0.15rem;
    margin-right: 0;
}

/* --- Tiles with a glyph ------------------------------------------------------ */

/* The glyph sits in a tinted square to the left and the lines stack beside it. Only a tile that
   carries one is laid out as a grid, so the hundred tiles that do not are untouched. */
.tile:has(.tile-icon) {
    align-items: start;
    column-gap: 0.9rem;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-rows: auto auto auto;
    row-gap: 0.15rem;
}

/* ⚠ Every line is put in the second column by name rather than left to auto-placement, and this
   is the whole of the fix. The glyph spans the three rows the tile was drawn with, so a tile that
   grew a fourth line had nowhere left beside the glyph and the browser dropped it into the first
   column instead — underneath the glyph, in a column as wide as a 46px square, where two words
   wrapped and widened it and shoved the date sideways. The covered-until tile grew that fourth
   line the day it started offering the licence sheet. */
.tile:has(.tile-icon) > :not(.tile-icon) {
    grid-column: 2;
}

.tile-icon {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 10%, var(--surface));
    border-radius: 12px;
    color: var(--accent);
    display: inline-flex;
    flex-shrink: 0;
    grid-row: 1 / span 3;
    height: 46px;
    justify-content: center;
    margin-top: 0.1rem;
    width: 46px;
}

/* The fourth tint means nothing: a device count beside a green "can invoice" and an orange
   "renews soon" needs a colour that is not a verdict. */
.tile-icon-info {
    background: var(--info-soft);
    color: var(--info);
}

.tile-icon-good,
.tile-good .tile-icon {
    background: var(--good-soft);
    color: var(--good);
}

.tile-icon-warn,
.tile-warn .tile-icon {
    background: var(--warn-soft);
    color: var(--warn);
}

.tile-icon-bad,
.tile-bad .tile-icon {
    background: var(--bad-soft);
    color: var(--bad);
}

.shell-customer .tile-value {
    font-size: 1.7rem;
    font-weight: 700;
}

/* Two tiles to a row on a phone leaves a column about a hundred pixels wide beside the glyph,
   and a date at 1.4rem is wider than that — it ran under the neighbouring tile. The glyph goes
   above the words down here, and a value that still does not fit breaks rather than escapes. */
@media (width <= 620px) {
    .tile:has(.tile-icon) {
        grid-template-columns: minmax(0, 1fr);
        row-gap: 0.1rem;
    }

    /* Back to the only column there is. Left at two, every line would ask for a column this
       grid no longer has and the browser would invent one beside it. */
    .tile:has(.tile-icon) > :not(.tile-icon) {
        grid-column: 1;
    }

    .tile-icon {
        grid-row: auto;
        height: 38px;
        margin-bottom: 0.35rem;
        width: 38px;
    }

    .shell-customer .tile-value {
        font-size: 1.4rem;
        min-width: 0;
        overflow-wrap: anywhere;
    }
}

/* --- What needs attention ---------------------------------------------------- */

/* A band rather than a notice: a glyph, the lines, and the way to the rest on the far side.
   Tinted from the bright warning colour, because the dark one is for text and a whole band of
   it is a brown stripe across the page. */
.attention {
    align-items: center;
    background: color-mix(in srgb, var(--warn-bright) 9%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--warn-bright) 35%, var(--line));
    border-radius: var(--radius-panel);
    display: grid;
    gap: 0.5rem 1rem;
    grid-template-columns: auto 1fr auto;
    margin-bottom: 1.1rem;
    padding: 0.9rem 1.1rem;
}

.attention-icon {
    align-items: center;
    background: color-mix(in srgb, var(--warn-bright) 18%, var(--surface));
    border-radius: 12px;
    color: var(--warn-bright);
    display: inline-flex;
    height: 44px;
    justify-content: center;
    width: 44px;
}

.attention-title {
    color: var(--warn);
    display: block;
    font-size: 1.02rem;
    margin-bottom: 0.15rem;
}

.attention-lines {
    color: var(--ink-soft);
    font-size: 0.92rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.attention-lines li + li {
    margin-top: 0.15rem;
}

.attention-side {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 0.9rem;
    justify-content: flex-end;
}

.attention-more {
    color: var(--ink-soft);
    font-size: 0.9rem;
}

/* The one filled button that is not purple. It is the band's own colour, so it reads as part
   of the warning rather than as a purchase. */
.attention-act-all {
    background: var(--warn-bright);
    border-color: var(--warn-bright);
    color: #fff;
    white-space: nowrap;
}

.attention-act-all:hover {
    background: var(--warn);
    border-color: var(--warn);
}

@media (max-width: 640px) {
    .attention {
        grid-template-columns: auto 1fr;
    }

    .attention-side {
        grid-column: 1 / -1;
        justify-content: flex-start;
    }
}

/* --- A panel's header -------------------------------------------------------- */

.shell-customer .card-header {
    align-items: center;
}

.shell-customer .card-header h2 {
    align-items: center;
    display: flex;
    font-size: 1.05rem;
    gap: 0.6rem;
}

.card-header-icon {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 10%, var(--surface));
    border-radius: 9px;
    color: var(--accent);
    display: inline-flex;
    flex-shrink: 0;
    height: 32px;
    justify-content: center;
    width: 32px;
}

/* The sentence under a panel's title, inside the heading so the two stay together beside the
   glyph. */
.card-header-sub {
    color: var(--ink-faint);
    display: block;
    font-size: 0.8rem;
    font-weight: 400;
    margin-top: 0.1rem;
}

/* "See all", with its arrow, on the far side of the title. */
.shell-customer .card-header > a {
    align-items: center;
    display: inline-flex;
    font-size: 0.9rem;
    font-weight: 600;
    gap: 0.3rem;
    text-decoration: none;
}

.shell-customer .card-header > a:hover {
    text-decoration: underline;
}

/* One figure that sums the panel, beside its title. */
.card-header-total {
    line-height: 1.2;
    text-align: right;
}

.card-header-total-label {
    color: var(--ink-faint);
    display: block;
    font-size: 0.72rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.card-header-total strong {
    font-size: 1.25rem;
}

/* A search box and a filter inside a panel's header, on one wrapping row. */
.card-tools {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.search-field {
    align-items: center;
    color: var(--ink-faint);
    display: inline-flex;
    position: relative;
}

.search-field .glyph {
    left: 0.6rem;
    pointer-events: none;
    position: absolute;
}

/* !important, because the shared control rule is a chain of eight :not() clauses and carries
   more weight than any selector a page would sanely write. Without it the placeholder sat under
   the glyph. The same reason [hidden] in nexus.css needs it. */
.search-field input {
    min-width: 11rem;
    padding-left: 2rem !important;
}

/* --- Two panels side by side under the tiles --------------------------------- */

.dash-grid {
    display: grid;
    gap: 1rem;
    grid-template-columns: minmax(0, 5fr) minmax(0, 3fr);
    margin-bottom: 1.25rem;
}

.dash-grid-one {
    grid-template-columns: 1fr;
}

@media (max-width: 900px) {
    .dash-grid {
        grid-template-columns: 1fr;
    }
}

.dash-side-act {
    margin-top: 0.9rem;
}

/* A short line under a panel's list, offering the rest. */
.card-more {
    color: var(--ink-soft);
    font-size: 0.9rem;
    margin: 0.9rem 0 0;
}

/* --- A state, as a pill with a dot ------------------------------------------- */

/* The dot is what the eye finds in a column of them; the word is what it means. Only where the
   markup asks, because a pill that says "Recommended" on a plan card is a label, not a state. */
.pill-dot::before {
    background: currentColor;
    border-radius: 50%;
    content: "";
    display: inline-block;
    height: 6px;
    margin-right: 0.35rem;
    vertical-align: 1px;
    width: 6px;
}

/* --- A shop, as a card, revisited -------------------------------------------- */

.store-card-title {
    align-items: center;
    display: flex;
    gap: 0.6rem;
    min-width: 0;
}

/* The badge and the state share the top line and fold under each other rather than breaking a
   two-word state across two lines, which is how "not connected" read as two facts. */
.shell-customer .store-card-head {
    align-items: center;
    flex-wrap: wrap;
}

.shell-customer .store-card-head .pill {
    white-space: nowrap;
}

.store-card-mark {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 10%, var(--surface));
    border-radius: 8px;
    color: var(--accent);
    display: inline-flex;
    flex-shrink: 0;
    height: 30px;
    justify-content: center;
    width: 30px;
}

/* Which application runs there, before the name. Two products, two tints: the till on a
   Windows machine and the till on a phone are the two things this list has to tell apart. */
.store-badge {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 10%, var(--surface));
    border-radius: 999px;
    color: var(--accent-dark);
    display: inline-flex;
    font-size: 0.66rem;
    font-weight: 700;
    gap: 0.3rem;
    letter-spacing: 0.06em;
    padding: 0.15rem 0.6rem;
    text-transform: uppercase;
    white-space: nowrap;
}

.store-badge-mini {
    background: color-mix(in srgb, var(--product-mini) 10%, var(--surface));
    color: var(--product-mini);
}

.shell-customer .store-card-where {
    align-items: center;
    display: inline-flex;
    gap: 0.3rem;
}

/* The facts in a row with a hairline between each, rather than floated apart. */
.shell-customer .store-card-facts {
    display: grid;
    gap: 0;
    grid-template-columns: repeat(auto-fit, minmax(5.5rem, 1fr));
    padding-top: 0.6rem;
}

.shell-customer .store-card-fact + .store-card-fact {
    border-left: 1px solid var(--line);
    padding-left: 0.75rem;
}

/* --- The drawer, revisited --------------------------------------------------- */

/* Whose company, as a small card with a glyph rather than two rules across the column. */
.shell-customer .nav-scope {
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: 12px;
    column-gap: 0.6rem;
    display: grid;
    grid-template-columns: auto 1fr;
    margin: 0 0 0.6rem;
    padding: 0.6rem 0.7rem;
}

.nav-scope-icon {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 12%, var(--surface));
    border-radius: 9px;
    color: var(--accent);
    display: inline-flex;
    grid-row: 1 / span 3;
    height: 34px;
    justify-content: center;
    margin-top: 0.15rem;
    width: 34px;
}

.shell-customer .nav-link.active {
    font-weight: 600;
}

.shell-customer .nav-link.active .nav-icon {
    opacity: 1;
}

/* Somewhere to turn, at the foot of the drawer. margin-top: auto pushes it under the last
   link however few links a role is shown. */
.nav-help {
    background: color-mix(in srgb, var(--accent) 6%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--accent) 16%, var(--line));
    border-radius: 12px;
    color: var(--ink-soft);
    display: flex;
    flex-direction: column;
    font-size: 0.85rem;
    gap: 0.3rem;
    margin-top: auto;
    padding: 0.9rem;
}

.nav-help-icon {
    color: var(--accent);
}

.nav-help strong {
    color: var(--ink);
}

.nav-help .button {
    align-self: flex-start;
    margin-top: 0.4rem;
}

/* --- The top bar, revisited -------------------------------------------------- */

/* Help and the bell sit with the account, on the right. The first of them claims the free
   space, and the download button stops asking for it - the same hand-over the download and
   the account menu already make between themselves. */
.topbar-help {
    align-items: center;
    border-radius: 8px;
    color: var(--ink-soft);
    display: inline-flex;
    font-weight: 500;
    gap: 0.35rem;
    margin-left: auto;
    padding: 0.35rem 0.55rem;
    text-decoration: none;
}

.topbar-help:hover {
    background: var(--paper);
    color: var(--ink);
}

.topbar-help ~ .topbar-download {
    margin-left: 0;
}

.topbar-bell {
    position: relative;
}

.topbar-bell .nav-badge {
    font-size: 0.65rem;
    margin: 0;
    min-width: 1.05rem;
    padding: 0.15rem 0.3rem;
    position: absolute;
    right: 2px;
    top: 2px;
}

/* On a phone the whole help link goes, not only its word: the drawer's own help card is a tap
   away, and the bar was 32px wider than the screen once before. */
@media (width <= 620px) {
    .topbar-help {
        display: none;
    }

    /* The mark alone in the header, and the name in the drawer beside it.

       The lockup only appears up here because the drawer has slid off-canvas and taken it
       with it, so the header would otherwise be unbranded. The mark is what does that job:
       it is the thing people recognise at a glance, and the words beside it were 110px of a
       375px bar spent saying what the screen they are already on is called. Open the drawer
       and the full lockup is the first thing in it.

       ⚠ Taken out of sight rather than out of the page. display:none takes it out of the
       accessibility tree too, and the link would then be announced as "N" — the one reading
       of it that is no use to anybody. This is the .visually-hidden recipe, applied here
       because the class cannot be put on and taken off at a breakpoint. */
    .topbar .brand-text {
        clip-path: inset(50%);
        height: 1px;
        overflow: hidden;
        position: absolute;
        white-space: nowrap;
        width: 1px;
    }
}

/* A phone's bar in two groups: the menu and the mark on the left, everything to act on at the
   right, each control the same square. The download had been a solid slab a size off its
   neighbours with its arrow riding high, the mark still glowed for a dark sidebar it is not on,
   and with help hidden nothing claimed the free space, so the controls just sat in a row.
   Measured at 375px: menu, mark, cart, bell, download and account come to 294px. */
@media (width <= 620px) {
    .topbar .brand {
        margin-right: auto;
    }

    .topbar-download,
    .topbar-help ~ .topbar-download,
    .topbar-download ~ .user-menu {
        margin-left: 0;
    }

    .topbar .brand-mark {
        border-radius: 9px;
        box-shadow: none;
        height: 32px;
        width: 32px;
    }

    /* Flex, because a button on a phone is a block, which is what sat the arrow at the top. */
    .topbar .topbar-download {
        align-items: center;
        background: color-mix(in srgb, var(--accent) 12%, transparent);
        border: 0;
        box-shadow: none;
        display: inline-flex;
        justify-content: center;
        border-radius: 8px;
        color: var(--accent);
        height: 44px;
        min-height: 0;
        padding: 0;
        width: 44px;
    }

    .topbar .topbar-download:hover {
        background: color-mix(in srgb, var(--accent) 20%, transparent);
    }

    .topbar-download-mark {
        height: 20px;
        width: 20px;
    }

    .topbar .user-button {
        padding: 0.2rem;
    }

    .topbar .user-button .chevron {
        display: none;
    }
}

/* A name with a glyph before it, in a table cell. */
.cell-lead {
    align-items: center;
    display: inline-flex;
    gap: 0.6rem;
}

/* One sentence under a band's title. */
.attention-body {
    color: var(--ink-soft);
    font-size: 0.92rem;
    margin: 0;
}

/* The filter and the search above a list, on one row that folds. */
.list-tools {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 1rem;
    justify-content: space-between;
    margin-bottom: 1rem;
}

/* Which application, as a row of pills. The same pair of classes the price list wears for its
   two products, so a reader meets one control twice rather than two controls once. */
.product-switch {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.product-switch .switch-link {
    align-items: center;
    display: inline-flex;
    font-size: 0.9rem;
    gap: 0.35rem;
    padding: 0.35rem 0.9rem;
}

.tile-icon-mini {
    background: color-mix(in srgb, var(--product-mini) 10%, var(--surface));
    color: var(--product-mini);
}

/* --- The live report's head --------------------------------------------------- */

/* The shop's name and whether it is on, on one line. */
.page-head-title {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

.page-head-title h1 {
    margin: 0;
}

.page-head-title .pill {
    white-space: nowrap;
}

.page-head-tools {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

/* The two downloads behind one button. A <details>, like the account menu, so it opens
   without script; the panel and its items are the account menu's own. */
.export-menu {
    position: relative;
}

.export-menu > summary {
    cursor: pointer;
    list-style: none;
}

.export-menu > summary::-webkit-details-marker {
    display: none;
}

.export-menu[open] > summary::before {
    content: "";
    inset: 0;
    position: fixed;
    z-index: -1;
}

.export-menu .user-panel {
    min-width: 18rem;
}

.export-note {
    margin: 0.3rem 0.6rem 0.4rem;
}

/* --- Who sold the most ------------------------------------------------------- */

.leader-list {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Rank, disc, name, figure on one line; the bar and the share under the name and the figure. */
.leader {
    align-items: center;
    display: grid;
    gap: 0.25rem 0.6rem;
    grid-template-columns: auto auto 1fr auto;
}

.leader-rank {
    color: var(--ink-faint);
    font-weight: 700;
    min-width: 1rem;
    text-align: center;
}

.leader-avatar {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 12%, var(--surface));
    border-radius: 50%;
    color: var(--accent-dark);
    display: inline-flex;
    font-size: 0.78rem;
    font-weight: 700;
    height: 32px;
    justify-content: center;
    width: 32px;
}

.leader-who {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.leader-name {
    font-weight: 600;
    overflow-wrap: anywhere;
}

.leader-note {
    color: var(--ink-faint);
    font-size: 0.8rem;
}

.leader-amount {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    white-space: nowrap;
}

.leader-bar {
    align-items: center;
    display: grid;
    gap: 0.5rem;
    grid-column: 3 / -1;
    grid-template-columns: 1fr auto;
}

.leader-share {
    color: var(--ink-faint);
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
    min-width: 2.2rem;
    text-align: right;
}

/* --- Each drawer ---------------------------------------------------------------- */

.till-list {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.till {
    align-items: center;
    background: var(--paper);
    border-radius: 10px;
    display: grid;
    gap: 0.35rem 0.6rem;
    grid-template-columns: 1fr auto;
    padding: 0.7rem 0.85rem;
}

.till-name {
    font-weight: 600;
    overflow-wrap: anywhere;
}

.till-facts {
    color: var(--ink-soft);
    display: flex;
    flex-wrap: wrap;
    font-size: 0.88rem;
    gap: 0.3rem 1.2rem;
    grid-column: 1 / -1;
}

.till-fact-label {
    color: var(--ink-faint);
    display: block;
    font-size: 0.7rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

/* --- How many devices ------------------------------------------------------------ */

/* The stepper: a tinted panel with the count in the middle of two big targets, because the
   plain number box it replaced was the control nobody on the page could find. */
.slot-picker {
    background: color-mix(in srgb, var(--accent) 5%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--accent) 18%, var(--line));
    border-radius: var(--radius-panel);
    display: flex;
    flex-direction: column;
    gap: 0.8rem;
    margin: 0 0 1.25rem;
    padding: 1rem 1.1rem;
}

.slot-picker-head {
    align-items: flex-start;
    display: flex;
    gap: 0.8rem;
}

.slot-picker-head label {
    display: block;
    font-size: 1.02rem;
    font-weight: 600;
}

.slot-picker-icon {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 12%, var(--surface));
    border-radius: 10px;
    color: var(--accent);
    display: inline-flex;
    flex-shrink: 0;
    height: 40px;
    justify-content: center;
    width: 40px;
}

.slot-picker-lede {
    color: var(--ink-soft);
    font-size: 0.9rem;
}

.slot-picker-control {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.slot-step {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 10px;
    color: var(--accent-dark);
    display: inline-flex;
    font-size: 1.5rem;
    font-weight: 600;
    height: 48px;
    justify-content: center;
    line-height: 1;
    text-decoration: none;
    width: 48px;
}

.slot-step:hover {
    background: color-mix(in srgb, var(--accent) 6%, var(--surface));
    border-color: var(--accent);
}

/* Kept in place at the ends rather than removed, so the other button does not slide under the
   pointer — the pager's reasoning. */
.slot-step-off {
    color: var(--ink-faint);
    opacity: 0.45;
    pointer-events: none;
}

/* !important for the reason the search box gives: the shared control rule outweighs anything
   sane, and it sets the font, so the count would otherwise be body-sized in a 48px box. */
.slot-picker input[type="number"] {
    font-size: 1.4rem !important;
    font-weight: 700 !important;
    height: 48px;
    text-align: center;
    width: 5.5rem;
}

.slot-picker .button {
    min-height: 48px;
}

.slot-picker .field-hint {
    margin: 0;
}

/* --- The university ------------------------------------------------------------- */

/* The course somebody is in the middle of, on its own band above the grid. Tinted from the
   accent, with a large glyph standing in for the artwork the drawings carried. */
.learn-hero {
    align-items: center;
    background:
        radial-gradient(60% 100% at 100% 0%,
            color-mix(in srgb, var(--accent) 12%, transparent) 0%, transparent 60%),
        color-mix(in srgb, var(--accent) 5%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--accent) 18%, var(--line));
    border-radius: var(--radius-panel);
    display: grid;
    gap: 1.5rem;
    grid-template-columns: 1fr auto;
    margin-bottom: 1.5rem;
    padding: 1.5rem 1.75rem;
}

.learn-eyebrow {
    color: var(--accent-dark);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.learn-hero-words h2 {
    font-size: 1.4rem;
    margin: 0.3rem 0 0.4rem;
}

.learn-hero-words p {
    color: var(--ink-soft);
    margin: 0 0 0.6rem;
    max-width: 52ch;
}

.learn-hero-facts {
    color: var(--ink-soft);
    display: flex;
    flex-wrap: wrap;
    font-size: 0.9rem;
    gap: 0.3rem 1.2rem;
}

.learn-percent,
.course-percent {
    color: var(--accent-dark);
    font-weight: 700;
}

.learn-hero .progress-track {
    margin: 0.6rem 0 0.9rem;
}

.learn-hero-art {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 12%, var(--surface));
    border-radius: 50%;
    color: var(--accent);
    display: flex;
    height: 140px;
    justify-content: center;
    width: 140px;
}

@media (max-width: 640px) {
    .learn-hero {
        grid-template-columns: 1fr;
        padding: 1.25rem;
    }

    .learn-hero-art {
        display: none;
    }
}

.course-mark {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 10%, var(--surface));
    border-radius: 10px;
    color: var(--accent);
    display: inline-flex;
    height: 40px;
    justify-content: center;
    width: 40px;
}

.course-title {
    font-size: 1.05rem;
    margin: 0.7rem 0 0.3rem;
}

.course-title a {
    color: var(--ink);
    text-decoration: none;
}

.course-title a:hover {
    text-decoration: underline;
}

.course-card p {
    color: var(--ink-soft);
    margin: 0 0 0.6rem;
}

/* The fraction and the percentage on one line above the bar, pushed to the foot of the card
   so every bar in a row sits level whatever the summaries above them say. */
.course-foot {
    align-items: baseline;
    display: flex;
    font-size: 0.85rem;
    justify-content: space-between;
    margin-top: auto;
}

.course-card .progress-track {
    margin: 0.4rem 0 0.2rem;
}

/* A person in a table: disc, name, address. The disc is the leaderboard's. */
.person-cell {
    align-items: center;
    display: inline-flex;
    gap: 0.6rem;
}

.person-words {
    display: flex;
    flex-direction: column;
    line-height: 1.3;
}

.progress-cell {
    align-items: center;
    display: flex;
    gap: 0.5rem;
    min-width: 9rem;
}

.progress-cell .progress-track {
    flex: 1;
    margin: 0;
}

/* Underway: neither done nor a problem, so neither green nor amber. */
.pill-accent {
    background: color-mix(in srgb, var(--accent) 12%, var(--surface));
    color: var(--accent-dark);
}

/* --- What asking the shop costs ------------------------------------------------- */

.report-ask-block {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

/* Amber, because it is a warning about spending something, and above the button so it is
   read before the press rather than discovered after the refusal. */
.report-ask-cost {
    align-items: flex-start;
    background: var(--warn-soft);
    border-radius: 10px;
    color: var(--warn);
    display: flex;
    font-size: 0.9rem;
    gap: 0.5rem;
    margin: 0;
    padding: 0.65rem 0.85rem;
}

.report-ask-cost .glyph {
    flex-shrink: 0;
    margin-top: 0.15rem;
}

/* --- Every open shop, as a list ---------------------------------------------- */

/* Rows rather than cards, and a ceiling on the panel's height so a chain of sixty scrolls
   inside it instead of pushing the credits off the page. */
.store-list {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    list-style: none;
    margin: 0;
    max-height: 26rem;
    overflow-y: auto;
    padding: 0;
}

.store-row {
    align-items: center;
    background: var(--paper);
    border-radius: 10px;
    display: grid;
    gap: 0.4rem 0.9rem;
    grid-template-columns: minmax(0, 1fr) auto auto auto auto;
    padding: 0.55rem 0.8rem;
}

/* An empty cell where a shop has no reports to open, so the column stays lined up. */
.store-row-act {
    justify-self: end;
    min-width: 6.5rem;
}

.store-row-name {
    align-items: center;
    color: var(--ink);
    display: inline-flex;
    font-weight: 600;
    gap: 0.6rem;
    min-width: 0;
    overflow-wrap: anywhere;
    text-decoration: none;
}

.store-row-name:hover {
    color: var(--accent-dark);
}

.store-row-cover {
    font-size: 0.9rem;
    line-height: 1.2;
    text-align: right;
    white-space: nowrap;
}

.store-row-cover .store-card-fact-label {
    display: block;
}

@media (max-width: 640px) {
    .store-row {
        grid-template-columns: minmax(0, 1fr) auto;
    }

    .store-row-cover {
        grid-column: 1 / -1;
        text-align: left;
    }

    .store-row-act {
        grid-column: 1 / -1;
        justify-self: start;
        min-width: 0;
    }
}

/* --- Buying more report requests, from the refusal itself ---------------------- */

.report-buy-extra {
    align-items: center;
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-left: 0.5rem;
}

/* --- How many device changes are left ----------------------------------------- */

.device-changes {
    align-items: flex-start;
    color: var(--ink-soft);
    display: flex;
    font-size: 0.9rem;
    gap: 0.5rem;
    margin: 0.5rem 0 0.9rem;
}

.device-changes .glyph {
    flex-shrink: 0;
    margin-top: 0.15rem;
}

/* None left: amber, because the switches under it will refuse, and the button beside the
   sentence is the way round. */
.device-changes-off {
    background: var(--warn-soft);
    border-radius: 10px;
    color: var(--warn);
    padding: 0.65rem 0.85rem;
}

.device-changes .button {
    margin-left: 0.4rem;
    vertical-align: middle;
}

/* --- Active, switched off and free, under the device list ------------------------ */

.device-tally {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0.9rem 0 0.4rem;
}

.device-tally-chip {
    align-items: baseline;
    border-radius: 999px;
    display: inline-flex;
    font-size: 0.9rem;
    gap: 0.35rem;
    padding: 0.3rem 0.8rem;
}

.device-tally-chip strong {
    font-size: 1.05rem;
}

.device-tally-on {
    background: var(--good-soft);
    color: var(--good);
}

.device-tally-off {
    background: var(--warn-soft);
    color: var(--warn);
}

.device-tally-free {
    background: color-mix(in srgb, var(--accent) 10%, var(--surface));
    color: var(--accent-dark);
}

/* --- Choosing how many of a package to buy ------------------------------------ */

/* The stepper the credits dialog uses, borrowing the shop page's steps. Two sizes of jump each
   way rather than one, because a package sold in fives up to a thousand is forty presses wide
   at one step apiece.

   Enclosed in a track, unlike the shop page's, which stands alone on a white card. Here it sits
   between a paragraph and a total, and five bordered squares in a row read as five separate
   things to decide rather than as one figure with a way to change it. */
.qty-picker {
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: 14px;
    gap: 0.3rem;
    justify-content: space-between;
    margin: 0.35rem 0 0.7rem;
    padding: 0.35rem;
}

/* Inside the track a step takes the width its label needs rather than a square's. Two of them
   read "−1.000", which a 48px box cuts in half.

   ⚠ The floor is a share of the track rather than a fixed figure, and that is what keeps the five
   controls on one line. Four steps at 3.1rem and a count at 5.5rem come to more than a dialog has
   on a telephone, so the last of them wrapped onto a row of its own with the gap the other four
   were spread across left beside it. Together these ask for 82% of the track at the narrow end,
   which leaves the gaps their room at every width without a breakpoint. */
.qty-picker .slot-step {
    flex: 0 1 auto;
    font-size: 1.05rem;
    min-width: clamp(2.6rem, 14%, 3.4rem);
    padding: 0 0.4rem;
    width: auto;
}

/* The figure itself, where the shop page puts a number box. It looks like one and is not: the
   quantity lives in the address so the total under it can be worked out on the server, and a
   field nobody can type into is worse than a figure that never invited them to. The surface and
   the height are what stop it disappearing into the track around it. The label above it names
   it, so nothing does underneath. */
.qty-count {
    align-items: center;
    background: var(--surface);
    border-radius: 10px;
    display: inline-flex;
    flex: 0 1 auto;
    justify-content: center;
    min-height: 48px;
    min-width: clamp(4rem, 26%, 6rem);
    text-align: center;
}

.qty-count strong {
    font-size: 1.6rem;
    line-height: 1.1;
}


/* The round figures, for the far end of a long range. */
.qty-jumps {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    justify-content: center;
    margin-bottom: 0.6rem;
}

.qty-jumps .switch-link {
    font-size: 0.85rem;
    padding: 0.3rem 0.8rem;
}

/* What the chosen figure costs, which is the number somebody is actually deciding on.

   A row rather than a centred band: the unit price at one end and the sum at the other, tinted
   towards the accent so the last thing above the button is the thing being agreed to. Both
   figures centred in grey gave them the same weight, and the one that matters is the total. */
.qty-total {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 7%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--accent) 22%, var(--line));
    border-radius: 12px;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: space-between;
    margin: 0.2rem 0 0.9rem;
    padding: 0.75rem 1rem;
}

.qty-total-label {
    color: var(--ink-soft);
    font-size: 0.85rem;
}

.qty-total-sum {
    font-size: 1.45rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.1;
}

/* "each", beside a price that is per unit rather than per package. */
.offer-each {
    color: var(--ink-soft);
    font-size: 0.8rem;
    font-weight: 500;
}

/* --- The pricing page: who it is for, and the finder --------------------------- */

/* The same gutter and the same rhythm as .compare, so the sections read as part of one page
   rather than as blocks bolted under it. */
.who,
.office,
.ia,
.promise,
.mini-block {
    margin-bottom: var(--landing-gap);
    padding-inline: max(1.5rem, calc((100% - 78rem) / 2));
}

.who-grid {
    display: grid;
    gap: 1.25rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
    margin-bottom: 1.5rem;
}

.who-card {
    display: flex;
    flex-direction: column;
    padding: 1.6rem 1.5rem;
}

.who-head {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-bottom: 0.6rem;
}

/* A long edition name and its badge share the line while they fit, and the badge drops below
   rather than running off the card on a phone. */
.who-head h3 {
    flex: 1 1 8rem;
    font-size: 1.3rem;
    margin: 0;
    min-width: 0;
}

.who-mark {
    align-items: center;
    background: color-mix(in srgb, var(--accent) 12%, var(--surface));
    border-radius: 12px;
    color: var(--accent-dark);
    display: inline-flex;
    flex-shrink: 0;
    height: 42px;
    justify-content: center;
    width: 42px;
}

.who-body {
    color: var(--ink);
    margin: 0 0 0.9rem;
}

.who-gets {
    display: grid;
    gap: 0.45rem;
    list-style: none;
    margin: 0 0 0.4rem;
    padding: 0;
}

.who-gets li {
    align-items: flex-start;
    display: flex;
    gap: 0.5rem;
}

.who-gets .glyph {
    color: var(--good);
    flex-shrink: 0;
    margin-top: 0.2rem;
}

.who-for {
    color: var(--ink-faint);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    margin: auto 0 0.3rem;
    padding-top: 1rem;
    text-transform: uppercase;
}

.who-shops {
    color: var(--ink-soft);
    font-size: 0.92rem;
    margin: 0;
}

/* Pro wears its price card's purple here too. The edge alone read as a second plain card, and
   the page had nothing to say which of the two it was recommending. */
/* An edition that is not on sale yet. Dashed rather than faded: faded text is harder to read, and
   the shop still has to read who it is for to know whether to wait for it. */
.who-card-soon {
    border-style: dashed;
}

.who-card-soon .who-mark {
    background: color-mix(in srgb, var(--ink) 6%, var(--surface));
    color: var(--ink-soft);
}

.who-card-featured {
    background: linear-gradient(160deg, var(--accent) 0%, var(--accent-dark) 100%);
    border-color: var(--accent-dark);
    box-shadow: 0 22px 45px -28px color-mix(in srgb, var(--accent) 85%, transparent);
    color: #ffffff;
}

.who-card-featured .who-head h3,
.who-card-featured .who-body {
    color: #ffffff;
}

.who-card-featured .who-mark {
    background: color-mix(in srgb, #ffffff 18%, transparent);
    color: #ffffff;
}

.who-card-featured .who-gets li {
    color: color-mix(in srgb, #ffffff 92%, transparent);
}

.who-card-featured .who-gets .glyph {
    color: #7ff0c4;
}

.who-card-featured .who-for {
    color: color-mix(in srgb, #ffffff 65%, transparent);
}

.who-card-featured .who-shops {
    color: color-mix(in srgb, #ffffff 80%, transparent);
}

/* --- Which plan do I need ------------------------------------------------------ */

.finder {
    background:
        radial-gradient(ellipse 60% 80% at 0% 0%,
            color-mix(in srgb, var(--accent) 7%, transparent) 0%, transparent 70%),
        var(--surface);
    padding: 1.5rem;
}

.finder-head {
    align-items: flex-start;
    display: flex;
    gap: 0.8rem;
    margin-bottom: 1.1rem;
}

.finder-head h3 {
    margin: 0 0 0.2rem;
}

.finder-head .field-hint {
    margin: 0;
}

.finder-mark {
    align-items: center;
    background: var(--accent);
    border-radius: 12px;
    color: #ffffff;
    display: inline-flex;
    flex-shrink: 0;
    height: 42px;
    justify-content: center;
    width: 42px;
}

/* The questions, and the answer beside them where there is room: the verdict changes with every
   tick, and below the list it changed where nobody was looking. */
.finder-body {
    align-items: start;
    display: grid;
    gap: 1.25rem;
    grid-template-columns: minmax(0, 1fr) minmax(16rem, 22rem);
}

.finder-topics {
    display: grid;
    gap: 1rem;
}

.finder-topic h4 {
    color: var(--accent-dark);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    margin: 0 0 0.45rem;
    text-transform: uppercase;
}

.finder-options {
    display: grid;
    gap: 0.5rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
    list-style: none;
    margin: 0;
    padding: 0;
}

/* A link drawn as a checkbox, so the finder needs no form on a public page. */
.finder-chip {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    color: inherit;
    display: flex;
    gap: 0.65rem;
    height: 100%;
    min-height: 48px;
    padding: 0.6rem 0.8rem;
    text-decoration: none;
    transition: border-color 0.12s ease, background 0.12s ease;
}

.finder-chip:hover {
    background: color-mix(in srgb, var(--accent) 5%, var(--surface));
    border-color: var(--accent);
}

.finder-chip-on {
    background: color-mix(in srgb, var(--accent) 9%, var(--surface));
    border-color: var(--accent);
    font-weight: 600;
}

.finder-box {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 6px;
    color: var(--accent-dark);
    display: inline-flex;
    flex-shrink: 0;
    font-weight: 700;
    height: 22px;
    justify-content: center;
    line-height: 1;
    width: 22px;
}

.finder-chip-on .finder-box {
    background: var(--accent);
    border-color: var(--accent);
    color: #ffffff;
}

/* The answer. Basic is a calm green; Pro turns purple, the colour it wears everywhere else. */
.finder-verdict {
    background: var(--good-soft);
    border: 1px solid color-mix(in srgb, var(--good) 30%, var(--line));
    border-radius: calc(var(--radius) * 1.5);
    padding: 1.2rem 1.25rem;
    position: sticky;
    top: 5.5rem;
}

.finder-verdict p {
    margin: 0 0 0.4rem;
}

.finder-plan {
    background: var(--good);
    border-radius: 999px;
    color: #ffffff;
    display: inline-block;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    margin-bottom: 0.6rem;
    padding: 0.15rem 0.65rem;
    text-transform: uppercase;
}

.finder-answer {
    font-size: 1.3rem;
    font-weight: 700;
    letter-spacing: -0.015em;
}

.finder-why {
    display: grid;
    gap: 0.35rem;
    list-style: none;
    margin: 0.7rem 0 1rem;
    padding: 0;
}

.finder-why li {
    align-items: flex-start;
    display: flex;
    font-size: 0.92rem;
    gap: 0.45rem;
}

.finder-why .glyph {
    color: var(--good);
    flex-shrink: 0;
    margin-top: 0.15rem;
}

.finder-verdict .action-row {
    flex-wrap: wrap;
}

.finder-verdict-pro {
    background: linear-gradient(160deg, var(--accent) 0%, var(--accent-dark) 100%);
    border-color: var(--accent-dark);
    color: #ffffff;
}

.finder-verdict-pro .finder-plan {
    background: #ffffff;
    color: var(--accent-dark);
}

.finder-verdict-pro .finder-why li {
    color: color-mix(in srgb, #ffffff 90%, transparent);
}

.finder-verdict-pro .finder-why .glyph {
    color: #7ff0c4;
}

/* The primary button would be purple on purple, so it turns white, as on the Pro price card. */
.finder-verdict-pro .button-primary {
    background: #ffffff;
    border-color: #ffffff;
    color: var(--accent-dark);
}

.finder-verdict-pro .button:not(.button-primary) {
    background: transparent;
    border-color: color-mix(in srgb, #ffffff 55%, transparent);
    color: #ffffff;
}

@media (max-width: 60rem) {
    .finder-body {
        grid-template-columns: 1fr;
    }

    .finder-verdict {
        position: static;
    }
}

/* --- Everything, or only what differs ------------------------------------------ */

/* A second question, so set apart from the kinds of business by a rule and in ink rather than
   purple: two rows of the same pills read as one control. */
.compare-filter {
    align-items: center;
    border-top: 1px solid var(--line);
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    justify-content: center;
    margin: 1.5rem auto 1.25rem;
    max-width: 30rem;
    padding-top: 1.1rem;
}

.compare-filter .field-hint {
    margin-right: 0.3rem;
}

.compare-filter .switch-current {
    background: var(--ink);
    border-color: var(--ink);
}

/* --- Shelli Oficina ------------------------------------------------------------- */

/* Each tool in the colour a shop already knows it by. Set once per card, and every part of the
   card reads it, so the three stay apart without three copies of each rule. */
.office-sheets {
    --tool: #1d7a46;
}

.office-docs {
    --tool: #2a62c9;
}

.office-pdf {
    --tool: #c8382f;
}

.office-grid {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
    margin-bottom: 1rem;
}

.office-tool {
    border-top: 4px solid var(--tool);
    padding: 1.3rem 1.25rem;
}

.office-head {
    align-items: center;
    display: flex;
    gap: 0.75rem;
    margin-bottom: 0.8rem;
}

.office-head h3 {
    font-size: 1.1rem;
    margin: 0;
}

.office-mark {
    align-items: center;
    background: color-mix(in srgb, var(--tool) 12%, var(--surface));
    border-radius: 12px;
    color: var(--tool);
    display: inline-flex;
    flex-shrink: 0;
    height: 44px;
    justify-content: center;
    width: 44px;
}

.office-all {
    color: var(--good);
    font-size: 0.75rem;
    font-weight: 700;
}

.office-list {
    display: grid;
    gap: 0.4rem;
    margin: 0;
    padding-left: 1.1rem;
}

.office-list li::marker {
    color: var(--tool);
}

/* What Pro adds, as one strip under the three: two extras and a size, which do not need three
   cards of their own. */
.office-pro {
    align-items: flex-start;
    background: color-mix(in srgb, var(--accent) 7%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--accent) 28%, var(--line));
    border-radius: calc(var(--radius) * 1.5);
    display: flex;
    gap: 0.9rem;
    padding: 1.1rem 1.25rem;
}

.office-pro h3 {
    color: var(--accent-dark);
    font-size: 1rem;
    margin: 0 0 0.4rem;
}

.office-pro ul {
    display: grid;
    gap: 0.3rem;
    margin: 0;
    padding-left: 1.1rem;
}

.office-pro-mark {
    align-items: center;
    background: var(--accent);
    border-radius: 10px;
    color: #ffffff;
    display: inline-flex;
    flex-shrink: 0;
    height: 36px;
    justify-content: center;
    width: 36px;
}

/* --- Shelli IA ------------------------------------------------------------------ */

/* The band the section opens on: deep purple with a light from one corner, white type, and what
   the assistant does as tiles across it. */
.ia-hero {
    background:
        radial-gradient(ellipse 55% 90% at 100% 0%,
            color-mix(in srgb, var(--accent-bright) 70%, transparent) 0%, transparent 70%),
        linear-gradient(150deg, #2a1764 0%, var(--accent-dark) 100%);
    border-radius: calc(var(--radius) * 2.5);
    color: #ffffff;
    margin-bottom: 2.25rem;
    padding: clamp(1.75rem, 4vw, 2.75rem);
    text-align: center;
}

.ia-hero h2 {
    color: #ffffff;
    font-size: clamp(1.5rem, 3vw, 2rem);
    margin: 0.6rem 0 0.5rem;
}

.ia-spark {
    align-items: center;
    background: color-mix(in srgb, #ffffff 16%, transparent);
    border-radius: 16px;
    color: #ffe08a;
    display: inline-flex;
    height: 56px;
    justify-content: center;
    width: 56px;
}

.ia-hero-lede {
    color: color-mix(in srgb, #ffffff 86%, transparent);
    margin: 0 auto 1.5rem;
    max-width: 48rem;
}

/* What a shop says to it, as speech: a white bubble with its tail, and where it is said above
   it. Sentences rather than feature names, because "it adds the item" is believed once it is
   heard. */
.ia-says {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
    list-style: none;
    margin: 0;
    padding: 0;
    text-align: left;
}

.ia-says li {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
}

.ia-says-where {
    align-items: center;
    color: color-mix(in srgb, #ffffff 80%, transparent);
    display: flex;
    flex-wrap: wrap;
    font-size: 0.78rem;
    font-weight: 700;
    gap: 0.35rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.ia-says-where .glyph {
    color: #ffe08a;
}

.ia-says q {
    background: #ffffff;
    border-radius: 16px 16px 16px 4px;
    box-shadow: 0 10px 24px -14px rgb(0 0 0 / 55%);
    color: var(--ink);
    flex: 1;
    font-weight: 600;
    padding: 0.8rem 0.95rem;
    quotes: none;
}

/* "With Pro", the same small tag on the band, on the ladder and nowhere else. */
.ia-tag {
    background: var(--accent);
    border-radius: 999px;
    color: #ffffff;
    display: inline-block;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    margin-left: 0.3rem;
    padding: 0.1rem 0.5rem;
    vertical-align: 0.1em;
    white-space: nowrap;
}

.ia-says .ia-tag {
    background: #ffffff;
    color: var(--accent-dark);
}

.ia-subtitle {
    margin: 0 0 0.4rem;
    text-align: center;
}

.ia-engines-lede {
    color: var(--ink-soft);
    margin: 0 auto 1.25rem;
    max-width: 46rem;
    text-align: center;
}

/* Not .field-hint: faint grey reads as small print, and this line says the dollars are not ours. */
.ia-ours {
    background: color-mix(in srgb, var(--accent) 6%, var(--surface));
    border-left: 3px solid var(--accent);
    border-radius: 0 10px 10px 0;
    color: var(--ink);
    font-size: 0.9rem;
    margin: 0.7rem 0 0;
    padding: 0.6rem 0.75rem;
}

.engine-grid,
.ia-tables {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
    margin-bottom: 2rem;
}

/* Two engines are free and one is on the shop's own account, and the colour says which before
   the words do: green for nothing to pay, amber for a bill that comes from somebody else. */
.engine {
    --engine: var(--good);
    border-top: 4px solid var(--engine);
    padding: 1.4rem 1.3rem;
}

.engine-online {
    --engine: #b26a00;
}

.engine-mark {
    align-items: center;
    background: color-mix(in srgb, var(--engine) 12%, var(--surface));
    border-radius: 12px;
    color: var(--engine);
    display: flex;
    height: 44px;
    justify-content: center;
    margin-bottom: 0.8rem;
    width: 44px;
}

.engine h4,
.ia-table h4 {
    margin: 0.2rem 0 0.5rem;
}

/* What it costs, above the name rather than beside it. Two of the three are free and that is the
   thing a reader is looking for, so it goes where the eye lands first. */
.engine-cost {
    background: color-mix(in srgb, var(--engine) 12%, var(--surface));
    border-radius: 999px;
    color: var(--engine);
    display: inline-block;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    padding: 0.15rem 0.6rem;
    text-transform: uppercase;
}

.engine p {
    color: var(--ink-soft);
    margin: 0;
}

/* The ladder, three rungs across: each needs more of the machine than the one before it, and the
   number climbs with the colour. */
.ia-ladder {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
    list-style: none;
    margin: 0 0 2rem;
    padding: 0;
}

.ia-rung {
    --rung: color-mix(in srgb, var(--accent) 45%, var(--surface));
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: calc(var(--radius) * 1.5);
    padding: 1.3rem 1.25rem;
    position: relative;
}

.ia-rung:nth-child(2) {
    --rung: color-mix(in srgb, var(--accent) 72%, var(--surface));
}

.ia-rung:nth-child(3) {
    --rung: var(--accent-dark);
}

.ia-rung-step {
    align-items: center;
    background: var(--rung);
    border-radius: 50%;
    color: #ffffff;
    display: inline-flex;
    font-weight: 700;
    height: 32px;
    justify-content: center;
    margin-bottom: 0.6rem;
    width: 32px;
}

.ia-rung h4 {
    margin: 0 0 0.7rem;
}

.ia-rung ul {
    display: grid;
    gap: 0.45rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.ia-rung li {
    align-items: flex-start;
    display: flex;
    gap: 0.45rem;
}

.ia-rung li .glyph {
    color: var(--good);
    flex-shrink: 0;
    margin-top: 0.2rem;
}

.ia-table h4 {
    align-items: center;
    display: flex;
    gap: 0.45rem;
}

.ia-table h4 .glyph {
    color: var(--accent);
}

/* What is worth singling out about one model, as a tag under its name rather than jammed on to
   the end of it. Inline, it read as part of the name: "Qwen 2.5 7Bbest Spanish". */
.ia-best {
    background: var(--good-soft);
    border-radius: 999px;
    color: var(--good);
    display: table;
    font-size: 0.72rem;
    font-weight: 700;
    margin-top: 0.25rem;
    padding: 0.05rem 0.5rem;
}

/* What it never does, as three promises in a row rather than a bulleted card. */
.ia-promises {
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: calc(var(--radius) * 1.5);
    padding: 1.2rem 1.25rem;
}

.ia-promises h4 {
    margin: 0 0 0.8rem;
    text-align: center;
}

.ia-promises ul {
    display: grid;
    gap: 0.75rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
    list-style: none;
    margin: 0;
    padding: 0;
}

.ia-promises li {
    align-items: center;
    display: flex;
    gap: 0.6rem;
}

.ia-promises .glyph {
    color: var(--accent);
    flex-shrink: 0;
}

/* --- What is promised, and what is sold on top ---------------------------------- */

.promise {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr));
}

/* Sentences rather than a table, with the marker the browser draws rather than a tick that would
   read as a plan including something. */
.plain-list {
    margin: 0.8rem 0 0;
    padding-left: 1.1rem;
}

.plain-list li {
    margin-bottom: 0.45rem;
}

.plain-list li:last-child {
    margin-bottom: 0;
}

.plain-list .muted {
    display: block;
    font-size: 0.9rem;
}

.mini-block .lede {
    max-width: 60ch;
}

/* A model name in a row header, not the uppercase column-header style .data-table gives it. */
.ia-table th[scope="row"] {
    color: var(--ink);
    font-size: 0.95rem;
    font-weight: 600;
    letter-spacing: normal;
    text-transform: none;
}

/* No 42rem floor here: in a card it overflowed and hid the memory column behind a scroll. */
.ia-table .data-table {
    min-width: 0;
}

/* The figures take the room they need and the name takes the rest, so the two columns on the
   right cannot be squeezed to nothing. */
.ia-table td {
    text-align: right;
    white-space: nowrap;
    width: 1px;
}

/* --- A shop's report tabs ------------------------------------------------------ */

/* Two rows of chips: the views one summary request fills, then the detailed reports, which are
   tinted apart because each one is a request of its own. */
.report-tabs {
    display: grid;
    gap: 0.75rem;
    margin: 0 0 0.5rem;
}

.report-tab-group {
    display: grid;
    gap: 0.35rem;
}

.report-tab-group-label {
    color: var(--ink-faint);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.report-tab-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.report-tab {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--ink-soft);
    display: inline-flex;
    font-size: 0.9rem;
    font-weight: 600;
    gap: 0.4rem;
    padding: 0.4rem 0.85rem;
    text-decoration: none;
}

.report-tab:hover {
    border-color: var(--accent);
    color: var(--accent-dark);
}

.report-tab-detailed {
    background: color-mix(in srgb, var(--maui) 6%, var(--surface));
}

.report-tab-current,
.report-tab-current:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

.report-tab-lede {
    color: var(--ink-soft);
    margin: 0 0 1rem;
}

.report-detail-table td {
    vertical-align: top;
    white-space: nowrap;
}

/* A cell of several parts - a number, its kind, its pills - stays one value, and on a phone
   card it sits beside its label as one item. */
.cell-stack {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.2rem 0.4rem;
    white-space: normal;
}

.cell-stack .tile-note {
    margin: 0;
}

.data-table th.num,
.data-table td.num {
    text-align: right;
    white-space: nowrap;
}

/* Greyed, with only the money struck: the pill beside the number says why. */
.data-table tr.row-voided td {
    color: var(--ink-faint);
}

.data-table tr.row-voided td.num {
    text-decoration: line-through;
}

@media (width <= 860px) {
    .report-detail-table td,
    .data-table th.num,
    .data-table td.num {
        text-align: left;
        white-space: normal;
    }
}

@media (max-width: 640px) {
    .report-tab {
        font-size: 0.85rem;
        padding: 0.35rem 0.7rem;
    }
}

/* --- The report page on a wide screen ----------------------------------------- */

/* Figures rather than prose, so it takes the width a monitor offers instead of the 64rem reading
   column; the tiles had four cramped to a row with every amount broken over two lines. */
.content:has(.report-tabs) {
    max-width: 110rem;
}

/* Rows that come out even: three to a row, four when a tab has exactly four, two when it has two.
   auto-fit left a row of four and an orphan pair under it. */
@media (width > 620px) {
    .content:has(.report-tabs) .tile-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (width > 1100px) {
    .content:has(.report-tabs) .tile-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .content:has(.report-tabs) .tile-grid:has(> :nth-child(2):last-child),
    .content:has(.report-tabs) .tile-grid:has(> :nth-child(4):last-child) {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (width > 1400px) {
    .content:has(.report-tabs) .tile-grid:has(> :nth-child(4):last-child) {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

/* A television is read from across the room: bigger figures, not more of them. */
@media (width > 1800px) {
    .content:has(.report-tabs) .tile {
        padding: 1.15rem 1.35rem;
    }

    .content:has(.report-tabs) .tile-value {
        font-size: 2.2rem;
    }
}

/* The times on offer for one day: small radio cards in a grid rather than one long column. */
.time-grid {
    display: grid;
    gap: 0.5rem;
    grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
    margin-bottom: 1rem;
}

.time-grid .method-option {
    margin-bottom: 0;
    padding: 0.55rem 0.7rem;
}

/* A shop's extras, one switch each. */
.extra-list {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    list-style: none;
    margin: 1rem 0 0;
    padding: 0;
}

.extra-item {
    align-items: flex-start;
    border: 1px solid var(--line);
    border-radius: var(--radius-panel);
    display: flex;
    gap: 1rem;
    justify-content: space-between;
    padding: 0.9rem 1rem;
}

.extra-body {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    min-width: 0;
}

.extra-head {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.extra-name {
    font-weight: 600;
}

.extra-price {
    color: var(--ink-soft);
}

.extra-note {
    margin: 0;
}

.extra-switch {
    background: var(--line);
    border: 0;
    border-radius: 999px;
    cursor: pointer;
    flex: none;
    height: 1.75rem;
    padding: 0;
    position: relative;
    transition: background 0.15s ease;
    width: 3.1rem;
}

.extra-switch-knob {
    background: var(--surface);
    border-radius: 50%;
    box-shadow: var(--shadow);
    height: 1.35rem;
    left: 0.2rem;
    position: absolute;
    top: 0.2rem;
    transition: transform 0.15s ease;
    width: 1.35rem;
}

.extra-switch-on {
    background: var(--good);
}

.extra-switch-on .extra-switch-knob {
    transform: translateX(1.35rem);
}

.extra-switch:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.extra-switch:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

/* --- Renewing several shops in one payment ---------------------------------------
   One block per shop: the switch that puts its renewal in the basket, then what the term
   carries, each with its figure. A shop left out is dimmed rather than hidden, so it is still
   there to tick. */
.renewal-list {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    list-style: none;
    margin: 1rem 0 0;
    padding: 0;
}

.renewal-shop {
    border: 1px solid var(--line);
    border-radius: var(--radius-panel);
    padding: 0.9rem 1rem;
}

.renewal-shop-off .renewal-items,
.renewal-shop-off .renewal-figure {
    color: var(--ink-soft);
}

.renewal-head {
    align-items: center;
    display: flex;
    gap: 0.9rem;
}

.renewal-name {
    display: flex;
    flex: 1;
    flex-direction: column;
    min-width: 0;
}

.renewal-figure {
    font-size: 1.1rem;
    font-weight: 700;
    white-space: nowrap;
}

.renewal-note {
    margin: 0.5rem 0 0;
}

.renewal-items {
    border-top: 1px solid var(--line);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    list-style: none;
    margin: 0.75rem 0 0;
    padding: 0.75rem 0 0;
}

.renewal-items > li {
    align-items: center;
    display: flex;
    gap: 0.75rem;
    justify-content: space-between;
}

.renewal-control {
    align-items: center;
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

/* The shop page's steps, smaller: here they sit on a line of text rather than in a panel. */
.renewal-step {
    cursor: pointer;
    font-size: 1.2rem;
    height: 36px;
    width: 36px;
}

.renewal-left-out {
    color: var(--ink-soft);
}

/* Title and "renew all" on one line, the button out of the way of the list. */
.renewal-top {
    align-items: flex-start;
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    justify-content: space-between;
}

.renewal-top h2 {
    margin-bottom: 0.25rem;
}

.renewal-overdue {
    color: var(--bad);
    font-weight: 600;
}

/* What the term carries, folded to one line under the shop until somebody opens it. */
.renewal-detail {
    margin-top: 0.5rem;
}

.renewal-detail > summary {
    color: var(--ink-soft);
    cursor: pointer;
    font-size: 0.9rem;
}

.renewal-detail .renewal-items {
    margin-top: 0.5rem;
}

/* Shops not payable yet: one link under the list, opened only on request. */
.renewal-later-wrap {
    margin-top: 1rem;
}

.renewal-later-wrap > summary {
    color: var(--accent);
    cursor: pointer;
    font-size: 0.95rem;
}

.renewal-later {
    border-top: 1px solid var(--line);
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    list-style: none;
    margin: 0.6rem 0 0;
    padding: 0.6rem 0 0;
}

.renewal-later > li {
    align-items: baseline;
    display: flex;
    gap: 0.75rem;
    justify-content: space-between;
}

.renewal-later > li > span:first-child {
    display: flex;
    flex-direction: column;
}

.cart-note > summary {
    color: var(--accent);
    cursor: pointer;
    margin-bottom: 0.5rem;
}

.cart-sum {
    align-items: baseline;
    display: flex;
    font-size: 1.15rem;
    justify-content: space-between;
    margin: 0 0 0.5rem;
}

.cart-sum strong {
    font-size: 1.35rem;
    white-space: nowrap;
}

@media (max-width: 560px) {
    .renewal-head {
        flex-wrap: wrap;
    }

    .renewal-figure {
        margin-left: auto;
    }
}
