/* =============================================================================
 * portal.css — the account portal's stylesheet.
 *
 * ## This file extends public/styles.css. It does not replace it.
 *
 * Every portal page links `styles.css` first and this file second, so everything below
 * either sets something `styles.css` left alone or overrides something it set at equal
 * weight. Nothing here restates a rule that is already correct: `.wrap`, `.button`,
 * `.button.small`, `.text-link`, `.eyebrow`, `.skip`, the `.brand`, `nav`, `footer`,
 * `[hidden]` and the `@media (prefers-reduced-motion)` block all arrive from there and are
 * used as they are.
 *
 * ## What the marketing stylesheet does not have
 *
 * Tables, form controls, definition lists, page headers, breadcrumbs, empty states,
 * badges, alerts and a loading placeholder. Those are what this file adds. It also adds
 * `.button.secondary` and `.button.ghost`, because `styles.css` has `.button` and
 * `.button.small` and no other variant, and a portal needs a quieter action than a blue
 * filled button.
 *
 * ## Colours
 *
 * Everything is a `var()` from `public/styles.css`: `--blue`, `--ink`, `--muted`, `--line`,
 * `--navy`, `--pale`. There are exactly two literals in this file and both are derived, in
 * `:root` below, with the reason recorded next to them. `tests/portal-ui.test.mjs` strips
 * the comments from this file and asserts that those two declarations are the only hex
 * literals left, so a colour added casually fails the gate.
 *
 * ## No @import, no remote anything beyond the fonts
 *
 * The CSP allows `style-src 'self' https://fonts.googleapis.com` and
 * `font-src 'self' https://fonts.gstatic.com`, and `styles.css` already uses the first for
 * its own `@import`. Nothing is added here: the portal uses the same DM Sans + Manrope
 * pair, and a monospace face comes from the platform stack rather than from a third
 * stylesheet request for one rule.
 *
 * ## No !important
 *
 * There is none, deliberately. `styles.css` uses it twice (`[hidden]` and the reduced-motion
 * block) and those are the only two places that needed it; every override below wins on
 * source order or on one more class. A war of `!important` between a marketing stylesheet
 * and a portal stylesheet is how a focus ring disappears.
 *
 * ## Breakpoints
 *
 * The two `styles.css` uses: `max-width: 1100px` and `max-width: 760px`. `downloads.css`
 * adds a third at 480px for its own grid; nothing here needs one, because a data table
 * scrolls inside `.table-wrap` rather than reflowing.
 * ========================================================================== */

:root {
  /* The only literal colour this file introduces, and it is a failure colour because
   * nothing else will do. `--blue` is the brand *and* every link and focus affordance on
   * the site, so using it for an error would be indistinguishable from a link; `--muted`
   * and `--navy` both read as "informational". 7.32:1 on white and 6.69:1 on `--pale`, so
   * it carries an error message at both sizes this project uses. */
  --portal-danger: #a3271f;

  /* The focus ring, copied from `styles.css`'s `:focus-visible` rule. It has to be a
   * literal rather than a token because `styles.css` inlines it and cannot be edited;
   * `AGENTS.md` freezes that file. `styles.css` covers `button`, `a` and `summary` only, so
   * the portal has to repeat the colour to give form controls a visible focus state at
   * all. The offset is 2px rather than the 5px that file uses, because a 5px ring on a
   * full-width input doubles the vertical rhythm of every field. */
  --portal-focus: #f2a500;

  /* The one colour keyword this file uses, which `styles.css` uses in three places
   * (`body`'s background, `.button`'s text, `.preview-note`'s text) and never gave a token.
   * A keyword rather than a literal so it is obviously not a new palette decision. Used
   * for text on the two dark badges and for form-control and panel backgrounds. */
  --portal-white: white;
}

/* -----------------------------------------------------------------------------
 * Page shell
 * -------------------------------------------------------------------------- */

.portal-main {
  padding-bottom: 72px;
}

