/**
 * portal.css - the Customer Portal's stylesheet.
 *
 * **Its own sheet, not the employee product's.** `css/style.css` arrives with
 * the Bridge menu and navbar; this surface has neither and must be free to move
 * without dragging Bridge with it. `tokens.css` is loaded BEFORE this file and
 * carries the palette as custom properties, so it can be shared without
 * bringing a shell along.
 *
 * **Every colour here is an alias.** The `--cp-` names at the top resolve to
 * tokens; none of them holds a colour of its own. Change a colour in
 * `tokens.css`, never here.
 *
 * **One size lever.** Every size is a `rem` against the root, which is set in
 * the TYPE section below. Change that number, not the hundred rules under it.
 *
 * Lifted out of `_shell.html`, where it was 876 lines of inline `<style>`:
 * inline meant it was re-sent on every page view and could never be cached,
 * and no editor treated it as CSS.
 */

/* ==== TOKENS ====
   **Aliases, not definitions.** Every colour resolves to a token in
   `static/tokens.css`, which is the one place the palette exists. The
   `--cp-` names stay because this shell's rules read them and the
   prefix says which surface a rule belongs to; none of them holds a
   colour of its own.

   This shell deliberately does NOT load `css/style.css`: that sheet is
   the employee product's and arrives with the employee menu and navbar.
   `tokens.css` carries custom properties and no rules, so it can be
   shared without bringing a shell with it.

   **No webfont.** A customer-facing page that fetches a face from a
   third party tells that host the customer opened their bill, which is
   the same reason both marks are served from the repo. The stack below
   is what a business machine already has, and every money column asks
   for tabular figures explicitly rather than relying on the face. */
:root {
    /* The horizontal inset every full-width band shares: the masthead, the
       content and the footer. Stated once, overridden per breakpoint. */
    --cp-gutter:   2rem;
    --cp-ink:      var(--text-primary);
    --cp-muted:    var(--text-secondary);
    --cp-line:     var(--border);
    --cp-ground:   var(--surface-muted);
    --cp-brand:    var(--brand-chrome);
    --cp-deep:     var(--brand-deep);
    --cp-figure:   var(--text-figure);
    --cp-rail:     var(--brand-surface);
    /* A green FIELD under white text: a filled button, the selected tab, a
       step marker. Not --cp-deep, which is green TEXT and turns light in
       dark mode so it can still be read on a dark page. */
    --cp-fill:     var(--brand-surface);
    --cp-rail-ink: var(--text-on-brand);
    --cp-active:   var(--wash-strong);
    --cp-active-2: var(--wash-soft);
    --cp-hover:    var(--surface-hover);
    --cp-warn-bg:  var(--amber-light);
    --cp-warn-ink: var(--amber-ink);
    --cp-warn-line:var(--amber-line);
    --cp-radius:   var(--radius);

    /* **The aging ramp: the only non-brand hues in the product, and
       they always mean one thing -- how late something is.** The bands
       they colour are AR's, not this stylesheet's: `aging_bucket`
       decides which band a document is in and these only paint it.
       Severity is never carried by colour alone, so every dot and
       every bar segment is accompanied by words. */
    /* **Named for AR's bucket keys, exactly.** A row sets
       `--row-age: var(--age-{{ bucket }})` from the key the domain
       gave it, so a second spelling here resolves to nothing and the
       tint silently does not paint -- which is what happened while
       these were `--age-1-30` and the keys were `days_1_30`. */
    --age-current:       var(--brand-chrome);
    --age-days_1_30:     #C08A2E;
    --age-days_31_60:    #B05C2A;
    --age-days_61_90:    #984423;
    --age-days_91_120:   #8E3520;
    --age-days_120_plus: #6E2415;

    --sans: 'Segoe UI', -apple-system, BlinkMacSystemFont, Arial, sans-serif;
    --mono: ui-monospace, 'Cascadia Mono', Consolas, 'Courier New', monospace;
}

* { box-sizing: border-box; }

/* **The one size lever for the whole portal.** Every size in this sheet
   is a `rem`, and a `rem` resolves against the ROOT, not the body, so
   the 14px that used to sit on `body` governed almost nothing: the
   hundred-odd `rem` rules below were all measuring against the browser's
   16px default. Raising the root scales type, padding and the rail
   together, in one place, instead of a hundred edits that drift apart.

   106.25% is 17px. `body` moves by the same factor, so text that
   inherits rather than declaring a size keeps its relationship to
   everything around it. Change this number, not the rules under it. */
html { font-size: 106.25%; }
body { margin: 0; background: var(--cp-ground); color: var(--cp-ink);
       font-family: var(--sans); font-size: .875rem; line-height: 1.5;
       -webkit-font-smoothing: antialiased; }
a { color: var(--cp-deep); }
h1, h2, h3 { margin: 0; letter-spacing: -.02em; }
:focus-visible { outline: 2px solid var(--cp-brand); outline-offset: 2px;
                 border-radius: 4px; }
.sr-only { position: absolute; width: 1px; height: 1px;
           overflow: hidden; clip: rect(0 0 0 0); }

/* A quiet label above a heading or a figure. Small, but set in the
   interface face at ordinary tracking -- widely letterspaced small caps
   read as a graphic device rather than as a label. */
.cp-label { font-size: .7rem; font-weight: 600; text-transform: uppercase;
            letter-spacing: .06em; color: var(--cp-muted); }
/* Money and counts, so columns line up digit for digit. */
.cp-num-t { font-variant-numeric: tabular-nums lining-nums; }
/* **A document code, in the one place a fixed width does a job.** An
   invoice number, a serial or an agreement id is read character by
   character and read back down a phone. It is not used for labels,
   dates or headings. */
.cp-code { font-family: var(--mono); font-size: .84rem; font-weight: 600; }

/* ==== FRAME ==== */

/* **`hidden` beats every layout rule in this sheet.** The attribute only
   carries the user agent's `display: none`, so any class setting a `display`
   of its own outranks it and the element renders anyway -- which is how an
   empty alert box appeared above the payment summary with nothing in it. */
[hidden] { display: none !important; }

.cp-app { display: flex; min-height: 100vh; }

/* The rail carries the same depth the sign-in page's brand half does,
   so the signed-out and signed-in surfaces read as one product.
   Painted as background layers rather than pseudo-elements: this is a
   flex column and a stacked child would need a z-index on every item. */
/* **The rail scrolls on its own.** `.cp-app` is a flex row, so the
   rail was a flex item stretching to whatever the page beside it
   happened to be: a long invoice list dragged the navigation down
   with it and the links left the screen. Pinned to the viewport and
   held to its own height, it stays put while the content moves, and
   `.cp-rail-scroll` below takes over if the links themselves ever
   outgrow the screen.

   `align-self: flex-start` is what stops the stretch; without it a
   flex item ignores its height and fills the row. Below 992px the
   drawer rule overrides all of this with `position: fixed`. */
.cp-rail { width: 16rem; flex-shrink: 0; position: sticky; top: 0;
           align-self: flex-start; height: 100vh; z-index: 2;
           display: flex; flex-direction: column;
           color: var(--cp-rail-ink);
           background-color: var(--cp-rail);
           background-image:
               radial-gradient(rgba(255,255,255,.07) 1.5px, transparent 1.5px),
               radial-gradient(circle at 14% 0%, rgba(255,255,255,.16), transparent 55%),
               linear-gradient(178deg, rgba(255,255,255,.05) 0%,
                               rgba(0,0,0,.04) 38%, rgba(0,0,0,.30) 100%);
           background-size: 26px 26px, auto, auto;
           box-shadow: 1px 0 0 rgba(0,0,0,.12), 10px 0 30px rgba(18,56,33,.10); }
.cp-rail-brand { padding: 1.5rem 1.35rem 1.3rem;
                 border-bottom: 1px solid rgba(255,255,255,.16); }
/* The company wordmark. One file: both grounds it sits on are the brand
   green, so the white mark serves each. Never inverted in CSS, which
   would misrepresent the mark. */
.cp-logo { display: block; width: 100%; height: auto; max-width: 8.5rem; }
.cp-brand-block { display: block; }
/* The site's name as a badge under the mark, not a line of fine
   print: it is the one thing on the rail that says which product a
   customer is looking at. */
.cp-rail-sub { display: inline-block; margin-top: .8rem;
               padding: .3rem .85rem; border-radius: 999px;
               font-size: .7rem; font-weight: 700;
               text-transform: uppercase; letter-spacing: .08em;
               color: #fff; background: rgba(255,255,255,.16);
               box-shadow: inset 0 0 0 1px rgba(255,255,255,.22); }

/* The rail scrolls, the identity block below it does not: who you are
   signed in as must not leave the screen on a short window. */
.cp-rail-scroll { flex: 1; overflow-y: auto; padding-bottom: .5rem; }
.cp-nav-group { padding: 1.1rem 1.35rem .45rem; font-size: .68rem;
                font-weight: 600; text-transform: uppercase;
                letter-spacing: .07em; color: rgba(255,255,255,.55); }
.cp-nav { display: flex; flex-direction: column; gap: .1rem; padding: 0 .75rem; }
.cp-nav a { position: relative; display: flex; align-items: center; gap: .7rem;
            padding: .55rem .75rem; border-radius: .55rem; text-decoration: none;
            color: rgba(255,255,255,.86); font-size: 1rem; font-weight: 500;
            transition: background .15s, color .15s; }
