/* ==========================================================================
   Little Moo Hosting - account portal structure
   ==========================================================================

   WHAT THIS SHEET IS FOR
   --------------------------------------------------------------------------
   The client area had the right information on every page and the wrong shape
   around it: a marketing page's shape. Big headings, a stack of buttons under
   the title, and one tall card per record - 134px of vertical space to say a
   plan name, a price and a date.

   This sheet is the account-portal shape instead. Breadcrumb, one heading, a
   toolbar, a real table, and a narrow column of actions on the right.

   WHY A NEW NAMESPACE
   --------------------------------------------------------------------------
   Everything here is `.lmh-portal-*` or `.lmh-dt*`, and nothing else in the
   theme styles those names. That is deliberate. Six token namespaces and a
   dozen sheets already compete over `.lmh-panel` and `.lmh-btn`, and every
   previous attempt to reshape a page by out-specifying them cost a round.
   New names have no competitors, so ordinary selectors win, and there is very
   little `!important` below.

   Loaded after lmh-brand.css - last of all - so where it does have to reach a
   shared class, source order is on its side.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Portal metrics
   --------------------------------------------------------------------------
   Body-scoped, not `:root`. A `:root` declaration loses to the body-scoped
   theme tokens that already exist, which is the single mistake this project
   has repeated most often.
   -------------------------------------------------------------------------- */

body.lmh-body {
    --lmh-portal-rail:      264px;   /* right-hand actions column          */
    --lmh-portal-gap:        24px;   /* main column to rail                */
    --lmh-portal-row-y:      13px;   /* table cell vertical padding        */
    --lmh-portal-row-x:      12px;   /* table cell horizontal padding      */
    --lmh-portal-radius:      8px;
    --lmh-portal-panel-pad:  18px;
}

/* --------------------------------------------------------------------------
   2. Page header: breadcrumb, one title, one action
   --------------------------------------------------------------------------
   The breadcrumb sits above the title and is the only thing that says where
   you are. The old purple uppercase eyebrow said the same word as the heading
   directly beneath it ("SERVICES" / "My services"), which is not orientation,
   it is repetition.
   -------------------------------------------------------------------------- */

.lmh-portal-header {
    border-bottom: 1px solid var(--lmh-client-border);
    padding-bottom: 14px;
    margin-bottom: 22px;
}

.lmh-portal-crumbs {
    margin: 0 0 4px;
    padding: 0;
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    line-height: 1.4;
}

.lmh-portal-crumbs li {
    display: flex;
    align-items: center;
    gap: 6px;
    color: var(--lmh-client-text-muted);
}

/* The separator is generated, so it is never read aloud as content. */
.lmh-portal-crumbs li + li::before {
    content: "";
    width: 5px;
    height: 5px;
    border-top: 1.5px solid currentColor;
    border-right: 1.5px solid currentColor;
    transform: rotate(45deg);
    opacity: .55;
    flex: none;
}

.lmh-portal-crumbs a {
    color: var(--lmh-client-purple);
    text-decoration: none;
}

.lmh-portal-crumbs a:hover,
.lmh-portal-crumbs a:focus-visible {
    text-decoration: underline;
}

.lmh-portal-header__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}

/* 24px, not the 34px display size a landing page uses. This is a screen
   label on a page you visit every week, not a headline. */
.lmh-portal-title {
    font-family: var(--lmh-font-heading);
    font-size: 24px;
    line-height: 1.25;
    font-weight: 700;
    letter-spacing: -.01em;
    margin: 0;
    color: var(--lmh-client-text);
}

.lmh-portal-header__actions {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* --------------------------------------------------------------------------
   3. Two-column workspace
   --------------------------------------------------------------------------
   `minmax(0, 1fr)` rather than `1fr`: a wide table in a grid track will
   otherwise force the track past its share and push the rail off-screen.
   -------------------------------------------------------------------------- */

.lmh-portal-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--lmh-portal-rail);
    gap: var(--lmh-portal-gap);
    align-items: start;
}

.lmh-portal-body--wide {
    grid-template-columns: minmax(0, 1fr);
}

.lmh-portal-main {
    min-width: 0;   /* lets the table scroll inside the track, not overflow it */
}

.lmh-portal-rail {
    display: flex;
    flex-direction: column;
    gap: 16px;
    position: sticky;
    top: 20px;
}

/* Below ~1080px the rail has nowhere useful to go, so it stops being a rail
   and becomes a block after the content it relates to. */
@media (max-width: 1080px) {
    .lmh-portal-body { grid-template-columns: minmax(0, 1fr); }
    .lmh-portal-rail { position: static; }
}

/* --------------------------------------------------------------------------
   4. Panels
   --------------------------------------------------------------------------
   Flat. One border, one background, small radius, no shadow and no gradient.
   -------------------------------------------------------------------------- */

.lmh-portal-panel {
    background: var(--lmh-client-surface);
    border: 1px solid var(--lmh-client-border);
    border-radius: var(--lmh-portal-radius);
    overflow: hidden;
}

.lmh-portal-panel + .lmh-portal-panel { margin-top: 16px; }

