﻿/* =====================================================================================
   Net-Cloud public website

   The palette comes from the logo (netcloud-logo.svg): #D9101E is the mark's red and
   #A50A18 its shade. Red is used as a dominant block colour — hero, portal panel, primary
   action — rather than as a sprinkled accent, which is what makes the site read as
   Net-Cloud's rather than as a generic template.

   Type: Poppins for headings (it matches the wordmark's geometric Century Gothic), IBM
   Plex Sans for reading. Both carry fallbacks that match the CRM's own stack.
   ===================================================================================== */

:root {
    --red: #D9101E;
    --red-deep: #A50A18;
    --red-ink: #7A0712;
    --red-tint: #FDF2F2;
    --red-pale: #FBE3E4;
    --red-light: #FFC9CC;

    --ink: #14100F;
    --ink-2: #3B3532;
    --muted: #5C5451;
    --faint: #7B7370;
    --paper: #FBF9F8;
    --surface: #FFFFFF;
    --line: #E7E1DF;
    --line-strong: #D6CFCC;
    --field-line: #CBD5E1;

    --ok: #067647;
    --warn: #B45309;

    --display: 'Poppins', 'Century Gothic', 'Trebuchet MS', 'Segoe UI', sans-serif;
    --body: 'IBM Plex Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;

    --radius: 12px;
    --radius-sm: 6px;
    --wrap: 1200px;
}

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

html { scroll-behavior: smooth; }

body {
    margin: 0;
    background: var(--paper);
    color: var(--ink);
    font-family: var(--body);
    font-size: 16px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
    font-family: var(--display);
    margin: 0;
    line-height: 1.2;
    letter-spacing: -0.01em;
    text-wrap: balance;
}

h1 { font-size: clamp(2.05rem, 1.3rem + 2.4vw, 3.625rem); font-weight: 700; line-height: 1.08; }
h2 { font-size: clamp(1.65rem, 1.25rem + 1.3vw, 2.5rem); font-weight: 600; }
h3 { font-size: 1.1875rem; font-weight: 600; }

p { margin: 0; text-wrap: pretty; }

a { color: var(--red-deep); text-decoration: none; }
a:hover { color: var(--red); }

img { max-width: 100%; display: block; }

hr { height: 1px; background: var(--line); border: 0; margin: 0; }

/* Visible only to keyboard users, and only when it matters. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    background: var(--red-deep);
    color: #fff;
    padding: 0.75rem 1.25rem;
    z-index: 100;
}
.skip-link:focus { left: 0; color: #fff; }

:focus-visible {
    outline: 2px solid var(--red);
    outline-offset: 2px;
}

/* ---------------------------------------------------------------- layout */

.wrap {
    max-width: var(--wrap);
    margin: 0 auto;
    padding-left: 40px;
    padding-right: 40px;
}

.wrap.narrow { max-width: 820px; }

.section { padding-top: 88px; padding-bottom: 88px; }
.section-tight { padding-top: 56px; padding-bottom: 56px; }
.section-white {
    background: var(--surface);
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
}

.eyebrow {
    font-family: var(--display);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--red-deep);
    margin: 0;
}

.lede {
    font-size: 1.0625rem;
    color: var(--muted);
    margin-top: 0.875rem;
    max-width: 62ch;
}

.muted { color: var(--muted); }
.hint { font-size: 0.8125rem; color: var(--muted); }
.num { font-variant-numeric: tabular-nums; }

.grid { display: grid; gap: 1.5rem; }
.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* Asymmetric two-column splits. These exist as CLASSES rather than inline
   grid-template-columns on purpose: an inline style cannot be undone by a media
   query, so a page that inlines its columns stays two-up on a phone and squashes. */
.split-7-5 { grid-template-columns: 7fr 5fr; }
.split-5-7 { grid-template-columns: 5fr 7fr; }
.split-wide { gap: 3rem; }
.align-start { align-items: start; }
.align-center { align-items: center; }

.stack { display: flex; flex-direction: column; }
.row { display: flex; align-items: center; }
.gap-sm { gap: 0.625rem; }
.gap { gap: 1rem; }
.gap-lg { gap: 1.75rem; }

.section-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 2rem;
    flex-wrap: wrap;
}

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

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.625rem;
    /* 48px: comfortably above the 44px touch minimum on a phone. */
    min-height: 48px;
    padding: 0 1.625rem;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    font-family: var(--display);
    font-size: 0.9375rem;
    font-weight: 600;
    cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease, box-shadow .2s ease, transform .12s ease;
}

