/*
  Portal styling.

  Tokens mirror site/style.css deliberately rather than importing it: the marketing site's
  stylesheet is 1900 lines of animation and layout the portal has no use for, and the two have
  different lifecycles. The values that matter are the ones the contrast suite checks.

  --muted is #8a8a8a, not #555. #555 measures 2.33:1 on --gray and was the cause of 31 of the 45
  contrast failures found on the marketing site. Picking it against the LIGHTEST surface it sits
  on is what makes one value safe everywhere.

  No inline styles anywhere in the portal: the CSP is `default-src 'self'` with no
  'unsafe-inline', which blocks inline style attributes as well as <style> blocks.
*/

:root {
  --black:   #080808;
  --black2:  #0e0e0e;
  --gray:    #1a1a1a;
  --gray2:   #2a2a2a;
  --muted:   #8a8a8a;
  --white:   #ffffff;
  --blue:    #1a56ff;
  --blue-text: #4d7fff;
  --danger:  #ff6b6b;
  --success: #3ddc97;
  --radius:  10px;
}

* { box-sizing: border-box; }

/*
 * `hidden` must actually hide, whatever a class says.
 *
 * The attribute hides an element through the user-agent stylesheet, which ANY author rule setting
 * `display` outranks. `.p-empty { display: grid }` therefore defeated it, so every empty state on
 * every screen of this portal rendered permanently — "No creators yet." sat underneath a list of
 * creators, and "No open opportunities." underneath the pipeline. It reads as a broken page, and it
 * was on every screen at once.
 *
 * The drop portal's reset has carried this line from the beginning; this stylesheet was written
 * later and without it. `!important` is correct here rather than lazy: the whole point is to
 * outrank whatever a component author writes, including a future one.
 */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  background: var(--black);
  color: var(--white);
  font-family: 'Space Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif;
  line-height: 1.55;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--blue);
  color: var(--white);
  padding: 12px 20px;
  z-index: 100;
}
.skip-link:focus { left: 0; }

a { color: var(--blue-text); }
a:hover { color: var(--white); }

/* There were no focus styles at all on the marketing site originally, which makes a page
   unusable by keyboard: you cannot see where you are. :focus-visible rather than :focus so a
   mouse click does not paint a ring. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible {
  outline: 2px solid var(--blue-text);
  outline-offset: 3px;
}

.bar {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 16px 24px;
  background: var(--black2);
  border-bottom: 1px solid var(--gray2);
  flex-wrap: wrap;
}
.bar__brand { font-weight: 700; letter-spacing: 0.08em; }
.bar__brand em { color: var(--blue-text); font-style: normal; }
/*
 * Wraps. The bar itself always did; this did not, so the links sat in one unwrappable row and
 * pushed the page 120px wider than a phone. It survived six links and broke at eight — which is
 * the shape of this bug generally: it arrives with the feature that adds the last item, nowhere
 * near the CSS that causes it.
 *
 * min-width: 0 because a flex item defaults to min-width: auto and refuses to shrink below its
 * content, which is what turns an overflow into a horizontally scrolling page.
 */
.bar__nav {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 18px;
  margin-right: auto;
  min-width: 0;
}
.bar__nav a { text-decoration: none; }
.bar__nav a[aria-current="page"] { color: var(--white); border-bottom: 2px solid var(--blue-text); }

main {
  max-width: 1040px;
  margin: 0 auto;
  padding: 32px 24px 80px;
}
main.centered { text-align: center; padding-top: 96px; }

h1 { font-size: clamp(28px, 4vw, 40px); margin: 0 0 8px; }
h2 { font-size: 20px; margin: 36px 0 12px; }
h3 { font-size: 16px; margin: 24px 0 8px; }
h4 { font-size: 15px; margin: 0 0 4px; }