/* The stacking rule above is an adjacent-sibling selector, so it only ever
   matched panel-after-panel. On the dashboard the welcome panel is followed by
   .lmh-dash-columns - a grid, not a panel - so nothing matched and the summary
   cards sat flush against the card above them.

   Same 16px as the rest of the portal stack rather than a new value, and
   stated in both directions so a panel following the grid is spaced too. */
.lmh-portal-panel + .lmh-dash-columns,
.lmh-dash-columns + .lmh-portal-panel { margin-top: 16px; }

.lmh-portal-panel__head {
    display: flex;
    /* WHY IT WRAPS
       -----------------------------------------------------------------------
       This row is heading + action. Without wrapping, flex shrinks the heading
       to its minimum content width when the card is narrow, and a heading whose
       min-width is unconstrained collapses character by character - "Billing"
       rendered as a vertical column of single letters. Observed on the
       dashboard summary cards at narrow viewports.

       Two rules stop it, and neither touches type size:
         flex-wrap        lets the action drop to a second row instead of
                          stealing width from the heading;
         min-width        max-content on the heading means it can never be
                          squeezed below its longest word, so the wrap happens
                          rather than the letter-stacking. */
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px 12px;
    padding: 13px var(--lmh-portal-panel-pad);
    border-bottom: 1px solid var(--lmh-client-border);
}

.lmh-portal-panel__head > h2,
.lmh-portal-panel__head > h3 {
    flex: 1 1 auto;
    min-width: max-content;   /* never narrower than its longest word */
    overflow-wrap: normal;
    word-break: normal;
    hyphens: none;
}

.lmh-portal-panel__head h2,
.lmh-portal-panel__head h3 {
    font-family: var(--lmh-font-heading);
    font-size: 15px;
    font-weight: 700;
    line-height: 1.3;
    margin: 0;
    color: var(--lmh-client-text);
}

.lmh-portal-panel__body {
    padding: var(--lmh-portal-panel-pad);
}

/* A count beside a panel or page title. Quiet - it is a fact, not a badge. */
.lmh-portal-count {
    display: inline-block;
    margin-left: 6px;
    font-family: var(--lmh-font-body);
    font-size: 12px;
    font-weight: 600;
    line-height: 18px;
    padding: 0 7px;
    border-radius: 9px;
    background: var(--lmh-client-surface-3);
    color: var(--lmh-client-text-muted);
    vertical-align: 1px;
}

/* --------------------------------------------------------------------------
   5. The actions rail
   --------------------------------------------------------------------------
   Text links with a leading icon, not a stack of buttons. Four buttons in a
   row under a page title all shout equally; a list of links has one obvious
   reading order and takes a quarter of the space.
   -------------------------------------------------------------------------- */

.lmh-portal-rail-list {
    margin: 0;
    padding: 6px 0;
    list-style: none;
}

.lmh-portal-rail-list a {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px var(--lmh-portal-panel-pad);
    font-size: 14px;
    line-height: 1.4;
    color: var(--lmh-client-text);
    text-decoration: none;
}

.lmh-portal-rail-list a:hover,
.lmh-portal-rail-list a:focus-visible {
    background: var(--lmh-client-bg);
    color: var(--lmh-client-purple);
}

.lmh-portal-rail-list i {
    width: 16px;
    flex: none;
    text-align: center;
    font-size: 13px;
    color: var(--lmh-client-text-muted);
}

.lmh-portal-rail-list a:hover i,
.lmh-portal-rail-list a:focus-visible i {
    color: var(--lmh-client-purple);
}

/* A short explanatory paragraph in the rail, e.g. the Xero note. */
.lmh-portal-rail-note {
    padding: 0 var(--lmh-portal-panel-pad) var(--lmh-portal-panel-pad);
    margin: 0;
    font-size: 13px;
    line-height: 1.55;
    color: var(--lmh-client-text-muted);
}

/* --------------------------------------------------------------------------
   6. List toolbar
   --------------------------------------------------------------------------
   Sits above the table, outside the panel. Search is pushed right, because
   that is where every list interface in this class of product puts it.
   -------------------------------------------------------------------------- */

.lmh-portal-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.lmh-portal-toolbar__count {
    font-size: 13px;
    color: var(--lmh-client-text-muted);
    margin: 0;
}

.lmh-portal-toolbar__spacer { flex: 1 1 auto; }

.lmh-portal-toolbar select,
.lmh-portal-toolbar input[type="search"] {
    height: 36px;
    font-size: 14px;
    font-family: inherit;
    padding: 0 12px;
    border-radius: 6px;
    border: 1px solid var(--lmh-client-border-strong);
    background: var(--lmh-client-surface);
    color: var(--lmh-client-text);
}

.lmh-portal-toolbar select { padding-right: 30px; }

/* The search box is an input GROUP, not an icon floated over an input.

   The overlay version is the obvious way to build this and it does not survive
   here: the client-shell form contract sets `padding: 11px 14px !important` on
   every input, at a weight built from a six-clause `:not()` chain, so the text
   starts under the icon and no amount of padding on the input can move it.

   Making the label the bordered box and the icon a flex sibling sidesteps the
   argument entirely. The input inside keeps whatever padding the contract
   wants to give it, because it no longer has a border or a background of its
   own for that padding to matter to. */