.btn:active { transform: translateY(1px); }

.btn-red { background: var(--red); color: #fff; box-shadow: 0 6px 18px rgba(165, 10, 24, .28); }
.btn-red:hover { background: var(--red-deep); color: #fff; }

.btn-ghost { background: transparent; color: var(--ink); border-color: var(--line-strong); }
.btn-ghost:hover { border-color: var(--ink); color: var(--ink); }

.btn-white { background: #fff; color: var(--red-deep); }
.btn-white:hover { background: var(--red-pale); color: var(--red-deep); }

/* On red: the border is the only edge, so it needs enough contrast to be a boundary. */
.btn-outline-white { color: #fff; border-color: rgba(255, 255, 255, .62); }
.btn-outline-white:hover { color: #fff; background: rgba(255, 255, 255, .12); }

.btn-block { width: 100%; }

.link-arrow {
    display: inline-flex;
    align-items: center;
    gap: 0.4375rem;
    font-weight: 600;
    font-size: 0.9375rem;
    white-space: nowrap;
}

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

.card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 1.625rem;
}

.card-flush { padding: 0; overflow: hidden; }

/* ------------------------------------------------------------------ forms */

.field-label {
    display: block;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--ink-2);
    margin-bottom: 0.4375rem;
}

.field {
    width: 100%;
    height: 48px;
    padding: 0 0.875rem;
    border: 1px solid var(--field-line);
    border-radius: var(--radius-sm);
    background: #fff;
    color: var(--ink);
    font-family: inherit;
    font-size: 0.9375rem;
    transition: border-color .15s ease, box-shadow .15s ease;
}

textarea.field {
    height: auto;
    min-height: 128px;
    padding: 0.75rem 0.875rem;
    line-height: 1.5;
    resize: vertical;
}

select.field { appearance: none; padding-right: 2.25rem; }

/* The caret is drawn as a background image so the select keeps its native behaviour. */
.select-wrap { position: relative; }
.select-wrap::after {
    content: "";
    position: absolute;
    right: 0.875rem;
    top: 50%;
    width: 10px;
    height: 6px;
    transform: translateY(-50%);
    background: var(--faint);
    clip-path: polygon(0 0, 100% 0, 50% 100%);
    pointer-events: none;
}

.field:focus {
    outline: none;
    border-color: var(--red);
    box-shadow: 0 0 0 3px rgba(217, 16, 30, .14);
}

.field::placeholder { color: var(--muted); }

.field-hint { font-size: 0.78125rem; color: var(--muted); margin-top: 0.4375rem; }

.field-error { border-color: #e0736f; }

/* The honeypot: off-screen and out of the tab order, but a bot filling every input finds it. */
.hp {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.form-note {
    display: flex;
    gap: 0.6875rem;
    align-items: flex-start;
    padding: 0.875rem 1rem;
    border-radius: 8px;
    font-size: 0.84375rem;
}

.note-ok { background: #ECFDF3; border: 1px solid #A6F4C5; color: #027A48; }
.note-err { background: var(--red-tint); border: 1px solid #F5C2C2; color: var(--red-ink); }
.note-warn { background: #FFFBEB; border: 1px solid #FDE68A; color: var(--warn); }

/* ------------------------------------------------------------------- tags */

.tag {
    display: inline-block;
    background: var(--red-tint);
    color: var(--red-deep);
    font-family: var(--display);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    padding: 0.25rem 0.5625rem;
    border-radius: 4px;
}

/* ------------------------------------------------------------- site header */

.site-header {
    background: var(--paper);
    border-bottom: 1px solid var(--line);
    position: sticky;
    top: 0;
    z-index: 30;
}

.site-header .wrap {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    min-height: 76px;
}

.brand {
    display: flex;
    align-items: center;
    gap: 0.6875rem;
    color: var(--ink);
    flex-shrink: 0;
}

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

.brand-name {
    font-family: var(--display);
    font-weight: 600;
    font-size: 1.3125rem;
    letter-spacing: -0.02em;
}

.site-nav {
    display: flex;
    gap: 1.875rem;
    margin-left: 1.25rem;
    font-size: 0.9375rem;
    font-weight: 500;
}

.site-nav a { position: relative; color: var(--muted); padding-bottom: 3px; transition: color .2s ease; }
.site-nav a:hover { color: var(--ink); }
.site-nav a.active { color: var(--ink); }

/* The underline is drawn rather than bordered: a border on hover would move the row by 2px,
   and a transform can be animated where a border cannot. */
.site-nav a::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 2px;
    background: var(--red);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform .22s cubic-bezier(.22, .61, .36, 1);
}

.site-nav a:hover::after,
.site-nav a.active::after { transform: scaleX(1); }

.header-actions {
    display: flex;
    align-items: center;
    gap: 1.125rem;
    margin-left: auto;
}

.header-phone {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.9375rem;
    font-weight: 500;
    color: var(--ink);
    white-space: nowrap;
}

.header-phone svg { flex-shrink: 0; }

.btn-header { min-height: 44px; padding: 0 1.25rem; font-size: 0.875rem; white-space: nowrap; }

/* Mobile navigation: a details/summary disclosure, so it works without any JavaScript. */
.nav-toggle { display: none; }

/* --------------------------------------------------------------- hero ---- */

.hero {
    position: relative;
    background: var(--red-deep);
    color: #fff;
    overflow: hidden;
}

.hero-mark {
    position: absolute;
    right: -170px;
    top: -130px;
    opacity: .07;
    pointer-events: none;
}

.hero .wrap {
    position: relative;
    display: grid;
    grid-template-columns: 7fr 5fr;
    gap: 2.5rem;
    align-items: center;
    padding-top: 84px;
    padding-bottom: 92px;
}

.hero-photo {
    position: absolute;
    inset: 0;
    object-fit: cover;
    width: 100%;
    height: 100%;
    opacity: .18;
}

.hero h1 { max-width: 15ch; }

.hero .eyebrow { color: var(--red-light); }

.hero-lede {
    font-size: 1.1875rem;
    line-height: 1.65;
    color: var(--red-pale);
    margin-top: 1.375rem;
    max-width: 52ch;
}

.hero-actions { display: flex; gap: 0.875rem; margin-top: 2.125rem; flex-wrap: wrap; }

.hero-card {
    background: #fff;
    color: var(--ink);
    border-radius: 14px;
    padding: 1.75rem;
    box-shadow: 0 24px 60px rgba(60, 4, 10, .34);
}

/* ------------------------------------------------------------- packages -- */

.pkg-card { position: relative; display: flex; flex-direction: column; }

.pkg-card.featured {
    border-color: var(--red);
    border-width: 2px;
    box-shadow: 0 14px 40px rgba(165, 10, 24, .12);
}

.pkg-badge {
    position: absolute;
    top: -13px;
    left: 1.875rem;
    background: var(--red);
    color: #fff;
    font-family: var(--display);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    padding: 0.3125rem 0.75rem;
    border-radius: 4px;
}

.pkg-name {
    font-family: var(--display);
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--muted);
}

.pkg-speed { display: flex; align-items: baseline; gap: 0.5rem; margin-top: 0.875rem; }

.pkg-speed-n {
    font-family: var(--display);
    font-size: 2.75rem;
    font-weight: 700;
    line-height: 1;
}

.featured .pkg-speed-n { color: var(--red-deep); }

.pkg-speed-u { font-size: 1.0625rem; font-weight: 600; color: var(--muted); }

.pkg-price { margin-top: 1rem; font-size: 0.9375rem; }
.pkg-price strong { font-weight: 600; }

.pkg-features {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    font-size: 0.90625rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.pkg-features li { display: flex; gap: 0.625rem; align-items: flex-start; }
.pkg-features svg { flex-shrink: 0; margin-top: 3px; }

/* Pushes the button to the bottom so cards of different feature counts line up. */
.pkg-card .btn { margin-top: auto; }
.pkg-card .pkg-cta-space { margin-top: 1.75rem; }

/* ------------------------------------------------------------- coverage -- */

.city-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.875rem; }

.city {
    display: flex;
    align-items: center;
    gap: 0.5625rem;
    padding: 0.75rem 0.875rem;
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: 8px;
    font-size: 0.90625rem;
    font-weight: 500;
}

.city .dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--red);
    flex-shrink: 0;
}

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

.feature-icon {
    width: 46px;
    height: 46px;
    border-radius: 10px;
    background: var(--red-tint);
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ------------------------------------------------------------------ news - */

.news-card { display: flex; flex-direction: column; padding: 1.625rem; }
.news-card h3 { margin-top: 1rem; font-size: 1.125rem; line-height: 1.35; }

.prose { max-width: 68ch; }
.prose p { margin-bottom: 1rem; }
.prose p:last-child { margin-bottom: 0; }
.prose h2 { font-size: 1.5rem; margin: 2rem 0 0.75rem; }
.prose h3 { font-size: 1.1875rem; margin: 1.5rem 0 0.5rem; }
.prose ul, .prose ol { margin: 0 0 1rem; padding-left: 1.25rem; }
.prose li { margin-bottom: 0.375rem; }

/* --------------------------------------------------------------- footer -- */

.site-footer {
    background: var(--ink);
    color: #C9C2BF;
    padding-top: 60px;
    padding-bottom: 34px;
}

.site-footer h4 {
    font-family: var(--display);
    font-size: 0.8125rem;
    font-weight: 600;
    color: #fff;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.footer-col { display: flex; flex-direction: column; gap: 0.625rem; margin-top: 1rem; font-size: 0.875rem; }
.footer-col a { color: #C9C2BF; }
.footer-col a:hover { color: #fff; }

.footer-grid { display: grid; grid-template-columns: 4fr 2fr 2fr 4fr; gap: 2.5rem; }

.footer-legal {
    display: flex;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: wrap;
    font-size: 0.8125rem;
    /* Lighter than the body grey: #7B7370 on this ground does not clear 4.5:1. */
    color: #9A928F;
}

.footer-legal a { color: #9A928F; }
.footer-legal a:hover { color: #fff; }

.footer-rule { background: #2E2724; margin-top: 44px; margin-bottom: 22px; }

/* ================================================================= PORTAL = */

.portal-header { background: var(--ink); color: #fff; }

.portal-header .wrap {
    display: flex;
    align-items: center;
    gap: 1.25rem;
    min-height: 68px;
}

.portal-header .brand-name { color: #fff; font-size: 1.125rem; }
.portal-sep { color: #4E4643; }
.portal-label { font-size: 0.90625rem; color: #C9C2BF; }

.portal-user { display: flex; align-items: center; gap: 1rem; margin-left: auto; }

.portal-user-name { text-align: right; line-height: 1.35; }
.portal-user-name .n { font-size: 0.875rem; font-weight: 600; }
.portal-user-name .c { font-size: 0.78125rem; color: #9A928F; }

.avatar {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--red-deep);
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--display);
    font-weight: 600;
    font-size: 0.8125rem;
    color: #fff;
    flex-shrink: 0;
}

.portal-signout {
    color: #C9C2BF;
    font-size: 0.875rem;
    font-weight: 500;
    background: none;
    border: 0;
    cursor: pointer;
    font-family: inherit;
    min-height: 44px;
    display: flex;
    align-items: center;
    /* Two lines of "Sign / out" in a 68px header looks broken; it stays on one. */
    white-space: nowrap;
}

.portal-signout:hover { color: #fff; }

.portal-tabs { background: var(--surface); border-bottom: 1px solid var(--line); }
.portal-tabs .wrap { display: flex; gap: 1.75rem; padding-top: 1rem; overflow-x: auto; }

.portal-tab {
    font-family: var(--display);
    font-size: 0.90625rem;
    font-weight: 600;
    color: var(--muted);
    padding-bottom: 0.875rem;
    white-space: nowrap;
}

.portal-tab:hover { color: var(--ink); }
.portal-tab.active { color: var(--ink); box-shadow: inset 0 -2px 0 var(--red); }

.portal-body { padding-top: 40px; padding-bottom: 64px; }
.portal-body .wrap { max-width: 1120px; }

/* --- the amount-due panel -------------------------------------------- */

.due-panel {
    background: var(--red-deep);
    color: #fff;
    border-radius: var(--radius);
    padding: 2rem;
    position: relative;
    overflow: hidden;
}

.due-panel.settled { background: #075E45; }

.due-mark { position: absolute; right: -90px; top: -70px; opacity: .09; pointer-events: none; }

.due-k {
    font-family: var(--display);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--red-light);
}

.settled .due-k { color: #A6F4C5; }

.due-n {
    font-family: var(--display);
    font-size: clamp(2.125rem, 1.4rem + 2.2vw, 3.25rem);
    font-weight: 700;
    line-height: 1.1;
    margin-top: 0.625rem;
}

.due-when {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.625rem;
    font-size: 0.96875rem;
    color: var(--red-pale);
}

.settled .due-when { color: #D1FADF; }

.due-actions { display: flex; gap: 0.75rem; margin-top: 1.625rem; flex-wrap: wrap; }

.meta-k {
    font-family: var(--display);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--muted);
}

.meta-v { font-size: 0.96875rem; font-weight: 500; margin-top: 0.25rem; }

.sum-row { display: flex; justify-content: space-between; gap: 1.5rem; font-size: 0.9375rem; }

.status-dot {
    display: inline-flex;
    align-items: center;
    gap: 0.4375rem;
    font-weight: 600;
    color: var(--ok);
}

.status-dot .dot { width: 7px; height: 7px; border-radius: 50%; background: #17B26A; }
.status-dot.off { color: var(--muted); }
.status-dot.off .dot { background: #98A2B3; }

/* --- tables ----------------------------------------------------------- */

.table-scroll { overflow-x: auto; }

table.data { width: 100%; border-collapse: collapse; }

table.data th,
table.data td {
    text-align: left;
    padding: 0.875rem 1.25rem;
    font-size: 0.9375rem;
    border-bottom: 1px solid var(--line);
}

table.data th {
    font-family: var(--display);
    font-weight: 600;
    font-size: 0.75rem;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--muted);
    background: var(--paper);
    white-space: nowrap;
}

table.data tr:last-child td { border-bottom: 0; }

.r { text-align: right; }

.row-current { background: var(--red-tint); }

/* --- portal sign-in ---------------------------------------------------- */

.signin {
    display: grid;
    grid-template-columns: 5fr 7fr;
    min-height: calc(100vh - 0px);
}

.signin-brand {
    background: var(--red-deep);
    color: #fff;
    position: relative;
    overflow: hidden;
    padding: 3.25rem;
    display: flex;
    flex-direction: column;
}

.signin-brand .mark { position: absolute; left: -190px; bottom: -180px; opacity: .08; }

.signin-brand h2 { max-width: 16ch; font-size: clamp(1.75rem, 1.2rem + 1.6vw, 2.375rem); font-weight: 700; }

.signin-points { display: flex; flex-direction: column; gap: 0.875rem; margin-top: 2.125rem; }
.signin-points li { display: flex; gap: 0.75rem; align-items: center; font-size: 0.96875rem; color: var(--red-pale); }
.signin-points { list-style: none; padding: 0; margin-left: 0; }
.signin-points svg { flex-shrink: 0; }

.signin-form {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 3.25rem;
}

.signin-form-inner { width: 100%; max-width: 440px; }

/* ------------------------------------------------------------ responsive - */

@media (max-width: 1024px) {
    .hero .wrap { grid-template-columns: 1fr; gap: 2.25rem; padding-top: 56px; padding-bottom: 64px; }
    /* The portal's own splits collapse a step earlier than the marketing pages: the
       amount-due panel needs its full width before a tablet gets narrow. */
    .portal-body .split-7-5,
    .portal-body .split-5-7 { grid-template-columns: 1fr; }
    .hero-mark { right: -220px; top: -180px; }
    .cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .signin { grid-template-columns: 1fr; }
    .signin-brand { padding: 2.5rem 1.5rem; }
    .signin-form { padding: 2.5rem 1.5rem; }
}

@media (max-width: 860px) {
    .wrap { padding-left: 20px; padding-right: 20px; }
    .section { padding-top: 56px; padding-bottom: 56px; }

    /* The nav collapses into a disclosure. `details`/`summary` gives a working menu
       with no script, so it behaves before (and without) blazor.web.js. */
    .site-header .wrap { min-height: 64px; flex-wrap: wrap; }
    .nav-toggle { display: block; margin-left: auto; }
    .nav-toggle summary {
        list-style: none;
        width: 44px;
        height: 44px;
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
    }
    .nav-toggle summary::-webkit-details-marker { display: none; }
    .site-nav {
        display: none;
        flex-direction: column;
        gap: 0;
        margin: 0;
        width: 100%;
        border-top: 1px solid var(--line);
    }
    .nav-toggle[open] ~ .site-nav { display: flex; }
    .site-nav a {
        padding: 0.875rem 0;
        border-bottom: 1px solid var(--line);
        min-height: 44px;
        display: flex;
        align-items: center;
    }
    .site-nav a.active { border-bottom-color: var(--red); }
    .header-actions { margin-left: 0; gap: 0.75rem; }
    .header-phone { display: none; }

    .cols-2, .cols-3, .cols-4,
    .split-7-5, .split-5-7 { grid-template-columns: 1fr; }
    .split-wide { gap: 1.75rem; }
    .city-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .footer-grid { grid-template-columns: 1fr; gap: 1.75rem; }
    .section-head { align-items: flex-start; }

    .due-panel { padding: 1.5rem; }
    .due-actions .btn { width: 100%; }
    .portal-body { padding-top: 24px; }

    /* The portal header has four things competing for 375px. The product label goes
       first — the mark and wordmark already say where you are — then the subscriber's
       name, leaving the Consumer ID, which is what they came to quote. */
    .portal-sep, .portal-label { display: none; }
    .portal-user { gap: 0.625rem; }
    .portal-user-name .n { display: none; }
    .portal-user-name .c { font-size: 0.8125rem; }
    .portal-header .brand-name { font-size: 1rem; }
    .portal-tabs .wrap { gap: 1.25rem; }

    table.data th, table.data td { padding: 0.75rem 0.875rem; font-size: 0.875rem; }
}

@media (max-width: 560px) {
    .city-grid { grid-template-columns: 1fr; }
    .live-readout { grid-template-columns: 1fr; }
    .live-head { flex-direction: column; gap: 0.875rem; }
    .hero-actions .btn { width: 100%; }
}

/* --- live speed (portal connection page) -------------------------------
   The portal twin of the CRM status popup's Live Speed block. Drawn as plain SVG by
   js/live-speed.js — the line colours live here rather than in the script so the chart
   stays part of the palette. */

.live-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1.5rem;
    padding: 1.375rem 1.625rem;
    border-bottom: 1px solid var(--line);
}

.live-body { padding: 1.375rem 1.625rem; }

.live-state {
    display: inline-flex;
    align-items: center;
    gap: 0.4375rem;
    flex: none;
    padding: 0.25rem 0.625rem;
    border-radius: 999px;
    font-size: 0.8125rem;
    font-weight: 600;
    white-space: nowrap;
}

.live-state::before {
    content: "";
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: currentColor;
}

.live-state-ok { color: var(--ok); background: #ECFDF3; }
.live-state-warn { color: var(--warn); background: #FFFAEB; }
.live-state-off { color: var(--muted); background: var(--paper); }

.live-readout {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.875rem;
}

.live-box {
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 0.875rem 1rem;
    background: var(--paper);
}

.live-num {
    display: block;
    font-family: var(--display);
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1.2;
    /* The figure is replaced every second; tabular digits stop the box twitching. */
    font-variant-numeric: tabular-nums;
}

.live-lbl {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.8125rem;
    color: var(--muted);
    margin-top: 0.125rem;
}

.live-lbl::before,
.live-key::before {
    content: "";
    width: 10px;
    height: 3px;
    border-radius: 2px;
}

.live-lbl-down::before, .live-key-down::before { background: #2563EB; }
.live-lbl-up::before, .live-key-up::before { background: var(--red); }

.live-note {
    margin-top: 0.875rem;
    padding: 0.625rem 0.875rem;
    border-radius: var(--radius-sm);
    background: var(--red-tint);
    color: var(--red-ink);
    font-size: 0.875rem;
}

.live-chart-wrap { position: relative; margin-top: 1rem; }

.live-chart {
    display: block;
    width: 100%;
    height: auto;
    /* The pointer reads the chart, so say so rather than leaving a text caret over it. */
    cursor: crosshair;
    touch-action: pan-y;
}

/* The hover readout: what the trace was carrying at the second under the pointer. Positioned
   by the script in this wrapper's pixels, and lifted clear of the pointer so it never sits
   under the finger on a touch screen. */
.live-tip {
    position: absolute;
    transform: translate(-50%, -100%);
    pointer-events: none;
    z-index: 1;
    min-width: 168px;
    padding: 0.5rem 0.625rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    box-shadow: 0 6px 16px rgba(20, 16, 15, .12);
    font-size: 0.8125rem;
}

/* Flipped under the trace when there is no room above it. */
.live-tip.below { transform: translate(-50%, 0); }

.live-tip-when {
    display: block;
    font-size: 0.75rem;
    color: var(--faint);
    margin-bottom: 0.25rem;
}

.live-tip-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.live-tip-row .live-key { color: var(--muted); }
/* The figure never wraps: "11.38 Mbps" split over two lines reads as two numbers. */
.live-tip-row b { white-space: nowrap; }

.live-hover line { stroke: var(--line-strong); stroke-width: 1; stroke-dasharray: 3 3; }
.live-hover .live-dot-down { fill: #2563EB; }
.live-hover .live-dot-up { fill: var(--red); }

.live-chart .live-grid line { stroke: var(--line); stroke-width: 1; }

.live-chart text {
    font-family: var(--body);
    font-size: 10px;
    fill: var(--faint);
}

.live-line {
    fill: none;
    stroke-width: 2;
    stroke-linejoin: round;
    stroke-linecap: round;
}

.live-line-down { stroke: #2563EB; }
.live-line-up { stroke: var(--red); }

/* The fill under each trace. Kept faint so the two series read through each other and the
   grid stays visible; the trace above carries the colour at full strength. */
.live-area {
    stroke: none;
    opacity: 0.2;
}

.live-area-down { fill: #2563EB; }
.live-area-up { fill: var(--red); }

.live-legend {
    display: flex;
    align-items: center;
    gap: 1.25rem;
    margin-top: 0.5rem;
    font-size: 0.8125rem;
    color: var(--muted);
}

.live-key { display: inline-flex; align-items: center; gap: 0.375rem; }


/* ================================================================= MOTION =
   The site read as a stack of printed pages. This is the whole of the movement on it, kept
   deliberately small: nothing travels more than 16px, nothing runs longer than 600ms, and
   nothing loops in the reader's eyeline except one 3s pulse on the coverage dots.

   Two safety rails:
   - The reveal styles hide content ONLY under `html.js-motion`, which the inline script in
     App.razor sets and takes away again if js/motion.js never arrives. A failed script leaves
     a complete page, not a blank one.
   - The whole layer is switched off under prefers-reduced-motion, at the end of this file.
   ------------------------------------------------------------------------ */

/* --- scroll reveal ----------------------------------------------------- */

.js-motion [data-reveal],
.js-motion [data-reveal-group] > * {
    opacity: 0;
    transform: translateY(16px);
}

.js-motion [data-reveal].shown,
.js-motion [data-reveal-group].shown > * {
    opacity: 1;
    transform: none;
    transition: opacity .5s ease, transform .55s cubic-bezier(.22, .61, .36, 1);
}

/* A group's children come in one after another, which reads as a list being dealt out rather
   than a block appearing. Capped at eight: past that the last card waits too long. */
.js-motion [data-reveal-group].shown > *:nth-child(2) { transition-delay: .07s; }
.js-motion [data-reveal-group].shown > *:nth-child(3) { transition-delay: .14s; }
.js-motion [data-reveal-group].shown > *:nth-child(4) { transition-delay: .21s; }
.js-motion [data-reveal-group].shown > *:nth-child(5) { transition-delay: .28s; }
.js-motion [data-reveal-group].shown > *:nth-child(6) { transition-delay: .35s; }
.js-motion [data-reveal-group].shown > *:nth-child(7) { transition-delay: .42s; }
.js-motion [data-reveal-group].shown > *:nth-child(n+8) { transition-delay: .49s; }

/* --- the hero ---------------------------------------------------------- */

@keyframes rise {
    from { opacity: 0; transform: translateY(18px); }
}

@keyframes drift {
    50% { transform: translate3d(-16px, 14px, 0) rotate(-1.5deg); }
}

@keyframes sink {
    from { transform: scale(1.08); }
    to { transform: scale(1); }
}

/* The hero is above the fold on every load, so it plays on arrival rather than waiting for an
   observer. `backwards` matters: without it the delayed items flash at full opacity first. */
@media (prefers-reduced-motion: no-preference) {
    .hero .eyebrow,
    .hero h1,
    .hero-lede,
    .hero-actions,
    .hero .hero-card {
        animation: rise .6s cubic-bezier(.22, .61, .36, 1) backwards;
    }

    .hero h1 { animation-delay: .06s; }
    .hero-lede { animation-delay: .12s; }
    .hero-actions { animation-delay: .18s; }
    .hero .hero-card { animation-delay: .16s; }

    /* The watermark drifts over half a minute — slow enough to read as depth rather than as
       something moving, and it is at 7% opacity behind the text either way. */
    .hero-mark { animation: drift 34s ease-in-out infinite; }

    /* A photographic banner settles instead of being pinned flat from the first frame. */
    .hero-photo { animation: sink 1.6s cubic-bezier(.22, .61, .36, 1) both; }
}

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

/* Only the marketing cards lift. A portal card is a document, and documents do not hover. */
.pkg-card,
.news-card {
    transition: transform .25s cubic-bezier(.22, .61, .36, 1), box-shadow .25s ease, border-color .25s ease;
}

.pkg-card:hover,
.news-card:hover {
    transform: translateY(-4px);
    border-color: var(--line-strong);
    box-shadow: 0 16px 34px rgba(20, 16, 15, .09);
}

.pkg-card.featured:hover {
    border-color: var(--red);
    box-shadow: 0 18px 40px rgba(165, 10, 24, .18);
}

/* --- buttons and links ------------------------------------------------- */

.btn-red:hover { box-shadow: 0 10px 26px rgba(165, 10, 24, .34); transform: translateY(-1px); }
.btn-red:active { transform: translateY(1px); }

.link-arrow svg { transition: transform .2s cubic-bezier(.22, .61, .36, 1); }
.link-arrow:hover svg { transform: translateX(4px); }

.footer-col a,
.footer-legal a,
.site-footer a { transition: color .18s ease; }

/* --- the header, once you have left the hero --------------------------- */

.site-header { transition: box-shadow .25s ease, background-color .25s ease; }

.site-header.scrolled {
    background: rgba(251, 249, 248, .88);
    backdrop-filter: saturate(140%) blur(10px);
    box-shadow: 0 6px 22px rgba(20, 16, 15, .07);
}

/* --- coverage ---------------------------------------------------------- */

.city { transition: border-color .2s ease, background-color .2s ease, transform .2s ease; }
.city:hover { border-color: var(--red-light); background: var(--red-tint); transform: translateY(-1px); }

.city .dot { position: relative; }

/* One slow ring off each dot, staggered, so the list of live areas looks live. Low contrast
   and a 3.4s cycle: it should be noticed on the second glance, not the first. */
@keyframes ping {
    0% { transform: scale(.6); opacity: .55; }
    70%, 100% { transform: scale(2.2); opacity: 0; }
}

@media (prefers-reduced-motion: no-preference) {
    .city .dot::after {
        content: "";
        position: absolute;
        inset: 0;
        border-radius: 50%;
        background: var(--red);
        animation: ping 3.4s ease-out infinite;
    }

    .city:nth-child(3n+2) .dot::after { animation-delay: .55s; }
    .city:nth-child(3n) .dot::after { animation-delay: 1.1s; }
}

/* --- feature blocks ---------------------------------------------------- */

.feature-icon { transition: transform .3s cubic-bezier(.22, .61, .36, 1), background-color .3s ease; }
.feature:hover .feature-icon { transform: translateY(-3px) scale(1.06); background: var(--red-pale); }

/* --- the coverage checker's answer ------------------------------------- */

/* The island replaces the card's contents in place, and an answer that simply appears is easy
   to miss when you were looking at the button you just pressed. */
@media (prefers-reduced-motion: no-preference) {
    .cc-in { animation: rise .35s cubic-bezier(.22, .61, .36, 1) backwards; }
}

/* Respect a reduced-motion preference. The rules above are written so that this block is
   enough on its own: the reveal targets are shown, the animations that are gated on
   no-preference never start, and what is left is reduced to an instant. */
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }

    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }

    /* Nothing may be left hidden waiting for a transition that will not run. */
    .js-motion [data-reveal],
    .js-motion [data-reveal-group] > * { opacity: 1 !important; transform: none !important; }

    .btn:active { transform: none; }
    .pkg-card:hover, .news-card:hover, .city:hover, .btn-red:hover { transform: none; }
}

/* Printing an invoice from the portal should not print the site furniture. */
@media print {
    .site-header, .site-footer, .portal-header, .portal-tabs, .no-print { display: none !important; }
    body { background: #fff; }
    .due-panel { background: #fff !important; color: #000 !important; border: 1px solid #000; }
    .due-k, .due-when { color: #000 !important; }
    .card { border: 1px solid #999; break-inside: avoid; }
}