.lede { color: var(--muted); margin: 0 0 24px; }
.hint { color: var(--muted); font-size: 13px; margin: 4px 0 0; }
.roles { color: var(--muted); font-size: 14px; margin: 0; }
.right { text-align: right; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.btn {
  background: var(--blue);
  color: var(--white);
  border: none;
  border-radius: var(--radius);
  padding: 11px 20px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.btn:hover { background: var(--blue-text); }
.btn--quiet { background: transparent; border: 1px solid var(--gray2); color: var(--white); }
.btn--quiet:hover { background: var(--gray); }
.btn--small { padding: 6px 12px; font-size: 13px; }
.btn--danger { background: transparent; border: 1px solid var(--danger); color: var(--danger); }
.btn--danger:hover { background: var(--danger); color: var(--black); }

.status {
  padding: 12px 16px;
  border-radius: var(--radius);
  background: var(--gray);
  border-left: 3px solid var(--muted);
  margin: 0 0 24px;
}
.status--error { border-left-color: var(--danger); }
.status--success { border-left-color: var(--success); }

.cards { list-style: none; padding: 0; margin: 0; display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.card { background: var(--black2); border: 1px solid var(--gray2); border-radius: var(--radius); padding: 16px; }
.card h3, .card h4 { margin-top: 0; }
.card .btn { margin-top: 12px; }

.table-scroll { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; min-width: 640px; }
.table th, .table td { text-align: left; padding: 12px 10px; border-bottom: 1px solid var(--gray2); vertical-align: top; }
.table th { font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }

.panel { margin-top: 40px; background: var(--black2); border: 1px solid var(--gray2); border-radius: var(--radius); padding: 24px; }

/*
 * A panel that opens. The default disclosure triangle and unstyled summary read as an unstyled
 * fragment of another site when the rest of the page is this dark, which is exactly the
 * "badly formatted elements" complaint. Styled as a control, because that is what it is.
 */
details.panel > summary {
  cursor: pointer;
  font-size: 18px;
  font-weight: 600;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 10px;
}
details.panel > summary::-webkit-details-marker { display: none; }
details.panel > summary::before {
  content: '+';
  display: inline-grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border: 1px solid var(--gray2);
  border-radius: 6px;
  font-weight: 400;
  color: var(--muted);
}
details.panel[open] > summary::before { content: '−'; }
details.panel > summary:focus-visible { outline: 2px solid var(--blue-text); outline-offset: 3px; }
details.panel[open] > summary { margin-bottom: 20px; }

.field { margin-bottom: 18px; border: none; padding: 0; }
.field label, .field legend { display: block; font-size: 14px; font-weight: 600; margin-bottom: 6px; padding: 0; }
/*
 * Every text-like control, not a list of the types somebody happened to use first.
 *
 * This was `input[type="text"], input[type="date"], select`. An `<input>` with no type attribute
 * does not match `[type="text"]`, and neither do password, email, number, month, search or tel —
 * so the account screen rendered its forms as small white boxes on a black page, and so did every
 * number and month field elsewhere in the portal. It looked exactly like a stylesheet that had
 * failed to load.
 *
 * Written as an exclusion so a type nobody has used yet is styled correctly the first time
 * somebody reaches for it, rather than being the next thing to look broken.
 */
.field input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.field select,
.field textarea,
.p-form input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.p-form select,
.p-form textarea {
  width: 100%;
  max-width: 420px;
  background: var(--gray);
  border: 1px solid var(--gray2);
  border-radius: 8px;
  color: var(--white);
  padding: 10px 12px;
  font: inherit;
}

.field input::placeholder, .p-form input::placeholder { color: var(--muted); }

/* A field somebody cannot change should look settled rather than broken. */
.field input:disabled, .p-form input:disabled {
  background: var(--black2);
  color: var(--muted);
  cursor: not-allowed;
}

/* An inline form's controls size to their row rather than to the 420px column. */
.p-form--inline input, .p-form--inline select { max-width: none; }

.checks { display: flex; flex-wrap: wrap; gap: 14px; }
.check { display: flex; align-items: center; gap: 7px; font-size: 14px; font-weight: 400; }
.check input { width: 17px; height: 17px; accent-color: var(--blue); }

.actions { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 20px; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
}

/* ------------------------------------------------------------------ *
 * Pipeline and shared portal surfaces
 *
 * Same shapes as the drop portal's layer — cards, pills, stage positions, labelled metadata — but
 * written against THIS portal's tokens rather than imported. The two sites have different palettes
 * and different build systems; sharing the CSS would mean one of them inheriting colours its own
 * contrast suite has never checked.
 *
 * State never rides on colour alone. Pills carry a word, stages carry a position, blocked rows
 * carry an edge — all of which survive a print, a greyscale screen, and a screenshot in a chat.
 * ------------------------------------------------------------------ */

.p-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px 28px;
  padding-block: 28px 18px;
  border-bottom: 2px solid var(--gray2);
}

.p-head h2 { margin: 0 0 4px; }
.p-head .hint { margin: 0; }

.p-list { display: grid; gap: 14px; list-style: none; padding: 0; margin: 18px 0 0; }

.p-card {
  display: grid;
  gap: 10px;
  padding: 18px;
  border: 1px solid var(--gray2);
  border-radius: var(--radius);
}

/* An edge, not a fill: a wash on this ground is nearly invisible and does not print. */
.p-card[data-flag='blocked'] { border-left: 3px solid var(--danger); }
.p-card[data-flag='waiting'] { border-left: 3px solid var(--blue); }
.p-card[data-flag='done']    { border-left: 3px solid var(--success); }

.p-card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

.p-card__title { margin: 0; font-size: 17px; line-height: 1.3; }

.p-pill {
  display: inline-block;
  padding: 2px 10px;
  border: 1px solid var(--gray2);
  border-radius: 999px;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--muted);
}
.p-pill[data-tone='open']    { border-color: var(--blue);    color: var(--blue-text); }
.p-pill[data-tone='won']     { border-color: var(--success); color: var(--success); }
.p-pill[data-tone='lost']    { border-color: var(--danger);  color: var(--danger); }
.p-pill[data-tone='blocked'] { border-color: var(--danger);  color: var(--danger); }

/*
 * An order's history. Indented under the order it belongs to, with the markers left on, because the
 * order of events is the information — a bulleted list would say these happened, not in what order.
 */
.p-trail { margin: 8px 0 0; padding-left: 20px; list-style: decimal; }
.p-trail li { margin: 2px 0; }

/* Labelled metadata. Bare spans ran values together with no way to tell which was which. */
.p-meta { display: flex; flex-wrap: wrap; gap: 8px 26px; margin: 0; }
.p-meta > div { display: grid; gap: 2px; }
.p-meta dt {
  font-size: 10px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}
.p-meta dd { margin: 0; font-size: 14px; font-variant-numeric: tabular-nums; }

/* Eight stages as a position, with the name printed beside it. A bar alone says how far, not what. */
.p-stage { display: grid; gap: 6px; }
.p-stage__track { display: flex; gap: 3px; }
.p-stage__seg { height: 4px; flex: 1 1 0; background: var(--gray2); border-radius: 1px; }
.p-stage__seg[data-state='done'] { background: var(--blue); }
.p-stage__seg[data-state='here'] { background: var(--blue-text); }
.p-stage__label { display: flex; justify-content: space-between; gap: 16px; font-size: 14px; }
.p-stage__step { font-size: 11px; color: var(--muted); white-space: nowrap; }

/* A blank region reads as broken. Every list says what would be here and how it gets here. */
.p-empty {
  display: grid;
  gap: 6px;
  padding: 40px 24px;
  text-align: center;
  border: 1px dashed var(--gray2);
  border-radius: var(--radius);
  margin-top: 18px;
}
.p-empty strong { font-size: 16px; }
.p-empty span { color: var(--muted); font-size: 14px; }

/*
 * `min-width: 0` on the children, for the same reason the drop portal needs it: grid items are at
 * least as wide as their content, and a <select> is as wide as its longest option. Without it a
 * long decision label makes the whole page scroll sideways on a phone.
 */
.p-form { display: grid; gap: 16px; max-width: 620px; margin-top: 20px; }
.p-form > *, .field { min-width: 0; }
.field input, .field select, .field textarea { max-width: 100%; min-width: 0; }
.field select { text-overflow: ellipsis; }

/*
 * The catalogue editor (brands.html), 2026-09-13.
 *
 * Its fields are `<label class="field">Name <input></label>`: the label IS the field. The rules above
 * style a label INSIDE a .field, so these labels stayed inline and each input wrapped beside the
 * next label ("Name [input] Slug"), which read as every value belonging to the wrong field. A label
 * that is the field stacks its text over its control.
 */
label.field { display: grid; gap: 6px; font-size: 14px; font-weight: 600; }
label.field > input, label.field > select, label.field > textarea { font-weight: 400; }
#product-form { max-width: 980px; }
.p-columns { display: grid; gap: 8px 32px; }
@media (min-width: 900px) { .p-columns { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }

/* Widths that used to be inline style attributes, which the CSP's style-src refuses. */
.p-form .w-price { max-width: 140px; }
.p-form .w-currency { max-width: 80px; }
.p-form .w-run { max-width: 100px; }
.actions--tight { margin-top: 0; margin-bottom: 12px; }
.p-preview { object-fit: cover; background: var(--gray); border-radius: 4px; }
/* A form that sits inside a card, beside the record it acts on, rather than standing on its own. */
.p-form--inline { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 10px; max-width: none; }
/* A label and its control on one line, for a filter that sits in a section header. */
.field--inline { display: flex; align-items: center; gap: 10px; }
.field--inline > span { color: var(--muted); font-size: 14px; white-space: nowrap; }
/* An audit entry is one line of fact, not a record with sections. */
.p-card--tight { gap: 6px; }
.p-card--tight .hint { margin: 0; }
.p-card--tight time { font-variant-numeric: tabular-nums; }
.p-form--inline input { flex: 1 1 200px; min-width: 0; }
.p-card__sub { font-size: 13px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); margin: 16px 0 6px; }
.p-actions { display: flex; flex-wrap: wrap; gap: 12px; margin: 0; }

/* ── Account, panels and the header's account link ─────────────────────────────
 *
 * The portal had no visual grouping above the level of a card, so every screen was a flat stack of
 * headings and lists in one column with a great deal of dark space below it. A panel is the missing
 * middle: a bounded region that says "these things belong together", which is what makes a form
 * feel like part of a page rather than adrift on it.
 */
.p-panel {
  border: 1px solid var(--gray2);
  border-radius: var(--radius);
  background: var(--black2);
  padding: 24px;
  margin-top: 24px;
}
.p-panel > h2 { margin: 0 0 6px; font-size: 18px; }
.p-panel > .hint:first-of-type { margin-top: 0; }
.p-panel .p-form { margin-top: 18px; }

/* Two panels side by side where there is room, stacked where there is not. */
.p-columns { display: grid; gap: 24px; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
.p-columns > .p-panel { margin-top: 24px; }

.bar__account {
  margin-left: auto;
  margin-right: 14px;
  font-size: 14px;
  color: var(--white);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
}
.bar__account:hover { border-bottom-color: var(--gray2); }
.bar__account[aria-current="page"] { border-bottom-color: var(--blue-text); }

/*
 * A card that is a link, without looking like one.
 *
 * The home page rendered each line of "what needs you" as a bare <a>, so a statement of calm —
 * "Nothing is waiting on your signature." — arrived underlined and blue, indistinguishable from a
 * warning and from every other link on the page. A whole card is the target now, the text is the
 * text, and only the count carries emphasis.
 */
.p-card--action { padding: 0; }
.p-card--action > a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 18px;
  color: inherit;
  text-decoration: none;
}
.p-card--action > a:hover { background: var(--gray); }
.p-card--action > a::after { content: "\203A"; color: var(--muted); font-size: 20px; line-height: 1; }
.p-card--action[data-flag="blocked"] > a { color: var(--white); }

/* A line that reports calm rather than offering an action. Not a link, and not styled as one. */
.p-quiet {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 18px;
  color: var(--muted);
  font-size: 14px;
  border: 1px solid var(--gray2);
  border-radius: var(--radius);
}
.p-quiet::before { content: "\2713"; color: var(--success); font-weight: 700; }

/* A button is a button. Underlined text inside one reads as a link that lost its way. */
.btn, .btn:hover, .btn:focus { text-decoration: none; }

/* ---- Money (WF09) ------------------------------------------------------
 *
 * Three headline figures and two list decorations. The figures carry a note each because a number
 * with no sentence beside it gets read as whatever the reader already believed — "owed" in
 * particular means nothing until you say owed under what.
 */

.p-figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
  margin: 20px 0 8px;
}