.lmh-portal-search {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 260px;
    max-width: 100%;
    height: 36px;
    padding-left: 12px;
    border: 1px solid var(--lmh-client-border-strong);
    border-radius: 6px;
    background: var(--lmh-client-surface);
    overflow: hidden;
}

.lmh-portal-search i {
    flex: none;
    font-size: 13px;
    color: var(--lmh-client-text-muted);
}

.lmh-portal-search:focus-within {
    border-color: var(--lmh-client-purple);
    outline: 2px solid var(--lmh-client-focus);
    outline-offset: 1px;
}

.lmh-portal-toolbar :is(select, input):focus-visible {
    outline: 2px solid var(--lmh-client-focus);
    outline-offset: 1px;
    border-color: var(--lmh-client-purple);
}

/* --------------------------------------------------------------------------
   7. The data table
   --------------------------------------------------------------------------
   The substantive change on every list page. One header row naming the
   columns, instead of the same field labels reprinted inside all eight rows.

   Wrapped in `.lmh-portal-tablewrap`, which scrolls on its own axis so a wide
   table can never make the page scroll sideways.
   -------------------------------------------------------------------------- */

/* No scroll container, and therefore no clipping.

   This was `overflow-x: auto`, from when the table could be wider than its
   column. It no longer can be: below 1400 the date columns wrap, and at 560
   and below each row becomes a stacked record - verified across all 23
   authenticated routes at 1440, 1200, 768 and 390 with no table exceeding its
   panel.

   Leaving it in place was not merely redundant, it was breaking the row
   overflow menu. `overflow-x: auto` makes the OTHER axis compute to `auto`
   too - CSS resolves `visible` to `auto` when its partner is not `visible` -
   so the wrapper clipped vertically. The menu is absolutely positioned inside
   it and opens downward, so it was cut off at the wrapper's edge: measured
   with the menu open, panel bottom 471px against a wrapper bottom of 441px.
   `z-index` cannot help, because clipping by an ancestor is not a stacking
   question.

   The menu opened correctly the whole time. It just could not be seen, which
   is indistinguishable from a dead control. */
.lmh-portal-tablewrap {
    overflow: visible;
}

table.lmh-dt {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
    line-height: 1.45;
    margin: 0;
    background: transparent;
}

table.lmh-dt th,
table.lmh-dt td {
    text-align: left;
    padding: var(--lmh-portal-row-y) var(--lmh-portal-row-x);
    vertical-align: middle;
}

table.lmh-dt thead th {
    font-family: var(--lmh-font-body);
    font-weight: 500;
    font-size: 13px;
    text-transform: none;      /* not a shouted uppercase label row */
    letter-spacing: 0;
    white-space: nowrap;
    color: var(--lmh-client-text-muted);
    background: var(--lmh-client-bg);
    border-bottom: 1px solid var(--lmh-client-border);
}

table.lmh-dt tbody tr + tr > td {
    border-top: 1px solid var(--lmh-client-border);
}

table.lmh-dt tbody tr:hover > td {
    background: var(--lmh-client-bg);
}

/* The identity column: name on top, its domain or reference beneath. */
.lmh-dt__name {
    display: block;
    font-weight: 600;
    color: var(--lmh-client-text);
    text-decoration: none;
}

a.lmh-dt__name:hover,
a.lmh-dt__name:focus-visible {
    color: var(--lmh-client-purple);
    text-decoration: underline;
}

.lmh-dt__sub {
    display: block;
    margin-top: 1px;
    font-size: 13px;
    color: var(--lmh-client-text-muted);
    overflow-wrap: anywhere;   /* a long hostname wraps rather than widening */
}

/* Numeric-ish columns line up when the figures use tabular digits. */
.lmh-dt__num {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.lmh-dt__cycle {
    display: block;
    font-size: 13px;
    color: var(--lmh-client-text-muted);
}

.lmh-dt__actions {
    width: 1%;
    white-space: nowrap;
    text-align: right;
}

/* Every column except the first shrinks to its content, so the identity
   column gets all the slack. Without this the browser shares width evenly and
   the service name wraps beside three columns of trailing whitespace. */
table.lmh-dt thead th:not(:first-child),
table.lmh-dt tbody td:not(:first-child) {
    width: 1%;
    white-space: nowrap;
}

/* The identity column is the exception: it may wrap, but only between words,
   never inside a hostname. */
/* Unread ticket subjects. */
.lmh-dt__name.is-unread { font-weight: 700; }

/* --------------------------------------------------------------------------
   8. Row-level controls
   -------------------------------------------------------------------------- */

.lmh-dt-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 30px;
    padding: 0 12px;
    border-radius: 6px;
    border: 1px solid var(--lmh-client-border-strong);
    background: var(--lmh-client-surface);
    color: var(--lmh-client-text);
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    line-height: 1;
    text-decoration: none;
    white-space: nowrap;
}

.lmh-dt-btn:hover,
.lmh-dt-btn:focus-visible {
    border-color: var(--lmh-client-purple);
    color: var(--lmh-client-purple);
    text-decoration: none;
}