.cp-nav a:hover { background: rgba(255,255,255,.10); color: #fff; }
.cp-nav a.is-on { background: rgba(255,255,255,.16); color: #fff; font-weight: 600;
                  box-shadow: inset 0 1px 0 rgba(255,255,255,.20),
                              0 1px 2px rgba(0,0,0,.12); }
.cp-nav a.is-on::before { content: ""; position: absolute; left: -.75rem;
                          top: .3rem; bottom: .3rem; width: 3px;
                          border-radius: 0 3px 3px 0; background: #fff; }
.cp-nav i { font-size: 1.05rem; flex-shrink: 0; opacity: .9; }

/* A count riding on a nav row. **The word carries the meaning where
   there is one** -- `4 late` says what it is, and an amber `4` would
   not; colour is never the only thing saying a figure is bad. */
.cp-badge { margin-left: auto; font-size: .7rem; font-weight: 600;
            font-style: normal; padding: .05rem .42rem;
            border-radius: 999px; background: rgba(255,255,255,.18);
            white-space: nowrap; }
.cp-badge.is-warn { background: var(--cp-warn-bg);
                    color: var(--cp-warn-ink); }


/* ==== SIGNED IN AS ====
   Sign out is not a page action, so it does not belong in the masthead
   beside the customer's address. It belongs to the person, next to who
   the person is -- which is also the question the button silently
   answers: am I in the right account? */
.cp-user { padding: .85rem 1.1rem 1.1rem; background: rgba(0,0,0,.10);
           border-top: 1px solid rgba(255,255,255,.18); }
.cp-user-row { display: flex; align-items: center; gap: .6rem; min-width: 0; }
.cp-avatar { width: 2.1rem; height: 2.1rem; border-radius: 50%; flex-shrink: 0;
             display: flex; align-items: center; justify-content: center;
             font-size: .72rem; font-weight: 700; letter-spacing: .02em;
             background: rgba(255,255,255,.18);
             box-shadow: inset 0 0 0 1px rgba(255,255,255,.20); }
.cp-user-txt { min-width: 0; }
.cp-user-name { font-size: .84rem; font-weight: 600; color: #fff;
                white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cp-user-mail { font-size: .72rem; color: rgba(255,255,255,.62);
                white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cp-signout { display: block; width: 100%; margin-top: .7rem; padding: .45rem;
              border-radius: .5rem; font-family: inherit; font-size: .81rem;
              font-weight: 600; text-align: center; cursor: pointer; color: #fff;
              background: rgba(255,255,255,.10);
              border: 1px solid rgba(255,255,255,.24); transition: background .15s; }
.cp-signout:hover { background: rgba(255,255,255,.20); }

/* ==== THE DRAWER ====
   At desktop width the rail is simply a column and none of this
   applies. Below the breakpoint it becomes an off-canvas drawer over
   the page, opened by the burger and closed by the scrim, the close
   control, or by following any link in it. */
.cp-menu-toggle { position: absolute; }
.cp-burger, .cp-scrim, .cp-rail-close { display: none; }
.cp-burger { align-items: center; justify-content: center;
             width: 2.4rem; height: 2.4rem; margin: -.3rem 0 -.3rem -.35rem;
             border-radius: .55rem; cursor: pointer; font-size: 1.5rem;
             color: var(--cp-ink); }
.cp-burger:hover { background: var(--cp-hover); }
.cp-topbar-t { font-size: .9rem; }
.cp-rail-close { position: absolute; top: .9rem; right: .9rem;
                 width: 2.2rem; height: 2.2rem; border-radius: .5rem;
                 align-items: center; justify-content: center;
                 cursor: pointer; font-size: 1.4rem; color: #fff;
                 background: rgba(255,255,255,.12); }
/* The focus ring belongs on the control a person can see, not on the
   checkbox driving it. */
.cp-menu-toggle:focus-visible ~ .cp-app .cp-burger {
    outline: 2px solid var(--cp-brand); outline-offset: 2px; }

/* ==== MAIN ==== */
.cp-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.cp-topbar { display: none; align-items: center; gap: .5rem; padding: .6rem 1.1rem;
             background: var(--surface); border-bottom: 1px solid var(--cp-line); }

/* ==== MASTHEAD ====
   Who we have on file, in one band: one identity line (monogram, name,
   account number), one location line, and the people you would actually
   contact ranged right against a rule. The account number rides with
   the name because that is what it identifies -- it does not need a
   column and a label of its own. */
.cp-top { border-bottom: 1px solid var(--cp-line);
          padding: 1rem var(--cp-gutter);
          background: linear-gradient(90deg, var(--cp-hover) 0%, var(--surface) 38%);
          display: flex; align-items: center; gap: 2rem; flex-wrap: wrap; }
.cp-top-party { display: flex; align-items: center; gap: .85rem; min-width: 0; }
/* A monogram, not a logo: it stands for the CUSTOMER, so it is drawn
   from their name rather than shipped as an asset. Tinted rather than
   solid green -- a second block of brand green beside the rail was loud. */
.cp-mono { width: 2.75rem; height: 2.75rem; border-radius: .65rem; flex-shrink: 0;
           display: flex; align-items: center; justify-content: center;
           font-size: .92rem; font-weight: 700; letter-spacing: -.01em;
           color: var(--cp-deep); background: var(--cp-hover);
           box-shadow: inset 0 0 0 1px var(--cp-line); }
.cp-who { font-size: 1.2rem; font-weight: 700; letter-spacing: -.025em;
          line-height: 1.2; display: flex; align-items: center; gap: .6rem;
          flex-wrap: wrap; }
.cp-acct { font-family: var(--mono); font-size: .72rem; font-weight: 600;
           color: var(--cp-muted); background: var(--surface);
           border: 1px solid var(--cp-line); padding: .12rem .5rem;
           border-radius: 999px; white-space: nowrap; }
.cp-addr { margin-top: .2rem; font-size: .82rem; color: var(--cp-muted);
           display: flex; flex-wrap: wrap; align-items: center; gap: .1rem .5rem; }
.cp-addr a { color: inherit; text-decoration: none; }
.cp-addr a:hover { color: var(--cp-deep); text-decoration: underline; }
.cp-addr i { font-style: normal; opacity: .4; }
.cp-top-facts { margin-left: auto; display: flex; gap: 2.5rem; flex-wrap: wrap;
                padding-left: 2.25rem; border-left: 1px solid var(--cp-line); }
.cp-kv { font-size: .84rem; line-height: 1.45; }
.cp-kv b { display: block; font-size: .7rem; font-weight: 600;
           text-transform: uppercase; letter-spacing: .06em;
           color: var(--cp-muted); margin-bottom: .3rem; }
.cp-kv a { color: var(--cp-ink); text-decoration: none; }
.cp-kv a:hover { text-decoration: underline; }
.cp-kv-sub { color: var(--cp-muted); font-size: .79rem; }

/* Full width. Nothing is capped: the table, the panels and the aging bar
   all read better with the room, and a centred column inside a left rail
   is off-centre on a wide screen anyway. Only lines of prose are
   limited, per element, so they stay readable. */
/* **One gap between every block in the column.** Panels used to sit
   flush because each carried its own margin and a new one arrived
   without; a column that owns the spacing cannot have that happen. */
.cp-body { padding: 1.85rem var(--cp-gutter) 3rem; width: 100%;
           /* Grows, so the footer sits at the bottom of a short page rather
              than partway up it under a stripe of empty background. */
           flex: 1 0 auto;
           display: flex; flex-direction: column; gap: 1rem; }
@media (min-width: 1600px) {
    :root { --cp-gutter: 2.75rem; }
    .cp-body { padding-top: 2rem; padding-bottom: 3.5rem; }
}

/* ==== PAGE HEAD ==== */
.cp-page { display: flex; align-items: flex-end; justify-content: space-between;
           gap: 1.25rem; flex-wrap: wrap; margin-bottom: 0; }
.cp-page-title { font-size: 1.55rem; font-weight: 700; letter-spacing: -.03em;
                 line-height: 1.2; margin: .3rem 0 0; }
.cp-page-lede { margin: .3rem 0 0; font-size: .87rem; color: var(--cp-muted);
                max-width: 38rem; }

/* ==== PANELS ==== */
/* ==== INVOICE DOCUMENT ====
   The document view: the invoice on the left, how to settle it on the
   right. **Every colour is a token alias**, like everything above it.

   The pair collapses to one column below the wide breakpoint, and the
   side panel goes FIRST there: on a phone the balance and how to pay
   it are what somebody opened the page for, and the line detail is
   what they scroll to. */
/* **The two column split, used by more than the invoice.** A main
   column and a narrower side one: the invoice document beside how to
   settle it, the recent activity beside what to do next. It was named
   `.cp-doc` when the invoice was its only caller; the name says what the
   shape is now, so a second page does not grow a second grid.

   Below the breakpoint the side column goes FIRST: on a phone the
   balance, or the list of actions, is what somebody came for and the
   detail is what they scroll to. */
/* The lead row: the one thing on this card with a number on it, so it
   carries a tint and the figure sits hard right where money belongs. */
.cp-next .cp-next-lead { background: var(--cp-active-2); font-weight: 600; }
/* A module row is a destination and what is there, which gives it the
   same two line shape as an activity entry beside it, so the pair
   reads as one row rather than a panel with a stub next to it. */
.cp-next-main { display: flex; flex-direction: column; gap: .1rem;
                min-width: 0; flex: 1; }
.cp-next-t { flex: 1; min-width: 0; }
.cp-next-d { font-size: .76rem; color: var(--cp-muted);
             font-weight: 400; line-height: 1.4; }
.cp-next-amt { font-variant-numeric: tabular-nums; color: var(--cp-muted);
               font-weight: 600; }

.cp-split { display: grid; grid-template-columns: minmax(0,1fr) 20.5rem;
            gap: 1rem; align-items: start; }
.cp-split-side { position: sticky; top: 1rem; }
/* The provider's element lays its own fields in two columns -- expiry beside
   security code -- and the standard side column squeezes them into one. */
.cp-split-wide { grid-template-columns: minmax(0,1fr) 27rem; }
@media (max-width: 62rem) {
    .cp-split, .cp-split-wide { grid-template-columns: minmax(0,1fr); }
    .cp-split-side { position: static; order: -1; }
}

/* **The head is on the document's grid, not the page's.** As a full
   width flex row its two ends sat at the window edges with a void
   between them, and the state floated far from anything it described.
   Sharing `.cp-doc`'s columns puts the number over the document and the
   state directly above the balance panel, so the head reads as the top
   of those two columns rather than as a band of its own. */
/* The document's own head, inside the panel. It was a full width band
   above the grid: its ends sat at the window edges and the middle was
   void whatever went in it. Here the number and the state are close
   enough to read as one line about one document. */
.cp-doc-head { display: flex; align-items: flex-start;
               justify-content: space-between; gap: 1rem; flex-wrap: wrap;
               padding: 1.05rem 1.15rem .9rem;
               border-bottom: 1px solid var(--cp-line); }
.cp-doc-head h1 { margin: .1rem 0 .2rem; font-size: 1.6rem;
                  letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
.cp-doc-head p { margin: 0; color: var(--cp-muted); font-size: .84rem; }

.cp-panel { background: var(--surface); border: 1px solid var(--cp-line);
            border-radius: var(--cp-radius);
            box-shadow: 0 1px 2px rgba(18,56,33,.04); overflow: hidden; }
.cp-panel + .cp-panel { margin-top: 1rem; }
.cp-panel-head { padding: .9rem 1.15rem; border-bottom: 1px solid var(--cp-line); }
.cp-panel-head h2 { margin: 0; font-size: .95rem; }
.cp-panel-sub { color: var(--cp-muted); font-size: .78rem; margin-top: .15rem; }
.cp-panel-body { padding: 1.05rem 1.15rem; }
.cp-panel-foot { padding: .9rem 1.15rem; border-top: 1px solid var(--cp-line);
                 background: var(--cp-ground); font-size: .8rem; }

/* The facts strip. Wraps rather than scrolls: these are five short
   values and a horizontal scrollbar on them would be absurd. */
.cp-meta { display: flex; flex-wrap: wrap; gap: .9rem 2rem;
           padding: .95rem 1.15rem; border-bottom: 1px solid var(--cp-line); }
.cp-meta > div { display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
.cp-meta b { font-size: .64rem; font-weight: 700; letter-spacing: .07em;
             text-transform: uppercase; color: var(--cp-muted); }
.cp-meta span { font-size: .86rem; font-weight: 600; }
.cp-meta .code { font-family: var(--mono, ui-monospace, monospace);
                 font-size: .8rem; }

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

.cp-line-name { font-weight: 600; font-size: .9rem; }
.cp-line-note { color: var(--cp-muted); font-size: .78rem; margin-top: .15rem; }

/* What the charge already covers. Chips, because each is a name and a
   count and nothing else -- a four-column table would be heavier than
   the facts in it. */
.cp-incl { list-style: none; margin: .55rem 0 0; padding: 0;
           display: flex; flex-wrap: wrap; gap: .35rem; }
.cp-incl li { display: inline-flex; align-items: center; gap: .4rem;
              border: 1px solid var(--cp-line); border-radius: 999px;
              padding: .18rem .6rem; font-size: .76rem;
              background: var(--cp-ground); }
.cp-incl em { font-style: normal; font-weight: 700; font-size: .72rem;
              font-variant-numeric: tabular-nums; color: var(--cp-muted); }
.cp-incl-note { color: var(--cp-muted); font-size: .76rem; margin-top: .5rem; }

.cp-totals { display: flex; justify-content: flex-end;
             padding: .9rem 1.15rem; border-top: 1px solid var(--cp-line); }
.cp-totals-inner { width: min(22rem, 100%); }
.cp-totals-row { display: flex; justify-content: space-between;
                 gap: 2rem; padding: .3rem 0; font-size: .86rem;
                 font-variant-numeric: tabular-nums; }
.cp-totals-row.is-sum { border-top: 1px solid var(--cp-line);
                        margin-top: .3rem; padding-top: .5rem; font-weight: 600; }
.cp-totals-row.is-due { border-top: 2px solid var(--cp-ink);
                        margin-top: .35rem; padding-top: .55rem;
                        font-weight: 700; font-size: .98rem; }

/* Activity. Same feed shape the employee profile uses, in this shell's
   own palette: a mark, what happened, and when. */
.cp-feed { list-style: none; margin: 0; padding: 0; }
.cp-feed li { display: flex; gap: .8rem; padding: .8rem 1.15rem;
              border-bottom: 1px solid var(--cp-line); }
.cp-feed li:last-child { border-bottom: 0; }
.cp-feed-mark { flex: none; width: 1.9rem; height: 1.9rem;
                border-radius: .5rem; display: inline-flex;
                align-items: center; justify-content: center;
                background: var(--cp-ground); color: var(--cp-muted); }
.cp-feed-mark svg { width: 1rem; height: 1rem; }
.cp-feed-t { font-size: .85rem; font-weight: 600; }
.cp-feed-d { color: var(--cp-muted); font-size: .74rem; margin-top: .1rem;
             font-variant-numeric: tabular-nums; }

.cp-pay-fig { font-size: 1.75rem; font-weight: 700; letter-spacing: -.01em;
              color: var(--cp-figure);
              font-variant-numeric: tabular-nums; }

.cp-ways { list-style: none; margin: 0; padding: 0; }
.cp-ways li { padding: .85rem 1.15rem; border-top: 1px solid var(--cp-line); }
.cp-ways b { display: block; font-size: .82rem; margin-bottom: .2rem; }
.cp-ways p { margin: 0; color: var(--cp-muted); font-size: .78rem; line-height: 1.5; }

/* A control that cannot act yet is shown disabled with its reason
   beside it, never hidden and never live (Docs/ui-canon.md section 9). */
.cp-btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed;
                                pointer-events: none; }

/* A covered item under the charge it belongs to: indented, lighter, and
   without the rule that separates one charge from the next, so the
   group reads as one thing rather than as many equal rows. */
.cp-sub-line > td { border-top: 0; padding-top: .1rem; padding-bottom: .1rem; }
.cp-sub-line:first-of-type > td { padding-top: .45rem; }
.cp-line-sub { font-size: .82rem; padding-left: .9rem; position: relative; }
.cp-line-sub::before { content: ""; position: absolute; left: .15rem; top: .58em;
                       width: .35rem; height: 1px; background: var(--cp-line); }

.cp-letterhead { display: none; }

/* The terms sit opposite the totals, so the block reads as one row
   rather than as a figure floating against nothing. Below the narrow
   breakpoint they stack, terms first: on a phone the amount should be
   the last thing before the pay panel. */
.cp-totals { justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; }
.cp-terms { max-width: 22rem; font-size: .8rem; color: var(--cp-muted); }
.cp-terms b { display: block; color: var(--cp-ink); font-size: .78rem;
              margin-bottom: .15rem; }
.cp-terms p { margin: 0 0 .35rem; line-height: 1.5; }
.cp-btn-wide { display: block; width: 100%; text-align: center; }

/* The words take the band colour too, not only the dot. Colour is
   never the whole message here: the day count is always spelled out
   beside it, so the state survives a monochrome screen. */
.age-ink-current       { color: var(--age-current); }
.age-ink-days_1_30     { color: var(--age-days_1_30); }
.age-ink-days_31_60    { color: var(--age-days_31_60); }
.age-ink-days_61_90    { color: var(--age-days_61_90); }
.age-ink-days_91_120   { color: var(--age-days_91_120); }
.age-ink-days_120_plus { color: var(--age-days_120_plus); }
/* The document state, opposite the number. Right aligned so the head
   reads as one line of identity across the page rather than as a
   title with nothing beside it. */
.cp-head-state { display: flex; flex-direction: column;
                 align-items: flex-start; gap: .15rem;
                 padding-bottom: .1rem; }
.cp-state-big { display: inline-flex; align-items: center; gap: .45rem;
                font-size: 1.05rem; font-weight: 700; }
.cp-state-big .cp-dot { width: .6rem; height: .6rem; border-radius: 50%; }
.cp-state-sub { font-size: .8rem; color: var(--cp-muted); }
.cp-card { background: var(--surface); border: 1px solid var(--cp-line);
           border-radius: var(--cp-radius);
           box-shadow: 0 1px 2px rgba(18,56,33,.04); }
.cp-card-head { padding: .95rem 1.25rem; border-bottom: 1px solid var(--cp-line);
                display: flex; align-items: center; justify-content: space-between;
                gap: .9rem; flex-wrap: wrap; }
.cp-card-head h2, .cp-title { font-size: 1rem; font-weight: 700; margin: 0; }
.cp-card-body { padding: 1.15rem 1.25rem; }
.cp-card-foot { padding: .85rem 1.25rem; background: var(--surface-muted);
                border-top: 1px solid var(--cp-line); font-size: .83rem;
                display: flex; justify-content: space-between;
                align-items: baseline; gap: 1rem; flex-wrap: wrap; }
.cp-sub { font-size: .79rem; color: var(--cp-muted); margin-top: .1rem;
          font-weight: 400; line-height: 1.5; }
.cp-sub-gap { margin: 0 0 1rem; }
.cp-note { font-size: .84rem; color: var(--cp-muted); }
.cp-quiet { color: var(--cp-muted); }
.cp-grid, .cp-stats { display: grid; gap: 1rem; margin-bottom: 0;
             grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
/* Four tiles read as one row rather than three and a stray. A modifier on the
   shared grid, not a second grid: the floor below this width is still the
   17rem above, so the tiles wrap where they stop fitting exactly as every
   other row of them does. */
@media (min-width: 1280px) {
    .cp-grid.is-four { grid-template-columns: repeat(4, 1fr); }
}

/* ==== BUTTONS / TABS ==== */
.cp-btn { font-family: inherit; font-size: .83rem; font-weight: 600;
          padding: .5rem .9rem; border-radius: .5rem;
          border: 1px solid var(--cp-brand); background: var(--cp-brand);
          color: #fff; cursor: pointer; text-decoration: none;
          display: inline-block; transition: background .15s, border-color .15s; }
.cp-btn:hover { background: var(--cp-fill); border-color: var(--cp-fill); color: #fff; }
.cp-btn-quiet { background: var(--surface); color: var(--cp-ink);
                border-color: var(--cp-line); font-weight: 500; }
.cp-btn-quiet:hover { background: var(--cp-hover); color: var(--cp-ink); }
.cp-btn-sm { padding: .32rem .7rem; font-size: .78rem; }
.cp-btn-wide { width: 100%; margin-top: .9rem; }
.cp-tabs { display: flex; gap: .3rem; }
.cp-tab { font-family: inherit; font-size: .79rem; padding: .34rem .85rem;
          border-radius: 999px; text-decoration: none;
          border: 1px solid var(--cp-line); color: var(--cp-muted);
          background: var(--surface); white-space: nowrap; cursor: pointer; }
.cp-tab:hover { background: var(--cp-hover); color: var(--cp-ink); }
.cp-tab.is-on { background: var(--cp-fill); border-color: var(--cp-fill);
                color: #fff; font-weight: 600; }

/* ==== SIGNAL TILE ==== */
.cp-stat { display: flex; flex-direction: column; height: 100%;
           padding: 1.15rem 1.25rem 1rem; text-decoration: none; color: inherit; }
a.cp-card { text-decoration: none; color: inherit;
            transition: border-color .15s, box-shadow .15s, transform .15s; }
a.cp-card:hover { border-color: var(--border-hover);
                  box-shadow: 0 6px 20px -14px rgba(18,56,33,.5);
                  transform: translateY(-1px); }
.cp-stat-label { font-size: .7rem; font-weight: 600; text-transform: uppercase;
                 letter-spacing: .06em; color: var(--cp-muted); }
.cp-stat-figure { font-size: 2.1rem; font-weight: 700; line-height: 1.05;
                  letter-spacing: -.035em; color: var(--cp-figure);
                  margin-top: .5rem;
                  font-variant-numeric: tabular-nums lining-nums; }
.cp-stat-figure small { font-size: 1.35rem; font-weight: 600; }
.cp-stat-note { margin-top: .5rem; font-size: .81rem; color: var(--cp-muted);
                line-height: 1.5; }
.cp-stat-note b { color: var(--cp-ink); font-weight: 600; }
/* A figure whose domain could not be answered. It reads as a state, not
   as an error the customer caused, and it never replaces the number
   with a zero. */
.cp-stat-failed { color: var(--cp-warn-ink); font-weight: 600; }
.cp-stat-go { margin-top: auto; padding-top: .9rem; font-size: .8rem;
              font-weight: 600; color: var(--cp-deep); }
.cp-stat-go::after { content: " \2192"; }

/* ==== AGING BAR -- the signature ====
   **The bands are AR's**, classified by `aging_bucket`; these rules only
   paint them. Every segment carries its label and amount in the legend
   beneath, because severity must never be carried by colour alone. */
.cp-bar { display: flex; height: .8rem; gap: 2px; }
.cp-bar.is-mini { height: .4rem; margin-top: .85rem; }
.cp-bar span { display: block; border-radius: 2px; transition: filter .15s; }
.cp-bar span:first-child { border-radius: 999px 2px 2px 999px; }
.cp-bar span:last-child { border-radius: 2px 999px 999px 2px; }
.cp-bar span:hover { filter: brightness(1.12); }
.cp-legend { display: grid; gap: .75rem 1.25rem; margin-top: 1rem;
             grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr)); }
.cp-leg-key { display: flex; align-items: center; gap: .45rem;
              font-size: .76rem; color: var(--cp-muted); }
.cp-dot { width: .55rem; height: .55rem; border-radius: 2px; flex-shrink: 0; }
.cp-leg-amt { margin-top: .15rem; padding-left: 1rem; font-size: .95rem;
              font-weight: 700; font-variant-numeric: tabular-nums; }

/* The band colours, as classes rather than inline values. */
.age-current       { background: var(--age-current); }
.age-days_1_30     { background: var(--age-days_1_30); }
.age-days_31_60    { background: var(--age-days_31_60); }
.age-days_61_90    { background: var(--age-days_61_90); }
.age-days_91_120   { background: var(--age-days_91_120); }
.age-days_120_plus { background: var(--age-days_120_plus); }
.ink-current       { color: var(--age-current); }
.ink-days_1_30     { color: var(--age-days_1_30); }
.ink-days_31_60    { color: var(--age-days_31_60); }
.ink-days_61_90    { color: var(--age-days_61_90); }
.ink-days_91_120   { color: var(--age-days_91_120); }
.ink-days_120_plus { color: var(--age-days_120_plus); }

/* ==== STATEMENT ==== */
.cp-statement { background: var(--surface); border: 1px solid var(--cp-line);
                border-radius: var(--cp-radius); padding: 1.4rem 1.6rem 1.35rem;
                box-shadow: 0 1px 2px rgba(18,56,33,.04),
                            0 8px 24px -18px rgba(18,56,33,.35); }
.cp-statement-top { display: flex; justify-content: space-between; gap: 1rem;
                    flex-wrap: wrap; padding-bottom: .85rem;
                    border-bottom: 1px solid var(--cp-line); }
.cp-figure-row { display: flex; align-items: flex-end; gap: 2.5rem;
                 flex-wrap: wrap; margin: 1.05rem 0 1.35rem; }
.cp-figure { font-size: 2.8rem; font-weight: 700; line-height: 1;
             letter-spacing: -.04em; color: var(--cp-figure);
             font-variant-numeric: tabular-nums lining-nums; }
.cp-figure small { font-size: 1.7rem; font-weight: 600; }
.cp-figure-label { margin-top: .45rem; font-size: .82rem; color: var(--cp-muted); }
.cp-figure-side { padding-bottom: .15rem; font-size: .88rem;
                  color: var(--cp-muted); max-width: 24rem; }
.cp-figure-side b { color: var(--cp-ink); font-weight: 600; }

/* ==== TABLE ==== */
.cp-wrap { width: 100%; }
.cp-table { width: 100%; border-collapse: collapse; }
.cp-table th { font-size: .7rem; font-weight: 600; text-transform: uppercase;
               letter-spacing: .06em; color: var(--cp-muted); text-align: left;
               padding: .7rem 1.25rem; border-bottom: 1px solid var(--cp-line);
               background: var(--surface-muted); white-space: nowrap; }
.cp-table td { padding: .85rem 1.25rem; font-size: .87rem;
               border-bottom: 1px solid var(--border); vertical-align: middle; }
.cp-table tbody tr:last-child td { border-bottom: none; }
.cp-table tbody tr { transition: background .12s; }
.cp-table tbody tr:hover { background: var(--cp-hover); }
.cp-table th.cp-num, .cp-table td.cp-num { text-align: right;
               font-variant-numeric: tabular-nums; width: 1%; white-space: nowrap; }
.cp-table th:first-child, .cp-table td:first-child,
.cp-table th:last-child, .cp-table td:last-child { width: 1%; white-space: nowrap; }
.cp-flex { width: auto; }
.cp-ref { font-family: var(--mono); font-size: .84rem; font-weight: 600;
          color: var(--cp-deep); text-decoration: none;
          border-bottom: 1px solid transparent; }
.cp-ref:hover { border-bottom-color: currentColor; }
.cp-balance { font-weight: 700; }
/* How late one row is: a coloured dot AND the day count in words. */
.cp-age { display: inline-flex; align-items: center; gap: .4rem;
          font-size: .78rem; font-weight: 600; white-space: nowrap; }
.cp-age .cp-dot { border-radius: 50%; }
/* The left edge of a late row, tinted by its band. Reinforcement only:
   the Age column already says it in words. */
tr.is-late td:first-child { box-shadow: inset 4px 0 0 var(--row-age); }
.cp-pill { display: inline-block; font-size: .74rem; font-weight: 600;
           padding: .12rem .55rem; border-radius: 999px;
           background: var(--cp-hover); border: 1px solid var(--cp-line);
           color: var(--cp-ink); }
/* On, for the one place a pill carries a live state rather than a label. */
.cp-pill.is-on { background: var(--brand-light); border-color: var(--border-hover);
                 color: var(--cp-deep); }

/* ==== LIST ROWS ==== */
.cp-list { list-style: none; margin: 0; padding: 0; }
.cp-list li { padding: 1rem 1.25rem; border-bottom: 1px solid var(--border);
              display: flex; gap: 1.25rem; align-items: flex-start;
              flex-wrap: wrap; }
.cp-list li:last-child { border-bottom: none; }
.cp-list-main { min-width: 14rem; flex: 1; }
.cp-list-name { font-weight: 600; font-size: .92rem; letter-spacing: -.01em; }
.cp-list-meta { margin-top: .25rem; font-size: .79rem; color: var(--cp-muted);
                display: flex; gap: .2rem 1rem; flex-wrap: wrap; }
.cp-list-meta code { font-family: var(--mono); font-size: .75rem; font-weight: 600; }
.cp-list-side { text-align: right; margin-left: auto; }

/* ==== ACTIVITY ====
   Composed from the customer's own documents, requests, agreements and
   installs. Never from the audit trail, which is our record of who
   looked at what. */
/* Named for screen readers, invisible to everyone else: the
   stretched link needs text of its own or it announces as an
   empty link. */
.visually-hidden { position: absolute; width: 1px; height: 1px;
                   overflow: hidden; clip: rect(0 0 0 0);
                   white-space: nowrap; }
.cp-act { list-style: none; margin: 0; padding: 0; }
.cp-act li { display: flex; gap: .85rem; padding: .85rem 1.25rem;
             border-bottom: 1px solid var(--border); align-items: flex-start; }
.cp-act li:last-child { border-bottom: none; }
/* The whole row is the target, not just the title: a stretched link
   over the row keeps one hit area and leaves the icon, the words and
   the date as ordinary text underneath, so nothing inside has to
   become an anchor to be clickable. The row needs `position:
   relative` for the stretch to bound to it. */
.cp-act li.is-link { position: relative; }
.cp-act li.is-link:hover { background: var(--cp-hover); }
.cp-act-hit { position: absolute; inset: 0; z-index: 1; }
.cp-act li.is-link:focus-within { outline: 2px solid var(--cp-deep);
                                  outline-offset: -2px; }
.cp-act-icon { width: 1.9rem; height: 1.9rem; border-radius: .5rem; flex-shrink: 0;
               display: flex; align-items: center; justify-content: center;
               background: var(--cp-hover); color: var(--cp-deep);
               font-size: 1rem; }
.cp-act-main { min-width: 0; }
.cp-act-t { font-size: .87rem; font-weight: 600; }
.cp-act-d { font-size: .76rem; color: var(--cp-muted); margin-top: .15rem; }
.cp-act-when { margin-left: auto; font-size: .78rem; color: var(--cp-muted);
               white-space: nowrap; font-variant-numeric: tabular-nums;
               padding-left: .75rem; }

/* ==== DO NEXT ====
   **Only actions that work.** Every row links to a page this portal
   actually serves; there is no row for a flow that does not exist,
   because a control that refuses teaches a reader to stop trusting
   the controls. */
.cp-next { list-style: none; margin: 0; padding: .5rem; }
.cp-next a { display: flex; align-items: center; gap: .7rem;
             padding: .6rem .75rem; border-radius: .5rem; text-decoration: none;
             color: var(--cp-ink); font-size: .87rem; font-weight: 500; }
.cp-next a:hover { background: var(--cp-hover); }
.cp-next i { font-size: 1.05rem; color: var(--cp-deep); flex-shrink: 0; }

/* ==== EMPTY / FAILED ====
   **They must look different.** A panel rendering empty because a query
   failed says there is nothing, which is a lie nobody investigates. */
.cp-empty { padding: 3rem 1.5rem; text-align: center; }
.cp-empty-icon { font-size: 1.9rem; display: block; margin-bottom: .6rem;
                 opacity: .45; }
/* A failed load and an empty result look different on purpose (ui-canon 5). */
.cp-empty-icon.is-failed { font-size: 1.7rem; margin-bottom: .5rem; color: var(--cp-warn-ink);
                          opacity: 1; }
.cp-empty-icon.is-clear { color: var(--cp-deep); opacity: .6; }
.cp-empty-text { font-weight: 600; font-size: 1rem; }
.cp-empty .cp-sub { max-width: 26rem; margin: .45rem auto 0; }
.cp-alert { display: flex; gap: .55rem; align-items: flex-start; font-size: .82rem;
            line-height: 1.45; padding: .65rem .8rem; border-radius: .55rem;
            background: var(--cp-warn-bg); border: 1px solid var(--cp-warn-line);
            color: var(--cp-warn-ink); }
/* Good news is not a warning: a received payment reads in the brand's quiet
   green, the same pair the "on" pill uses. The words carry the meaning. */
.cp-alert.is-ok { background: var(--brand-light); border-color: var(--border-hover);
                  color: var(--cp-ink); }
/* Waiting on Bridge for a payment's outcome: neutral, not a warning, with a
   small spinner. The words say what is happening; the motion only says it is
   still happening, and stops for a reader who asked for less motion. */
.cp-alert.cp-confirming { background: var(--surface); border-color: var(--cp-line);
                          color: var(--cp-ink); align-items: center; }
.cp-alert[hidden] { display: none; }
.cp-spinner { width: 1rem; height: 1rem; flex-shrink: 0; border-radius: 50%;
              border: 2px solid var(--cp-line);
              border-top-color: var(--cp-brand);
              animation: cp-spin .8s linear infinite; }
@keyframes cp-spin { to { transform: rotate(360deg); } }
/* A payment being confirmed (portal.js, cpPaymentBusy): the Pay button greys
   and carries the spinner, and the fields fade and take no input. */
.cp-btn.is-busy, .cp-btn.is-busy:hover {
    background: var(--cp-muted); border-color: var(--cp-muted); color: #fff;
    cursor: progress; display: flex; align-items: center;
    justify-content: center; gap: .5rem; }
.cp-btn.is-busy .cp-spinner { border-color: rgba(255,255,255,.4);
                              border-top-color: #fff; }
.cp-paying { opacity: .5; pointer-events: none; transition: opacity .15s; }
@media (prefers-reduced-motion: reduce) {
    .cp-spinner { animation-duration: 2.4s; }
}

/* ==== FORMS (the signed-out screens) ==== */
.cp-form { padding: 1.15rem; }
.cp-form-top { margin-top: .6rem; }
.cp-narrow { max-width: 34rem; margin: 0 auto; }
.cp-input { width: 100%; font-family: inherit; font-size: .95rem;
            padding: .58rem .75rem; border: 1px solid var(--cp-line);
            border-radius: .55rem; background: var(--surface); color: var(--cp-ink); }
.cp-input:focus { outline: 2px solid var(--cp-brand); outline-offset: 1px;
                  border-color: var(--cp-brand); }
.cp-code-input { letter-spacing: .5em; font-variant-numeric: tabular-nums;
                 text-align: center; font-size: 1.3rem; }

/* ==== EMPLOYEE PREVIEW BANNER ====
   Not dismissible while the view is open, and it says so in words
   rather than by colour. */
.cp-imp { display: flex; align-items: center; justify-content: space-between;
          gap: 1rem; flex-wrap: wrap; padding: .7rem 2rem;
          background: var(--cp-warn-bg);
          border-bottom: 1px solid var(--cp-warn-line); }
.cp-imp-t { font-size: .82rem; font-weight: 700; color: var(--cp-warn-ink); }
.cp-imp-s { font-size: .74rem; color: var(--cp-warn-ink); opacity: .85; }
.cp-imp-btn { font-family: inherit; font-size: .76rem; font-weight: 600;
              padding: .32rem .75rem; border-radius: .45rem; cursor: pointer;
              color: #fff; background: var(--cp-warn-ink);
              border: 1px solid var(--cp-warn-ink); }

/* ==== SIGNED OUT ====
   The split screen: the mark on the green, the one thing you can do on
   the light side. Its own scoped rules, not Bridge's -- this shell
   loads `tokens.css` and nothing else. */
.cp-solo { min-height: 100vh; display: flex; }
.cp-solo-brand { flex: 1 1 0; position: relative; display: flex;
                 align-items: center; justify-content: center;
                 padding: 3rem 2.5rem; background: var(--cp-rail);
                 overflow: hidden; }
.cp-solo-brand::before { content: ""; position: absolute; inset: 0;
    pointer-events: none;
    background: radial-gradient(circle at 22% 18%, rgba(255,255,255,.16), transparent 55%),
                radial-gradient(circle at 82% 88%, rgba(0,0,0,.14), transparent 55%); }
.cp-solo-brand::after { content: ""; position: absolute; inset: 0;
    pointer-events: none; opacity: .35;
    background-image: radial-gradient(rgba(255,255,255,.22) 1.5px, transparent 1.5px);
    background-size: 26px 26px; }
.cp-solo-brand-inner { position: relative; z-index: 1; width: 100%;
                       max-width: 26rem; text-align: center; }
.cp-solo-brand .cp-logo { max-width: 20rem; margin: 0 auto; }
.cp-solo-tagline { margin: 1.75rem 0 0; color: var(--cp-rail-ink);
                   font-size: .95rem; line-height: 1.5; }
.cp-solo-panel { flex: 1 1 0; display: flex; align-items: center;
                 justify-content: center; padding: 3rem 1.5rem;
                 background: var(--cp-ground); }
.cp-solo-inner { width: 100%; max-width: 26rem; }
.cp-solo-foot { text-align: center; margin-top: 1.1rem; }
.cp-solo-contact { margin-top: .2rem; }
.cp-solo-contact a { color: inherit; text-decoration: none; }
.cp-solo-contact a:hover { text-decoration: underline; }
.cp-solo-sep { margin: 0 .4rem; opacity: .5; }

/* ==== ACCOUNT FACT CARDS (several accounts) ==== */
/* ONE CARD PER ACCOUNT, on a page of its own once a customer reaches more
   than two. Three columns each listing every account made a reader match the
   same numbers across columns by eye, and at ten accounts it ran taller than
   the invoices underneath. */
.cp-facts-grid { display: grid; gap: .6rem;
                 grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
.cp-facts-page { margin-bottom: 1.25rem; }

.cp-fact { background: var(--surface-muted); border: 1px solid var(--cp-line);
           border-radius: var(--cp-radius); padding: .7rem .85rem; }
.cp-fact-title { display: flex; align-items: baseline; gap: .45rem;
                 flex-wrap: wrap; margin-bottom: .5rem;
                 padding-bottom: .4rem;
                 border-bottom: 1px solid var(--cp-line); }
.cp-fact-row { display: grid; gap: .1rem; margin-top: .5rem; }
.cp-fact-icon { font-size: .95rem; flex-shrink: 0; color: var(--cp-deep); }
.cp-fact-label { font-size: .68rem; font-weight: 600; text-transform: uppercase;
                 letter-spacing: .06em; color: var(--cp-muted); }
.cp-fact-name { font-weight: 600; }
.cp-fact-body { font-size: .82rem; line-height: 1.45; }
.cp-fact-none { font-size: .8rem; color: var(--cp-muted); }
.cp-fact-who { font-size: .7rem; color: var(--cp-muted); }

/* ==== MOBILE CARDS ==== */
.cp-cards { display: none; }
.cp-row { padding: .85rem 1.15rem; border-bottom: 1px solid var(--border); }
.cp-row:last-child { border-bottom: none; }
.cp-row-top { display: flex; justify-content: space-between;
              align-items: baseline; gap: .5rem; }
.cp-row-sub { font-size: .78rem; color: var(--cp-muted); margin-top: .3rem;
              display: flex; flex-wrap: wrap; gap: .15rem .75rem; }

@media (max-width: 991.98px) {
    /* The rail becomes the drawer. It is not hidden and rebuilt: the
       same element, the same links, moved off screen until asked for. */
    .cp-rail { position: fixed; top: 0; bottom: 0; left: 0; z-index: 60;
               width: 17rem; max-width: 84vw; transform: translateX(-100%);
               height: auto; align-self: auto;
               transition: transform .22s ease; }
    .cp-menu-toggle:checked ~ .cp-app .cp-rail { transform: none; }
    .cp-scrim { display: block; position: fixed; inset: 0; z-index: 55;
                background: rgba(10,26,15,.45); opacity: 0;
                pointer-events: none; transition: opacity .22s ease; }
    .cp-menu-toggle:checked ~ .cp-app .cp-scrim { opacity: 1;
                pointer-events: auto; }
    .cp-burger, .cp-rail-close { display: flex; }
    .cp-topbar { display: flex; position: sticky; top: 0; z-index: 40; }
    .cp-position-relative { position: relative; }
    :root { --cp-gutter: 1.1rem; }
    .cp-body { padding-top: 1.1rem; padding-bottom: 2.5rem; }
    .cp-top { padding-top: .9rem; padding-bottom: .9rem;
              gap: 1rem; background: var(--surface); }
    .cp-top-facts { margin-left: 0; padding-left: 0; border-left: none;
                    gap: 1.75rem; padding-top: .85rem;
                    border-top: 1px solid var(--cp-line); width: 100%; }
    .cp-mono { width: 2.4rem; height: 2.4rem; }
    .cp-statement { padding: 1.2rem 1.1rem; }
    .cp-figure { font-size: 2.3rem; }
    .cp-imp { padding: .7rem 1.1rem; }
}
@media (max-width: 767.98px) {
    /* Tables become stacked cards. A phone must never depend on
       sideways scrolling to read a row. */
    .cp-wrap { display: none; }
    .cp-cards { display: block; }
    .cp-page-title { font-size: 1.35rem; }
    .cp-list-side { margin-left: 0; text-align: left; }
    .cp-stat-figure { font-size: 1.7rem; }
}
@media (prefers-reduced-motion: reduce) {
    * { transition: none !important; animation: none !important; }
}
/* ==== PRINT ====
   **One block for the whole shell.** What comes out is the document, not
   the application around it: the rail, the tabs and the page's own
   navigation are chrome and go, and so does the pay panel, which is an
   action nobody can take on paper. The activity feed carries
   `cp-no-print` because a printed invoice is the document, not our
   record of what has happened to it. The letterhead is the reverse: on
   screen the shell says who we are in every header, and on paper the
   page has to identify itself. */
@media print {
    .cp-rail, .cp-topbar, .cp-scrim, .cp-tabs, .cp-imp, .cp-top,
    .cp-nav, .cp-back, .cp-split-side, .cp-no-print { display: none !important; }
    body { background: #fff; }
    .cp-card, .cp-statement { box-shadow: none; }
    .cp-split { grid-template-columns: 1fr !important; }
    /* No card on paper. A printed invoice is a sheet, not a boxed
       component, so the panel loses its border, its corners and its
       shadow and the rules inside it do the separating. */
    .cp-panel { border: 0 !important; border-radius: 0 !important;
                box-shadow: none !important; }

    /* **The screen layout is what produced a blank first page.** `.cp-app`
       is a flex row with `min-height: 100vh` so the rail and the content
       stand full height together; on paper that reserves a viewport
       before anything is drawn, and the invoice starts on sheet two.
       Flattened to ordinary blocks with no forced height, the document
       begins where the letterhead ends.

       `break-inside: avoid` is gone from the panel for the same family of
       reason: a panel taller than a sheet cannot honour it, and asking
       pushes the whole block to the next page rather than splitting it. */
    .cp-app { display: block !important; min-height: 0 !important; }
    .cp-main { display: block !important; }
    .cp-body { padding: 0 !important; }
    .cp-panel + .cp-panel { margin-top: 0 !important; }

    /* The totals stay right whatever the width. The terms and the
       figures share a row on screen; in the narrower print column the
       row wraps, and without this the figures land under the terms on
       the left, which is not where anybody looks for a total. */
    .cp-totals { flex-wrap: nowrap !important; align-items: flex-start; }
    .cp-totals-inner { margin-left: auto; flex: none; }
    .cp-terms { max-width: 55%; }
    a[href]::after { content: ""; }

    .cp-letterhead { display: flex; align-items: flex-start;
                     justify-content: space-between; gap: 1rem;
                     padding-bottom: .6rem; margin-bottom: 1rem;
                     border-bottom: 1px solid #bbb; }
    .cp-letterhead img { max-width: 11rem; height: auto; }
    .cp-letterhead-info { text-align: right; font-size: .8rem;
                          display: flex; flex-direction: column; }
    .cp-letterhead-info b { font-size: .85rem; }
    /* A space above the rep, so the two facts about us and the one
       about them do not read as a single list. */
    .cp-letterhead-rep { margin-top: .35rem; }

    /* A receipt is one sheet: the help footer says what the receipt already
       says, so on paper it is left off. Scoped to the receipt, so a printed
       invoice is unchanged. */
    body:has(.cp-receipt) .cp-foot { display: none !important; }

    /* **A scrolling table has nothing to scroll on paper.** `.cp-scroll-x`
       clips a wide table to the screen viewport and lets the reader drag
       the rest into view; a printed sheet cannot take that drag, so the
       clipped columns (Rate, Overage on the meter table) never made it
       onto the page at all. Paper gets the whole table instead, shrunk to
       fit rather than cut. */
    .cp-scroll-x { overflow-x: visible; }
    .cp-table th, .cp-table td { padding: .3rem .4rem; font-size: .68rem; }
    .cp-table th:first-child, .cp-table td:first-child,
    .cp-table th:last-child, .cp-table td:last-child { white-space: normal; }
}
    

/* ==== SERVICE AT A GLANCE ====
   The card above the request list: what is open now, what the last year looked
   like, and which machine the requests came from.

   **Counting only.** Every figure is a count or a date over the customer's own
   requests. Nothing here groups faults by their wording or recommends
   anything: telling somebody their copier is the problem is a claim this
   application has no basis for. */
.cp-page-action { text-align: right; }
.cp-page-action-note { font-size: .74rem; color: var(--cp-muted);
                       margin-top: .35rem; max-width: 15rem; }

.cp-glance { background: var(--surface); border: 1px solid var(--cp-line);
             border-radius: var(--cp-radius); box-shadow: 0 1px 2px rgba(18,56,33,.04);
             padding: 1.15rem 1.25rem 1.25rem; margin-bottom: 1rem; }
.cp-glance-head { display: flex; align-items: baseline;
                  justify-content: space-between; gap: 1rem; flex-wrap: wrap;
                  padding-bottom: .9rem; border-bottom: 1px solid var(--cp-line);
                  margin-bottom: 1.1rem; }
.cp-glance-top { display: flex; gap: 2.5rem; flex-wrap: wrap;
                 align-items: flex-start; margin-bottom: 1.1rem; }
.cp-glance-state { min-width: 14rem; }
.cp-glance-state h2 { margin: 0; font-size: 1.5rem; letter-spacing: -.02em;
                      display: inline-flex; align-items: center; gap: .55rem; }
.cp-glance-state h2 .cp-dot { width: .6rem; height: .6rem; border-radius: 50%; }
.cp-glance-state p { margin: .35rem 0 0; color: var(--cp-muted); font-size: .82rem; }
/* **Prose gets a measure, not the whole row.** `flex: 1` alone stretched this
   to the full card width on a wide screen, so a two-line paragraph became one
   line running the width of the page and read as a banner rather than as
   something to read. 34rem is about 70 characters at this size. */
.cp-glance-facts { flex: 1; min-width: 16rem; max-width: 34rem;
                   font-size: .88rem;
                   line-height: 1.6; color: var(--cp-muted); }
.cp-glance-facts b { color: var(--cp-ink); }

/* One bar, one segment per machine, widths in proportion. The legend under it
   names every segment, so the bar is never the only place a figure appears. */
.cp-glance-bar { display: flex; height: .55rem; border-radius: 999px;
                 overflow: hidden; gap: 2px; margin-bottom: .9rem; }
.cp-glance-bar span { display: block; min-width: 2px; }
.cp-glance-keys { display: grid; gap: 1rem 2rem;
                  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); }
.cp-glance-key { min-width: 0; }
/* The swatch sits on the FIRST line of the label, not centred against the
   block. A model description wraps to two or three lines and a centred square
   then floats beside the middle of the text, pointing at nothing. */
.cp-glance-key-name { display: flex; align-items: flex-start; gap: .45rem;
                      font-size: .78rem; color: var(--cp-muted);
                      overflow-wrap: anywhere; }
.cp-glance-key-name i { width: .55rem; height: .55rem; border-radius: 2px;
                        flex: none; margin-top: .35em; }
.cp-glance-key-n { font-weight: 700; font-size: .95rem; margin-top: .15rem; }

/* ==== LIST CONTROLS ====
   The row above a list: one search box, the selects the domain declared, and
   the pager under the rows. **One implementation for every customer list**, so
   Invoices, Service requests, Equipment and Agreements wear the same control
   and a reader learns it once. */
.cp-filters { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center;
              padding: .85rem 1.15rem; border-bottom: 1px solid var(--cp-line); }
.cp-search { flex: 1 1 15rem; max-width: 22rem; min-width: 0;
             padding: .5rem .7rem; font: inherit; font-size: .85rem;
             color: var(--cp-ink); background: var(--cp-bg);
             border: 1px solid var(--cp-line); border-radius: .45rem; }
/* **Capped, because a select sizes itself to its longest option.** One machine
   description is sixty characters, which made the control wider than the search
   box beside it and pushed the rest of the row off the card. */
.cp-select { max-width: 14rem; text-overflow: ellipsis;
             padding: .5rem 1.9rem .5rem .7rem; font: inherit; font-size: .85rem;
             color: var(--cp-ink); background: var(--cp-bg);
             border: 1px solid var(--cp-line); border-radius: .45rem;
             /* The native arrow is replaced so the control matches the search
                box on every platform rather than only on the author's. */
             appearance: none; -webkit-appearance: none;
             background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                               linear-gradient(135deg, currentColor 50%, transparent 50%);
             background-position: calc(100% - 1.05rem) 55%, calc(100% - .75rem) 55%;
             background-size: .3rem .3rem, .3rem .3rem;
             background-repeat: no-repeat; }
.cp-search:focus, .cp-select:focus { outline: none; border-color: var(--cp-deep);
                                     box-shadow: 0 0 0 3px rgba(46, 124, 70, .16); }
.cp-filter-clear { font-size: .82rem; color: var(--cp-muted);
                   text-decoration: underline; }
.cp-filter-count { padding: .6rem 1.15rem 0; font-size: .8rem;
                   color: var(--cp-muted); }
.cp-pager { display: flex; gap: 1rem; align-items: center;
            padding: .8rem 1.15rem; border-top: 1px solid var(--cp-line);
            font-size: .82rem; color: var(--cp-muted); }
.cp-pager a { color: var(--cp-deep); font-weight: 600; }
/* A band heading inside a list. It names the group and counts its own rows, so
   a reader scrolling a long history always knows which year they are in. */
.cp-band td, .cp-band th { background: var(--cp-ground);
                           padding: .55rem 1.15rem; font-size: .78rem;
                           font-weight: 700; letter-spacing: .04em;
                           text-transform: uppercase; color: var(--cp-ink);
                           border-top: 1px solid var(--cp-line); }
.cp-band span { margin-left: .6rem; font-weight: 500; letter-spacing: 0;
                text-transform: none; color: var(--cp-muted); }
@media (max-width: 575.98px) {
    .cp-search, .cp-select { flex: 1 1 100%; max-width: none; }
}

/* ==== SERVICE REQUEST DETAIL ====
   A customer facing service record: what was reported, what happened, and the
   machine and agreement beside it. It borrows the invoice document's parts
   (`.cp-split`, `.cp-panel`, `.cp-meta`, `.cp-feed`) rather than growing its
   own, so the two records read as one product. */
.cp-req-banner { display: flex; gap: .85rem; align-items: flex-start;
                 padding: .95rem 1.15rem; border-bottom: 1px solid var(--cp-line);
                 font-size: .86rem; }
.cp-req-banner b { display: block; font-size: .95rem; }
.cp-req-banner div { color: var(--cp-muted); }
.cp-req-banner.is-closed { background: var(--cp-active-2); }
.cp-req-banner.is-closed b { color: var(--cp-deep); }
.cp-req-mark { flex: none; width: 2rem; height: 2rem; border-radius: 50%;
               display: inline-flex; align-items: center; justify-content: center;
               background: var(--cp-fill); color: #fff; }
.cp-req-mark svg { width: 1.05rem; height: 1.05rem; }
.cp-req-banner.is-open .cp-req-mark { background: var(--age-days_1_30); }

.cp-req-section { padding: 1.05rem 1.15rem .2rem; }
.cp-req-section h2 { margin: 0; font-size: .95rem; }
.cp-req-section .cp-panel-sub { margin-bottom: .55rem; }

/* What the customer told us, set apart from our own words about it. */
.cp-req-quote { margin: .55rem 0 .9rem; padding: .75rem .9rem;
                background: var(--cp-ground); border-radius: .5rem;
                font-size: .88rem; line-height: 1.55; }

.cp-req-machine { font-weight: 600; font-size: .9rem; margin-bottom: .35rem; }

/* The timeline mark is a square on this page: it is a step in a sequence, not
   an item in a feed, and the shape says so without a second colour. */
.cp-feed-mark.is-step { border-radius: .4rem; background: var(--cp-fill);
                        color: #fff; }
.cp-feed-mark.is-step svg { width: .95rem; height: .95rem; }

/* The cover figure reads as one number out of another, so the total is set
   quieter than the count rather than the same size. */
.cp-cover-n { font-size: 2.4rem; letter-spacing: -.03em; color: var(--cp-figure); }
.cp-cover-of { font-size: 1.15rem; font-weight: 600; color: var(--cp-muted); }

/* ==== PAYMENT METHOD CHOICE ====
   One row per card, plus one for a new one. It borrows the panel's own type
   and rule colours rather than introducing a second card component: a saved
   method is a choice inside a form, not an object with a surface of its own. */
.cp-choices { display: flex; flex-direction: column; gap: .4rem;
              margin-top: .45rem; }
.cp-choice { display: flex; align-items: flex-start; gap: .65rem;
             padding: .7rem .85rem; border: 1px solid var(--border);
             border-radius: 6px; cursor: pointer; }
.cp-choice:hover { border-color: var(--cp-figure); }
/* The whole row reads as selected, not just the dot: a customer scanning a
   list of near-identical card labels needs the answer at a glance. */
.cp-choice:has(input:checked) { border-color: var(--cp-figure);
                                background: var(--surface-muted); }
.cp-choice input { margin-top: .2rem; flex: none; }
.cp-choice-txt { display: flex; flex-direction: column; gap: .1rem; }
.cp-choice-label { font-weight: 600; font-size: .92rem; }

/* An opt-in, so it is a plain checkbox beside its sentence and never a switch:
   a switch reads as a setting that persists, and this decides one payment. */
.cp-check { display: flex; align-items: flex-start; gap: .55rem;
            font-size: .85rem; cursor: pointer; }
.cp-check input { margin-top: .15rem; flex: none; }

/* An agreement that a saved card is committed to. Named in words beside the
   card, so a removal refusal is understandable before it happens. */
.cp-method-used { margin-top: .3rem; font-size: .79rem; color: var(--cp-muted); }

/* ==== FOOTER ====
   Signed in there was no footer at all, so the portal stated no policy on the
   pages where a customer saves a card. Support on the left, policy on the
   right; it wraps to two rows on a narrow screen rather than shrinking. */
/* **`.cp-body` owns the gap above this rule**, the same way it owns the gap
   between its own blocks. A margin here as well stacked two separations into
   one and left a band of empty page under every short surface. */
.cp-foot { border-top: 1px solid var(--border);
           padding: 1rem var(--cp-gutter) 1.6rem; }
.cp-foot-inner { display: flex; flex-wrap: wrap; gap: .5rem 1.5rem;
                 align-items: baseline; justify-content: space-between;
                 font-size: .79rem; color: var(--cp-muted); }
.cp-foot-legal { display: flex; gap: 1.1rem; }
.cp-foot a { color: var(--cp-muted); text-decoration: underline; }
.cp-foot a:hover { color: var(--cp-figure); }
@media (max-width: 640px) {
    .cp-foot-inner { flex-direction: column; gap: .5rem; }
}

/* ==== SAVED PAYMENT METHODS ====
   A saved card is a thing you recognise, not a row of data: the label leads,
   the detail sits under it, and the controls stay on the right where every
   other row on this surface puts them. */
.cp-methods { list-style: none; margin: 0; padding: 0; }
.cp-method { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
             padding: .8rem 0; border-bottom: 1px solid var(--border); }
.cp-method:first-child { padding-top: 0; }
.cp-method:last-child { border-bottom: 0; padding-bottom: 0; }
.cp-method-id { display: flex; flex-direction: column; gap: .15rem;
                min-width: 0; flex: 1 1 14rem; }
.cp-method-label { font-weight: 600; font-size: .92rem; }
.cp-method-used { font-size: .78rem; color: var(--cp-muted); }
.cp-method-actions { display: flex; gap: .4rem; flex-wrap: wrap;
                     margin-left: auto; }
.cp-method .cp-pill { align-self: flex-start; }

/* The card's own action row: a button at its natural width, not a bar across
   the full card. */
.cp-actions { display: flex; align-items: center; gap: .5rem;
              flex-wrap: wrap; margin-top: 1rem; }

/* ==== ADD A METHOD ====
   The provider's element decides its own height, so this only has to give it
   room and keep the two ways out beside each other. */
.cp-setup-note { margin: 0 0 1rem; }
.cp-setup-error:empty { display: none; }
.cp-setup-error { margin: .75rem 0 0; color: var(--cp-risk, #8E3520); }
.cp-setup-actions { margin-top: 1.15rem; }
#cp-setup-element { margin-top: .25rem; }

/* ==== ACCOUNT CHOOSER ====
   Which account a payment-method page is acting on. Shown only where the
   sign-in reaches more than one, so a customer with a single account sees the
   name and no control. */
.cp-acct-pick { display: flex; flex-wrap: wrap; align-items: baseline;
                gap: .4rem .75rem; margin-bottom: 1rem;
                padding: .6rem .8rem; border: 1px solid var(--cp-line);
                border-radius: var(--cp-radius); background: var(--surface); }
.cp-acct-pick-label { font-size: .7rem; font-weight: 600; letter-spacing: .06em;
                      text-transform: uppercase; color: var(--cp-muted); }
.cp-acct-pick-name { font-size: .92rem; }
.cp-acct-pick-form { display: flex; align-items: center; gap: .4rem;
                     margin-left: auto; }
@media (max-width: 640px) {
    .cp-acct-pick-form { margin-left: 0; width: 100%; }
    .cp-select { flex: 1 1 auto; max-width: none; }
}

/* ==== PAY BASKET =========================================================
   The parts a multi-invoice payment needs, built from the portal's own
   tokens so the page reads as the same surface as everything around it. */

/* The two-column frame, the sticky rail and the document head are
   `.cp-split`, `.cp-split-side` and `.cp-doc-head` -- already defined above and
   already used by the invoice document. A second set under different names
   would be two rules and two selectors for one component. */

/* One tickable invoice per row. The amount is right-aligned and tabular so a
   column of figures can be read down rather than compared shape by shape. */
.cp-choose { list-style: none; margin: 0; padding: 0; }
.cp-choose li { display: flex; align-items: center; gap: .8rem;
                padding: .7rem 1.15rem;
                border-top: 1px solid var(--cp-line); }
.cp-choose li:first-child { border-top: 0; }
.cp-choose li:hover { background: var(--cp-hover); }
.cp-choose input[type="checkbox"] { width: 1.05rem; height: 1.05rem;
                                    accent-color: var(--cp-deep); flex: none; }
.cp-choose-main { flex: 1 1 auto; min-width: 0; cursor: pointer; }
.cp-choose-amt { font-weight: 650; font-variant-numeric: tabular-nums;
                 white-space: nowrap; }

/* The method choices. A method that cannot be used is shown, not hidden, and
   is visibly inert rather than merely greyed. */
.cp-methods { display: grid; grid-template-columns: repeat(auto-fit,
              minmax(15rem, 1fr)); gap: .7rem; padding: 1rem 1.15rem; }
.cp-methods .cp-method { display: flex; align-items: flex-start; gap: .6rem;
                         padding: .8rem .9rem; border: 1px solid var(--cp-line);
                         border-radius: var(--cp-radius); cursor: pointer; }
.cp-methods .cp-method:hover { background: var(--cp-hover); }
.cp-methods .cp-method b { display: block; font-size: .9rem; }
.cp-methods .cp-method p { margin: .15rem 0 0; font-size: .8rem;
                           color: var(--cp-muted); line-height: 1.5; }
.cp-methods .cp-method:has(input:checked) { border-color: var(--cp-deep);
                                            background: var(--cp-active-2); }
.cp-methods .cp-method.is-off { cursor: not-allowed; opacity: .72;
                                background: var(--cp-ground); }

.cp-sum-row { display: flex; align-items: baseline;
              justify-content: space-between; gap: 1rem;
              padding: .4rem 0; font-size: .88rem;
              font-variant-numeric: tabular-nums; }
.cp-sum-row.is-total { border-top: 1px solid var(--cp-line);
                       margin-top: .35rem; padding-top: .7rem;
                       font-size: 1.02rem; font-weight: 700; }

.cp-secure { display: flex; gap: .55rem; padding: .8rem 1.15rem;
             border-top: 1px solid var(--cp-line);
             background: var(--cp-ground); font-size: .78rem;
             color: var(--cp-muted); line-height: 1.55; }

.cp-mount-note { margin-top: .45rem; font-size: .78rem;
                 color: var(--cp-muted); line-height: 1.5; }
.cp-toggle { display: flex; align-items: center; gap: .5rem;
             font-size: .85rem; cursor: pointer; }
.cp-toggle input { width: 1rem; height: 1rem; accent-color: var(--cp-deep); }

/* The method choices in the summary rail: side by side, so the alternatives
   are compared at a glance rather than read as a list, with a rule between
   them and the figures so the choice reads as part of the same decision.
   Two in a 20.5rem rail is tight, so the copy under each label is compact and
   the pair stacks below the breakpoint where the rail becomes full width. */
.cp-methods.is-rail { grid-template-columns: 1fr 1fr; gap: .5rem;
                      padding: .85rem 1.15rem;
                      border-bottom: 1px solid var(--cp-line); }
.cp-methods.is-rail .cp-method { padding: .6rem .65rem; gap: .45rem; }
.cp-methods.is-rail .cp-method b { font-size: .85rem; }
.cp-methods.is-rail .cp-method p { font-size: .74rem; line-height: 1.45; }
@media (max-width: 62rem) {
    .cp-methods.is-rail { grid-template-columns: repeat(auto-fit,
                          minmax(13rem, 1fr)); }
}

/* The extra receipt address and the save opt-in share a line where there is
   room, and stack where there is not. */
.cp-receipt-row { display: flex; align-items: center; gap: 1rem;
                  flex-wrap: wrap; }
.cp-receipt-row .cp-input { flex: 1 1 18rem; min-width: 0; }
.cp-field-label { display: block; font-size: .78rem; font-weight: 600;
                  margin-bottom: .3rem; }

/* ==== PROJECTS ====
   A shared project's text is plain text: line breaks are kept by CSS, never
   by markup, so nothing anybody typed is read as HTML. A comment just posted
   is the target of the redirect and is marked, which is the confirmation. */
.cp-prose { margin: 0; white-space: pre-line; overflow-wrap: anywhere; font-size: .88rem; line-height: 1.55; }
.cp-comments { list-style: none; margin: .5rem 0 1rem; padding: 0; display: grid; gap: .6rem; }
.cp-comment { border: 1px solid var(--cp-line); border-radius: .6rem; padding: .7rem .85rem; }
.cp-comment.is-customer { background: var(--cp-ground); }
.cp-comment:target { outline: 2px solid var(--cp-brand); outline-offset: 2px; }
.cp-comment-head { display: flex; flex-wrap: wrap; align-items: center; gap: .45rem; margin-bottom: .3rem;
                   font-size: .84rem; }
.cp-comment-head .cp-quiet { font-size: .76rem; }
.cp-comment-form { display: grid; gap: .5rem; margin-top: .5rem; }
.cp-comment-form .cp-btn { justify-self: start; }
.cp-req-section .cp-quiet, .cp-req-section .cp-note { margin: .4rem 0 0; }
.cp-attachments { list-style: none; margin: .5rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: .5rem; }
.cp-attachment { display: flex; flex-direction: column; gap: .25rem; font-size: .82rem; max-width: 100%; overflow-wrap: anywhere; }
.cp-attachment:not(.is-image) { flex-basis: 100%; }
.cp-attachment img { display: block; max-width: min(100%, 20rem); max-height: 14rem; border-radius: .5rem;
                     border: 1px solid var(--cp-line); }

/* ==== DARK MODE ====
   Every colour above is a token, so most of the portal changes with
   `tokens.css` alone. These are the pairings that do not: white text on a
   colour that turns light in dark mode. Tokens only, never a value. */
:root[data-color-mode="dark"] .cp-btn.is-busy,
:root[data-color-mode="dark"] .cp-btn.is-busy:hover {
    background: var(--surface-hover); border-color: var(--border-hover);
    color: var(--text-primary); }
:root[data-color-mode="dark"] .cp-btn.is-busy .cp-spinner {
    border-color: var(--border-hover); border-top-color: var(--text-primary); }
:root[data-color-mode="dark"] .cp-imp-btn { color: var(--surface); }

/* **The rail and the sign-in brand panel are a dark surface, not a green
   field.** A large flat green beside a dark page is the brightest thing on
   it and pulls the eye from the content. Green stays as the accent: the
   selected item's tint and marker, the badges, the filled buttons. */
:root[data-color-mode="dark"] .cp-rail {
    background-color: var(--surface); background-image: none;
    box-shadow: 1px 0 0 var(--border); }
:root[data-color-mode="dark"] .cp-rail-brand,
:root[data-color-mode="dark"] .cp-user { border-color: var(--border); }
:root[data-color-mode="dark"] .cp-user { background: var(--surface-muted); }
:root[data-color-mode="dark"] .cp-nav a { color: var(--text-secondary); }
:root[data-color-mode="dark"] .cp-nav a:hover {
    background: var(--surface-hover); color: var(--text-primary); }
:root[data-color-mode="dark"] .cp-nav a.is-on {
    background: var(--brand-light); color: var(--text-primary); box-shadow: none; }
:root[data-color-mode="dark"] .cp-nav a.is-on::before { background: var(--brand); }
:root[data-color-mode="dark"] .cp-nav a.is-on i { color: var(--brand); opacity: 1; }
:root[data-color-mode="dark"] .cp-nav-group { color: var(--text-muted); }
:root[data-color-mode="dark"] .cp-solo-brand {
    background: var(--surface); box-shadow: 1px 0 0 var(--border); }
:root[data-color-mode="dark"] .cp-solo-brand::before {
    background: radial-gradient(circle at 22% 18%, var(--brand-light), transparent 60%); }
:root[data-color-mode="dark"] .cp-solo-brand::after { opacity: .12; }
:root[data-color-mode="dark"] .cp-solo-tagline { color: var(--text-secondary); }

/* ==== COLOUR MODE TOGGLE ====
   One button per frame: in the sign-in card while signed out, beside sign out
   in the rail once signed in. Shipped `hidden`; `color-mode.js` shows it. */
.cp-mode { font-family: inherit; display: inline-flex; align-items: center;
           justify-content: center; width: 2.1rem; height: 2.1rem;
           border-radius: .55rem; cursor: pointer; font-size: 1.1rem;
           color: var(--cp-muted); background: transparent;
           border: 1px solid var(--cp-line); }
.cp-mode:hover { color: var(--cp-ink); background: var(--cp-hover); }
.cp-mode[hidden] { display: none; }
.cp-solo-mode { display: flex; justify-content: flex-end; margin-bottom: .75rem; }
.cp-user .cp-mode { color: #fff; border-color: rgba(255,255,255,.24);
                    background: rgba(255,255,255,.10); flex: none; }
.cp-user .cp-mode:hover { background: rgba(255,255,255,.20); }