/* A reading measure. `.wrap` is 1320px because it holds a marketing grid; prose and forms
 * do not want 1320px, so the content column is narrower while the gutters stay the same. */
.portal-body {
  max-width: 1040px;
}

.portal-public .portal-body {
  max-width: 660px;
}

.portal-trail {
  padding-top: 26px;
}

.portal-content > * + * {
  margin-top: 26px;
}

.portal-footer {
  flex-wrap: wrap;
}

/* -----------------------------------------------------------------------------
 * Header
 * -------------------------------------------------------------------------- */

.portal-header {
  gap: 22px;
  flex-wrap: wrap;
}

.portal-nav {
  flex-wrap: wrap;
}

/* `changelog.css` has `nav a[aria-current=page]`, but that stylesheet is not linked on a
 * portal page, so the rule is repeated here. It is a duplicate selector, not a change to a
 * frozen file. */
.portal-nav a[aria-current="page"] {
  color: var(--blue);
  font-weight: 700;
}

.portal-account-nav {
  border-left: 1px solid var(--line);
  padding-left: 22px;
  margin-left: 2px;
}

.portal-signout {
  display: flex;
  align-items: center;
}

/* -----------------------------------------------------------------------------
 * Page header
 * -------------------------------------------------------------------------- */

/* The same size as `styles.css`'s `h2`, not its `h1`. The site's `h1` is a 5rem marketing
 * headline with `clamp(3rem, 5.4vw, 5rem)`; a portal page heading is a page heading. Same
 * family, weight, tracking and line-height as the site's `h2` — a different size, not a
 * different type style. Specificity 0,0,1,1 against the bare `h1`, so no `!important`. */
.page-header-page h1 {
  font-size: clamp(2.2rem, 3.4vw, 3.2rem);
  letter-spacing: -0.055em;
  line-height: 1.18;
  font-weight: 700;
}

/* The page's own rule under its heading. Only the page-level header gets it: `pageHeader` is
 * also a *section* header, and a rule and a 52px gap inside a panel is not a section. */
.page-header-page {
  padding: 52px 0 32px;
  border-bottom: 1px solid var(--line);
}

.page-header .eyebrow {
  margin-bottom: 14px;
}

.page-lead {
  font-size: 18px;
  line-height: 1.65;
  color: var(--muted);
  margin-top: 16px;
  max-width: 62ch;
}

.page-actions,
.actions-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
}

.page-actions {
  margin-top: 26px;
}

/* -----------------------------------------------------------------------------
 * Buttons this stylesheet has to add
 * -------------------------------------------------------------------------- */

/* `styles.css` has `.button` and `.button.small`. A portal needs a quiet action beside a
 * committed one, and a third that is a link wearing a button's clothes. */
.button.secondary {
  background: var(--navy);
}

.button.secondary:hover {
  background: var(--ink);
}

.button.ghost {
  background: transparent;
  color: var(--blue);
  box-shadow: inset 0 0 0 1px var(--blue);
}

.button.ghost:hover {
  background: var(--pale);
  transform: none;
}

/* -----------------------------------------------------------------------------
 * Breadcrumbs
 * -------------------------------------------------------------------------- */

.breadcrumb-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 10px;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 13px;
  color: var(--muted);
}

.breadcrumb-entry + .breadcrumb-entry::before {
  content: "/";
  margin-right: 10px;
  color: var(--line);
}

.breadcrumb-link {
  color: var(--blue);
}

.breadcrumb-current {
  font-weight: 700;
  color: var(--ink);
}

/* -----------------------------------------------------------------------------
 * Panels
 * -------------------------------------------------------------------------- */

.panel {
  background: var(--portal-white);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 28px 30px;
}

.panel > :first-child {
  margin-top: 0;
}

.panel-heading {
  font-size: 1.3rem;
  margin-bottom: 16px;
}

/* -----------------------------------------------------------------------------
 * Key/value rows — the shape the pricing summary already uses for `.price-details`
 * -------------------------------------------------------------------------- */

.key-value-list {
  margin: 0;
}