/* The primary fill is `--lmh-purple-fill`, not `--lmh-client-purple`.

   `--lmh-client-purple` is the *link and accent* purple, and in dark it is a
   pale lavender - #A78BFA. Used as a button fill under white text it measured
   2.72:1, well below the 4.5:1 AA needs, and it looked washed out beside every
   other primary button on the page.

   `--lmh-purple-fill` is the token the theme already uses for a filled primary
   action - #7C5CE0 in dark, which carries white at about 5.1:1 - paired with
   `--lmh-brand-on-fill` for its text. Adopting the existing pair rather than
   inventing a second primary treatment is what makes these buttons match the
   ones beside them, and it fixes both themes at once. */
.lmh-dt-btn--primary {
    background: var(--lmh-purple-fill);
    border-color: var(--lmh-purple-fill);
    color: var(--lmh-brand-on-fill);
}

.lmh-dt-btn--primary:hover,
.lmh-dt-btn--primary:focus-visible {
    background: var(--lmh-brand-fill-hover);
    border-color: var(--lmh-brand-fill-hover);
    color: var(--lmh-brand-on-fill);
}

/* The overflow menu, built on <details> so it needs no JavaScript and closes
   on Escape by itself. */
.lmh-dt-menu { position: relative; display: inline-block; }

.lmh-dt-menu > summary {
    list-style: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 6px;
    border: 1px solid var(--lmh-client-border-strong);
    background: var(--lmh-client-surface);
    color: var(--lmh-client-text-muted);
    cursor: pointer;
    font-size: 13px;
}

.lmh-dt-menu > summary::-webkit-details-marker { display: none; }

.lmh-dt-menu > summary:hover,
.lmh-dt-menu[open] > summary {
    border-color: var(--lmh-client-purple);
    color: var(--lmh-client-purple);
}

.lmh-dt-menu__panel {
    position: absolute;
    right: 0;
    top: calc(100% + 4px);
    z-index: 40;
    min-width: 200px;
    padding: 5px 0;
    text-align: left;
    background: var(--lmh-client-surface);
    border: 1px solid var(--lmh-client-border-strong);
    border-radius: var(--lmh-portal-radius);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
}

.lmh-dt-menu__panel a {
    display: block;
    padding: 7px 14px;
    font-size: 13px;
    font-weight: 500;
    color: var(--lmh-client-text);
    text-decoration: none;
    white-space: nowrap;
}

.lmh-dt-menu__panel a:hover,
.lmh-dt-menu__panel a:focus-visible {
    background: var(--lmh-client-bg);
    color: var(--lmh-client-purple);
}

/* --------------------------------------------------------------------------
   9. Status pills
   --------------------------------------------------------------------------
   Small, tinted, readable. Sized to the row rather than to a card.
   -------------------------------------------------------------------------- */

.lmh-pill {
    display: inline-flex;
    align-items: center;
    height: 22px;
    padding: 0 9px;
    border-radius: 11px;
    border: 1px solid transparent;
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    white-space: nowrap;
    background: var(--lmh-client-surface-3);
    color: var(--lmh-client-text-muted);
}

.lmh-pill--ok   { background: var(--lmh-success-surface); color: var(--lmh-success-text); border-color: var(--lmh-success-border); }
.lmh-pill--warn { background: var(--lmh-warning-surface); color: var(--lmh-warning-text); border-color: var(--lmh-warning-border); }
.lmh-pill--bad  { background: var(--lmh-danger-surface);  color: var(--lmh-danger-text);  border-color: var(--lmh-danger-border);  }
.lmh-pill--info { background: var(--lmh-info-surface);    color: var(--lmh-info-text);    border-color: var(--lmh-info-border);    }

/* --------------------------------------------------------------------------
   10. Empty states
   --------------------------------------------------------------------------
   Inside the panel, on the table's own ground, so an empty list still looks
   like the list it will become.
   -------------------------------------------------------------------------- */

.lmh-portal-empty {
    padding: 40px 24px;
    text-align: center;
}

.lmh-portal-empty strong {
    display: block;
    font-family: var(--lmh-font-heading);
    font-size: 16px;
    font-weight: 700;
    color: var(--lmh-client-text);
}

.lmh-portal-empty p {
    max-width: 44ch;
    margin: 6px auto 0;
    font-size: 14px;
    line-height: 1.55;
    color: var(--lmh-client-text-muted);
}

.lmh-portal-empty__actions {
    display: flex;
    justify-content: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 16px;
}

/* --------------------------------------------------------------------------
   11. Dashboard summary tiles
   --------------------------------------------------------------------------
   Four counts in one panel, each a link to the list it counts. Krystal's
   equivalent is a "Quick Actions" grid; this is the same idea reduced to what
   Little Moo actually has pages for. No invented destinations.
   -------------------------------------------------------------------------- */

.lmh-portal-tiles {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    padding: var(--lmh-portal-panel-pad);
}

.lmh-portal-tile {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border: 1px solid var(--lmh-client-border);
    border-radius: 6px;
    background: var(--lmh-client-surface);
    color: var(--lmh-client-text);
    text-decoration: none;
}

.lmh-portal-tile:hover,
.lmh-portal-tile:focus-visible {
    border-color: var(--lmh-client-purple);
    color: var(--lmh-client-text);
    text-decoration: none;
}

.lmh-portal-tile__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex: none;
    border-radius: 6px;
    background: var(--lmh-client-purple-soft);
    color: var(--lmh-client-purple);
    font-size: 14px;
}