.p-figure {
  display: grid;
  gap: 4px;
  align-content: start;
  padding: 18px;
  border: 1px solid var(--gray2);
  border-radius: var(--radius);
  /* Grid items default to min-width:auto; a long currency string would otherwise force the track. */
  min-width: 0;
}

.p-figure__value {
  font-size: 1.7rem;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.p-figure__label {
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.p-figure__note { font-size: 0.85rem; color: var(--muted); }

/* The amount sits opposite the title in a card head, and lines up down the list. */
.p-card__figure {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  font-size: 1.05rem;
}

/* Approver slots: one line each, filled or not, so the wait has a visible shape. */
.p-gates { display: grid; gap: 6px; list-style: none; padding: 0; margin: 0; }

.p-gate {
  font-size: 0.9rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* ---- Search ------------------------------------------------------------ */

.bar__search {
  margin-left: auto;
  font-size: 0.9rem;
  color: var(--muted);
}
.bar__search:hover, .bar__search:focus-visible { color: var(--white); }

/* The account link previously claimed the auto margin; search now sits before it. */
.bar__search:not([hidden]) + .bar__account { margin-left: 16px; }

.p-search { display: grid; gap: 8px; margin: 20px 0 12px; max-width: 560px; }

.p-search__row { display: flex; gap: 10px; }

/* Grid and flex items default to min-width:auto, which is how a long value forces a 4000px track. */
.p-search__row input { flex: 1 1 auto; min-width: 0; }