.key-value {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 20px;
  border-bottom: 1px solid var(--line);
  padding: 12px 0;
  font-size: 14px;
}

.key-value:last-child {
  border-bottom: 0;
}

.key-value-label {
  color: var(--muted);
  margin: 0;
}

.key-value-value {
  margin: 0;
  font-weight: 600;
  text-align: right;
  overflow-wrap: anywhere;
}

/* -----------------------------------------------------------------------------
 * Definition lists
 * -------------------------------------------------------------------------- */

.definition-list {
  margin: 0;
}

.definition-item {
  padding: 17px 0;
  border-bottom: 1px solid var(--line);
}

.definition-item:last-child {
  border-bottom: 0;
}

.definition-term {
  font-family: Manrope, sans-serif;
  font-weight: 700;
}

.definition-body {
  margin: 7px 0 0;
}

.definition-description,
.definition-value {
  color: var(--muted);
  font-size: 15px;
  overflow-wrap: anywhere;
}

/* -----------------------------------------------------------------------------
 * Tables
 * -------------------------------------------------------------------------- */

/* The wrapper scrolls horizontally on a narrow screen, which is why there is no 480px
 * breakpoint: a thirteen-column admin table does not reflow, it scrolls. */
.table-wrap {
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow-x: auto;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

.data-table-caption {
  caption-side: top;
  text-align: left;
  padding: 18px 22px;
  font-family: Manrope, sans-serif;
  font-weight: 700;
  font-size: 16px;
  border-bottom: 1px solid var(--line);
}

.data-table th,
.data-table td {
  padding: 14px 22px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line);
}

.data-table thead th {
  background: var(--pale);
  color: var(--muted);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

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

.data-table td {
  overflow-wrap: anywhere;
}

.table-empty {
  color: var(--muted);
  text-align: center;
  padding: 32px 22px;
}

.mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  font-size: 0.94em;
}

/* -----------------------------------------------------------------------------
 * Forms
 * -------------------------------------------------------------------------- */

.field {
  margin-bottom: 22px;
}

.field-label {
  display: block;
  font-weight: 600;
  font-size: 14px;
  margin-bottom: 7px;
}

.field-input {
  display: block;
  width: 100%;
  padding: 13px 14px;
  /* `--muted`, not `--line`. A control's edge is a non-text boundary and WCAG 1.4.11 wants
   * 3:1 for one; `--line` is 1.29:1 on white, which is right for the decorative rules it
   * already draws and not right for the edge of a field a keyboard user has to find.
   * `--muted` is the nearest existing token that clears the bar, at 6.32:1. */
  border: 1px solid var(--muted);
  border-radius: 5px;
  background: var(--portal-white);
  color: var(--ink);
  font: inherit;
  font-size: 15px;
}

.field-input:hover {
  border-color: var(--blue);
}

/* `styles.css`'s focus ring covers `button`, `a` and `summary`. It does not cover form
 * controls, and `TODO.md:439` asks for visible focus — so the ring is repeated here, in the
 * same colour, for the four control types the portal renders. See `--portal-focus`. */
.field-input:focus-visible,
.field-checkbox:focus-visible {
  outline: 3px solid var(--portal-focus);
  outline-offset: 2px;
}

.field-textarea {
  min-height: 112px;
  resize: vertical;
}

.field-hint {
  font-size: 13px;
  color: var(--muted);
  margin-top: 7px;
}

.field-error {
  font-size: 13px;
  font-weight: 600;
  color: var(--portal-danger);
  margin-top: 7px;
}

.field-invalid .field-input {
  border-color: var(--portal-danger);
}

.checkbox-field {
  margin-bottom: 22px;
}