.lmh-portal-tile__label {
    font-size: 14px;
    font-weight: 600;
    line-height: 1.3;
}

.lmh-portal-tile__value {
    margin-left: auto;
    font-size: 15px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--lmh-client-text);
}

@media (max-width: 560px) {
    .lmh-portal-tiles { grid-template-columns: minmax(0, 1fr); }
}

/* --------------------------------------------------------------------------
   12. Compact stacked list (dashboard panels)
   -------------------------------------------------------------------------- */

.lmh-portal-list { margin: 0; padding: 0; list-style: none; }

.lmh-portal-list > li {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 11px var(--lmh-portal-panel-pad);
}

.lmh-portal-list > li + li { border-top: 1px solid var(--lmh-client-border); }

.lmh-portal-list__main { min-width: 0; flex: 1 1 auto; }

.lmh-portal-list__meta {
    flex: none;
    font-size: 13px;
    color: var(--lmh-client-text-muted);
    white-space: nowrap;
}

/* --------------------------------------------------------------------------
   13. Notice block
   --------------------------------------------------------------------------
   Used by the invoices route, which is an explanation rather than a list.
   -------------------------------------------------------------------------- */

.lmh-portal-notice {
    padding: var(--lmh-portal-panel-pad);
    font-size: 14px;
    line-height: 1.6;
    color: var(--lmh-client-text);
}

.lmh-portal-notice p { margin: 0 0 10px; }
.lmh-portal-notice p:last-child { margin-bottom: 0; }

.lmh-portal-steps {
    counter-reset: lmhstep;
    margin: 0;
    padding: 0;
    list-style: none;
}

.lmh-portal-steps > li {
    position: relative;
    counter-increment: lmhstep;
    padding: 12px var(--lmh-portal-panel-pad) 12px 52px;
    font-size: 14px;
    line-height: 1.55;
    color: var(--lmh-client-text);
}

.lmh-portal-steps > li + li { border-top: 1px solid var(--lmh-client-border); }

.lmh-portal-steps > li::before {
    content: counter(lmhstep);
    position: absolute;
    left: var(--lmh-portal-panel-pad);
    top: 12px;
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--lmh-client-purple-soft);
    color: var(--lmh-client-purple);
    font-size: 12px;
    font-weight: 700;
}

.lmh-portal-steps strong { display: block; font-weight: 600; }

.lmh-portal-steps span {
    display: block;
    font-size: 13px;
    color: var(--lmh-client-text-muted);
}

/* --------------------------------------------------------------------------
   14. Narrow widths
   -------------------------------------------------------------------------- */

@media (max-width: 640px) {
    .lmh-portal-title { font-size: 21px; }
    .lmh-portal-header__row { align-items: flex-start; }
    .lmh-portal-search { width: 100%; }
    body.lmh-body { --lmh-portal-row-x: 12px; }
}

/* --------------------------------------------------------------------------
   15. Where the portal has to reach shared components
   --------------------------------------------------------------------------
   Everything above wins on names alone. These four do not, because stock
   WHMCS elements - a `th`, a `select`, an `input` - are already claimed by
   rules elsewhere in the theme. Each was measured rather than guessed, and
   each is answered at the smallest weight that actually wins.
   -------------------------------------------------------------------------- */

/* The column labels. Three separate rules set uppercase on a `th`; the
   strongest is `body.lmh-client-shell .lmh-primary-content th` at (0,2,2).
   Prefixing the shell class takes this rule to (0,2,3), so it wins on
   specificity and needs no `!important`.

   Uppercase with letter-spacing is a card-era label treatment. In a table it
   makes the header row shout louder than the data underneath it. */
body.lmh-client-shell table.lmh-dt thead th,
body.lmh-client-shell table.lmh-dt tbody td {
    padding: var(--lmh-portal-row-y) var(--lmh-portal-row-x);
}

body.lmh-client-shell table.lmh-dt thead th {
    font-size: 13px;
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    color: var(--lmh-client-text-muted);
}

/* The toolbar controls. The form-control contract sets
   `width: 100%; padding: 11px 14px !important` on every input and select in
   the client shell, which is right for a stacked form and wrong for a filter
   bar - the status select stretched to the full 752px of the column.

   The padding declaration it loses to is already `!important`, so `!important`
   is the only thing that can answer it. This is the whole of it: two controls,
   on one component, sized to sit in a row. */
body.lmh-client-shell .lmh-portal-toolbar select {
    width: auto !important;
    min-width: 150px;
    padding: 0 30px 0 12px !important;
    height: 36px !important;
}

body.lmh-client-shell .lmh-portal-search input[type="search"] {
    width: 100% !important;
    padding: 0 12px 0 32px !important;   /* 32px clears the search icon */
    height: 36px !important;
}

/* The button text colour.

   The header's primary action rendered as an empty purple pill - purple text
   on a purple fill. The cause is the light theme's link colour, whose selector
   ends `a:not(.btn):not(.lmh-btn):not(...)`: a contract rule that already
   exempts buttons, and could not know about `.lmh-dt-btn`.

   The first attempt was to take the contract's own exemption by adding
   `lmh-btn` to these buttons. That fixed the colour and broke the box:
   `.lmh-btn` sets `border: none` and its own height, at a weight the portal
   sheet does not beat, so every row button lost its outline and grew to 44px.
   Opting in to a component to borrow one of its exemptions means inheriting
   all of its opinions.

   So the class came back off, and the one declaration that was actually wrong
   is answered directly. The contract's colour is not `!important`, but it does
   carry six `:not()` clauses of specificity; `!important` on two declarations
   is a smaller, more legible thing than a selector built to mirror that chain,
   which is a trap this theme has fallen into before and documents elsewhere.

   This is the only `!important` in the sheet that is not answering another
   `!important`. */
.lmh-dt-btn { color: var(--lmh-client-text) !important; }

.lmh-dt-btn:hover,
.lmh-dt-btn:focus-visible { color: var(--lmh-client-purple) !important; }

.lmh-dt-btn--primary,
.lmh-dt-btn--primary:hover,
.lmh-dt-btn--primary:focus-visible { color: var(--lmh-brand-on-fill) !important; }

/* The identity column takes 40% of the table and the rest shrink to their
   content. A `min-width` on a `td` is advisory under automatic table layout -
   the browser had settled it at 192px, which wrapped every hostname over three
   lines - so the share is stated as a percentage on the header cell, where
   table layout does honour it. */
table.lmh-dt thead th:first-child { width: 40%; }

/* The table header band.

   Two rules had to be answered here, and only one of them was visible at a
   time, which is why the same table looked different on two pages.

   `.lmh-route-v2 table thead th` forces `background: transparent !important`.
   Every list route carries that class, so on those pages the band simply did
   not paint. The dashboard does not carry it - and there, a third rule,
   `… .lmh-primary-content table:not(…) thead th`, painted the band
   `rgba(75, 54, 123, .26)`: a purple wash left over from the card era.

   So the Overview had a lavender header row and My services had none, from
   the same stylesheet and the same markup.

   Naming `.lmh-primary-content` takes this to (0,3,4) - past the purple rule's
   (0,3,3) - and `!important` answers the transparent one, which is itself
   `!important` and cannot be outranked any other way. The plain-scoped rule
   below it covers any route that is not inside `.lmh-primary-content`. */
body.lmh-client-shell .lmh-primary-content table.lmh-dt thead th,
body.lmh-client-shell table.lmh-dt thead th {
    background: var(--lmh-client-bg) !important;
}

/* Row hover, for the same reason: it was painting white on white. */
body.lmh-client-shell .lmh-primary-content table.lmh-dt tbody tr:hover > td,
body.lmh-client-shell table.lmh-dt tbody tr:hover > td {
    background: var(--lmh-client-bg);
}

/* ==========================================================================
   16. The rest of the client area
   ==========================================================================
   The five rebuilt routes use `.lmh-portal-panel` directly. Every other
   authenticated route - service status, the knowledgebase, account details,
   security, announcements, ticket views, the order routes - is built on the
   older `.lmh-panel` / `.lmh-section-heading` components, and those still
   carried the card-era treatment: 24px headings, large radii, gradients,
   shadows and generous padding.

   Rather than rewrite thirty templates, the shared components are given the
   portal treatment here. A route that uses `.lmh-panel` gets a flat panel with
   a 15px titled head, because that is what `.lmh-panel` now means.

   Templates are still worth rewriting where the *structure* is wrong - a list
   that should be a table, a stack of buttons that should be a rail. This is
   for the routes where the structure is already right and only the surface
   was shouting.
   ========================================================================== */

body.lmh-client-shell :is(.lmh-panel, .lmh-card, .lmh-status-card, .lmh-dashboard-panel) {
    background: var(--lmh-client-surface);
    border: 1px solid var(--lmh-client-border);
    border-radius: var(--lmh-portal-radius);
    background-image: none;
    box-shadow: none;
}

/* The panel head: a titled band with a rule under it, matching the portal
   panels exactly, so the two systems are indistinguishable on screen. */
body.lmh-client-shell :is(.lmh-panel-header, .lmh-dashboard-panel-header, .lmh-section-heading) {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 13px var(--lmh-portal-panel-pad);
    margin: 0;
    border-bottom: 1px solid var(--lmh-client-border);
    background: transparent;
}

body.lmh-client-shell :is(.lmh-panel-header, .lmh-dashboard-panel-header, .lmh-section-heading) :is(h2, h3) {
    font-family: var(--lmh-font-heading);
    font-size: 15px;
    font-weight: 700;
    line-height: 1.3;
    margin: 0;
}

body.lmh-client-shell :is(.lmh-panel-header, .lmh-dashboard-panel-header, .lmh-section-heading) p {
    margin: 2px 0 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--lmh-client-text-muted);
}

body.lmh-client-shell .lmh-panel-body {
    padding: var(--lmh-portal-panel-pad);
}

/* A subtitle under a page title, for the routes that genuinely need a line of
   explanation. One line, at body size - not a marketing lede. */
.lmh-portal-subtitle {
    margin: 4px 0 0;
    max-width: 76ch;
    font-size: 14px;
    line-height: 1.55;
    color: var(--lmh-client-text-muted);
}

.lmh-portal-header__titles { min-width: 0; }

/* Section headings that are not inside a panel still shrink: a route-level
   subheading is a label, not a headline. */