.checkbox-control {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.field-checkbox {
  width: 18px;
  height: 18px;
  margin: 2px 0 0;
  flex-shrink: 0;
  accent-color: var(--blue);
}

.checkbox-label {
  font-size: 15px;
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin-top: 28px;
}

/* -----------------------------------------------------------------------------
 * Badges
 * -------------------------------------------------------------------------- */

.badge {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: 3px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.badge-neutral {
  background: var(--pale);
  color: var(--muted);
}

.badge-info {
  background: var(--navy);
  color: var(--portal-white);
}

.badge-success {
  background: var(--blue);
  color: var(--portal-white);
}

.badge-warning {
  background: var(--pale);
  color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--navy);
}

.badge-error {
  background: var(--pale);
  color: var(--portal-danger);
  box-shadow: inset 0 0 0 1px var(--portal-danger);
}

/* -----------------------------------------------------------------------------
 * Alerts and notices
 * -------------------------------------------------------------------------- */

.alert {
  background: var(--pale);
  border: 1px solid var(--line);
  border-left: 4px solid var(--muted);
  border-radius: 5px;
  padding: 18px 22px;
}

.alert-title {
  font-family: Manrope, sans-serif;
  font-weight: 700;
  margin-bottom: 6px;
}

.alert-message {
  font-size: 15px;
}

.alert-error {
  border-left-color: var(--portal-danger);
}

.alert-success {
  border-left-color: var(--blue);
}

.alert-info {
  border-left-color: var(--navy);
}

.alert-warning {
  border-left-color: var(--muted);
}

/* -----------------------------------------------------------------------------
 * Empty state and the loading placeholder
 * -------------------------------------------------------------------------- */

.empty-state {
  background: var(--pale);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 38px 30px;
  text-align: center;
}

.empty-state-title {
  font-family: Manrope, sans-serif;
  font-weight: 700;
  font-size: 18px;
}

.empty-state-body {
  color: var(--muted);
  font-size: 15px;
  margin-top: 8px;
}

.empty-state-action {
  color: var(--muted);
  font-size: 14px;
  margin-top: 16px;
}

/* Static on purpose. `styles.css` removes transitions globally under
 * `prefers-reduced-motion`, and a pulsing skeleton that cannot be turned off is a motion
 * hazard rather than a loading indicator. The label is the announcement. */
.placeholder {
  background: var(--pale);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 34px 30px;
}

.placeholder-label {
  color: var(--muted);
  font-size: 15px;
}

/* -----------------------------------------------------------------------------
 * max-width: 1100px — the breakpoint public/styles.css uses
 * -------------------------------------------------------------------------- */

@media (max-width: 1100px) {
  .portal-header {
    gap: 16px;
  }

  .portal-nav {
    gap: 18px;
  }

  .portal-account-nav {
    padding-left: 18px;
  }

  .portal-body,
  .portal-public .portal-body {
    max-width: none;
  }
}

/* -----------------------------------------------------------------------------
 * max-width: 760px — the other breakpoint public/styles.css uses
 * -------------------------------------------------------------------------- */

@media (max-width: 760px) {
  .portal-header {
    gap: 14px;
    padding-top: 14px;
  }

  /* One rule per column on a phone. The border was separating two nav groups side by side;
   * stacked, it is just a stray line. */
  .portal-account-nav {
    border-left: 0;
    padding-left: 0;
    margin-left: 0;
  }

  .portal-nav {
    gap: 12px 18px;
    font-size: 13px;
  }

  .portal-trail {
    padding-top: 20px;
  }

  .page-header-page {
    padding: 34px 0 24px;
  }

  .page-lead {
    font-size: 17px;
  }

  .breadcrumb-list {
    font-size: 12px;
  }

  .panel {
    padding: 22px 20px;
  }

  .key-value {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }

  .key-value-value {
    text-align: left;
  }

  .data-table th,
  .data-table td {
    padding: 12px 16px;
  }

  .data-table-caption {
    padding: 15px 16px;
  }

  .empty-state,
  .placeholder {
    padding: 26px 20px;
  }

  /* Stacked, a full-width button is the one control a thumb can reach without aiming. */
  .form-actions,
  .page-actions,
  .actions-row {
    flex-direction: column;
    align-items: stretch;
  }
}