body.lmh-client-shell .lmh-primary-content :is(h2, h3):not(.lmh-portal-title) {
    font-family: var(--lmh-font-heading);
    letter-spacing: 0;
}

/* ---- column share on wide screens ---------------------------------------
   Every column except the first shrinks to its content, so all the slack lands
   in the identity column. At 1440 that is right - it is the column that wraps.
   At 1920 it means a 240px hostname sitting in a 640px cell, with the dates it
   should be read against pushed far to the right.

   So the share comes down as the screen grows. The table still fills the
   width; the data just stays close enough together to scan across a row. */
@media (min-width: 1600px) {
    table.lmh-dt thead th:first-child { width: 30%; }
}

@media (min-width: 1900px) {
    table.lmh-dt thead th:first-child { width: 26%; }
}

/* --------------------------------------------------------------------------
   17. The application icon
   --------------------------------------------------------------------------
   Icons are inline SVG that inherit their colour from the text beside them, so
   the rules here are only about size and alignment. Colour is never set on the
   icon itself - that is what `stroke="currentColor"` is for, and it means an
   icon in a hovered link, a disabled control or a status pill is automatically
   the right colour without a rule per state.
   -------------------------------------------------------------------------- */

.lmh-icon {
    flex: none;
    display: inline-block;
    vertical-align: -0.18em;
}

/* In the rail, the icon column is a fixed width so every label starts on the
   same vertical line regardless of how wide its glyph happens to be. */
.lmh-portal-rail-list a .lmh-icon {
    width: 17px;
    height: 17px;
    color: var(--lmh-client-text-muted);
}

.lmh-portal-rail-list a:hover .lmh-icon,
.lmh-portal-rail-list a:focus-visible .lmh-icon {
    color: var(--lmh-client-purple);
}

/* The tile icon keeps its container: a count tile is a target, and the
   container is what makes it read as one. */
.lmh-portal-tile__icon .lmh-icon {
    width: 18px;
    height: 18px;
}

/* The search icon sits inside the field, at label size. */
.lmh-portal-search .lmh-icon {
    width: 15px;
    height: 15px;
    color: var(--lmh-client-text-muted);
}

/* The row overflow trigger: three dots, drawn rather than lettered. */
.lmh-dt-menu > summary .lmh-icon {
    width: 16px;
    height: 16px;
}

/* A button that leads with an icon gets optical spacing, not a nbsp. */
.lmh-dt-btn .lmh-icon {
    margin-right: 6px;
}

/* The sidebar disclosure chevron, rotated when its group is open.

   Font Awesome drew a solid glyph; this is the same stroke chevron the rest of
   the application uses. It only ever appears on an item that has something to
   expand - the sidebar template counts the children that will actually render
   rather than trusting hasChildren(), which reported true for Services long
   after every one of its children had been removed. */
body.lmh-client-shell .lmh-client-nav-disclosure .lmh-icon,
body.lmh-client-shell .lmh-client-nav-disclosure-main .lmh-icon {
    transition: transform var(--lmh-ease, .18s ease);
}

body.lmh-client-shell .lmh-client-nav-item.is-open > .lmh-client-nav-group-row .lmh-client-nav-disclosure .lmh-icon,
body.lmh-client-shell .lmh-client-nav-item.is-open > .lmh-client-nav-disclosure-main .lmh-icon {
    transform: rotate(180deg);
}

/* The theme toggle holds both icons and reveals one.

   It used to swap `fa-sun` / `fa-moon` classes on a single glyph element. That
   only works with an icon font; with inline SVG there is nothing for a class
   swap to change. Both are rendered and the wrapper's `is-light` class decides
   which is visible, so the JS still toggles exactly one class. */
.lmh-theme-icon { display: inline-flex; }
.lmh-theme-icon .lmh-icon--sun { display: none; }
.lmh-theme-icon.is-light .lmh-icon--moon { display: none; }
.lmh-theme-icon.is-light .lmh-icon--sun { display: inline-block; }

/* The verification banner: icon beside its sentence, not above it.

   Font Awesome drew an inline glyph, so the markup relied on inline flow to
   keep the warning symbol next to the text. An inline SVG at 20px is taller
   than the line box and pushed the sentence onto the next line. Making the
   column a flex row restores the relationship explicitly rather than relying
   on the icon happening to fit. */
body.lmh-client-shell .verification-banner [class*="col-"] > .lmh-icon {
    /* Explicit box. A theme rule sets `svg { display: block }`, and a block
       SVG in a flex row does not always resolve its width from the width
       attribute - it claimed the whole line and pushed the sentence below it.
       Stating the size in CSS settles it. */
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    margin-top: 1px;
}

body.lmh-client-shell .verification-banner [class*="col-"]:has(> .lmh-icon) {
    display: flex;
    align-items: flex-start;
    gap: 10px;
}

/* --------------------------------------------------------------------------
   Dashboard: summary, attention, columns
   --------------------------------------------------------------------------
   Restrained on purpose. The dashboard answers "what do I need to do?", so the
   only thing that gets visual weight is the attention list; everything else is
   the same panel language as the rest of the client area. No hero card, no
   gradient, no shadow.
   -------------------------------------------------------------------------- */

.lmh-dash-summary {
    margin: 0 0 var(--lmh-portal-gap, 16px);
    font-size: 15px;
    line-height: 1.55;
    color: var(--lmh-client-text);
}

.lmh-dash-summary span { color: var(--lmh-client-text-muted); padding: 0 2px; }

/* The attention panel earns a left edge. Nothing else on the page has one, so
   it reads as "this is the part that is asking you for something". */
.lmh-attention { border-left: 3px solid var(--lmh-accent, #6D4CDB); }
.lmh-attention--clear { border-left-color: var(--lmh-client-border); }

.lmh-attention-list { margin: 0; padding: 0; list-style: none; }

.lmh-attention-item {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 12px var(--lmh-portal-panel-pad);
}

.lmh-attention-item + .lmh-attention-item { border-top: 1px solid var(--lmh-client-border); }

.lmh-attention-item__icon { flex: none; margin-top: 1px; color: var(--lmh-client-text-muted); }

/* Tone is carried by the icon only, and never alone: every row states its
   situation in words as well. */
.lmh-attention-item--bad  .lmh-attention-item__icon { color: var(--lmh-danger, #B42318); }
.lmh-attention-item--warn .lmh-attention-item__icon { color: var(--lmh-warning, #C2410C); }
.lmh-attention-item--info .lmh-attention-item__icon { color: var(--lmh-accent, #6D4CDB); }

.lmh-attention-item__body { flex: 1 1 auto; min-width: 0; }
.lmh-attention-item__body strong { display: block; font-weight: 500; font-size: 14px; }

.lmh-attention-item__body span {
    display: block;
    margin-top: 2px;
    font-size: 13px;
    line-height: 1.45;
    color: var(--lmh-client-text-muted);
}

.lmh-attention-item > .lmh-dt-btn { flex: none; align-self: center; }

.lmh-attention-clear {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0;
    font-size: 14px;
    color: var(--lmh-client-text-muted);
}

.lmh-attention-clear .lmh-icon { color: var(--lmh-success, #067647); }

/* --------------------------------------------------------------------------
   Dashboard summary trio - Billing, Support, Platform status
   --------------------------------------------------------------------------
   This used to be `repeat(auto-fit, minmax(260px, 1fr))`, and 260px was the
   problem. On the dashboard the trio sits inside .lmh-portal-main, which at
   1440px is only ~841px wide once the 232px sidebar and the 264px rail have
   taken their share. Three tracks at a 260px minimum therefore just fit, at
   264px each - so auto-fit packed three columns into the width where three
   columns are least appropriate.

   Measured consequences at 1440: body copy 226px wide, so "No invoices yet..."
   wrapped to three lines; the "Platform status" heading wrapped to two; and
   the gap between heading and its button collapsed from 76px on Billing to
   12px there.

   The perverse part: the cards were WIDER at 1024 (310px) than at 1440
   (264px), because below 1080px the rail stops being a rail and gives the
   width back.

   So the count is stated per breakpoint rather than inferred from a minimum.
   Three across only above 1600px, where the main column can genuinely afford
   it. Between that and the mobile stack, two across - and the odd third card
   spans the row rather than sitting alone beside a gap.
   -------------------------------------------------------------------------- */

.lmh-dash-columns {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--lmh-portal-gap, 16px);
}

/* Exactly three cards, two tracks: the last one takes the full row. */
.lmh-dash-columns > .lmh-portal-panel:nth-child(3):last-child {
    grid-column: 1 / -1;
}

@media (min-width: 1600px) {
    .lmh-dash-columns { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .lmh-dash-columns > .lmh-portal-panel:nth-child(3):last-child {
        grid-column: auto;
    }
}

.lmh-dash-columns > .lmh-portal-panel { margin: 0; }

/* The action keeps its own width instead of being compressed by a heading
   that wants to wrap; the heading gets the remaining room. */
.lmh-dash-columns .lmh-portal-panel__head { gap: 16px; }
.lmh-dash-columns .lmh-portal-panel__head > .lmh-dt-btn { flex: none; }

.lmh-dash-figure {
    margin: 0 0 6px;
    font-size: 24px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--lmh-client-text);
}

.lmh-dash-figure span {
    display: block;
    font-size: 13px;
    font-weight: 400;
    color: var(--lmh-client-text-muted);
}

.lmh-dash-status { font-weight: 500; }
.lmh-dash-status--ok   { color: var(--lmh-success, #067647); }
.lmh-dash-status--warn { color: var(--lmh-warning, #C2410C); }
.lmh-dash-status--bad  { color: var(--lmh-danger, #B42318); }

.lmh-dash-status-note {
    margin-top: 4px;
    font-size: 13px;
    color: var(--lmh-client-text-muted);
}

.lmh-portal-list__icon {
    flex: none;
    display: inline-flex;
    color: var(--lmh-client-text-muted);
}

@media (max-width: 640px) {
    .lmh-dash-columns { grid-template-columns: minmax(0, 1fr); }

    /* The action moves under the text rather than being squeezed beside it,
       so it stays a real target instead of a chip in the corner. */
    .lmh-attention-item { flex-wrap: wrap; }
    .lmh-attention-item > .lmh-dt-btn { margin-left: 34px; }
}
