/* Customer-portal submission-journey surface (#467 / #472).

   Self-contained — every selector is portal-prefixed so it cannot bleed into
   the operator workspace (.tool-ws-grid / .page-card in main.css stay
   untouched). Design tokens only; see rules/design-system.md.

   NOTE(#472): the portal grid is intentionally separate from L2's unshipped
   shared page_grid.html. Consolidation is parked in #472.

   Composes on core primitives — alert, tag, card, empty_state, button.
   New variants added here:
     · .progress-meter  — segmented status bar + legend (document detail)
     · .doc-row         — flat list-of-requirements row (overview)
     · .tag--pending    — pulsing-dot info tag for "Under review"
     · .page-card--{status}   — status-aware page card variants
     · .upload-inline / .dropzone — visual variants of upload_form
     · .hint-disclose / .attempt-bar — small layout helpers
*/

/* ── Reset old `.portal-status` pill — kept as deprecated shim so any
   stray template (page_modal.html) still renders while it moves to the `tag`
   macro. New code uses `tag` instead. */

.portal-status {
  display: inline-block;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--font-xs);
  font-weight: var(--weight-medium);
  letter-spacing: 0.01em;
  background: var(--color-surface-muted);
  color: var(--color-subtle);
  border: 1px solid transparent;
}
.portal-status--accepted {
  background: color-mix(in srgb, var(--color-success) 12%, transparent);
  color: var(--color-success);
}
.portal-status--needs-redo {
  background: color-mix(in srgb, var(--color-danger) 12%, transparent);
  color: var(--color-danger);
}
.portal-status--new-copy,
.portal-status--under-review,
.portal-status--pending-review {
  background: color-mix(in srgb, var(--color-info) 12%, transparent);
  color: var(--color-info);
}
.portal-status--missing {
  background: transparent;
  color: var(--color-warning-text);
  border-color: color-mix(in srgb, var(--color-warning) 35%, transparent);
}
.portal-status--superseded,
.portal-status--old-copy,
.portal-status--dismissed {
  background: transparent;
  color: var(--color-muted);
}

/* ─────────────────────────────────────────────────────────────────────
   Progress meter — NEW primitive
   ───────────────────────────────────────────────────────────────────── */

.progress-meter {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}

.progress-meter__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.progress-meter__title {
  font-size: 1.25rem;
  font-weight: var(--weight-semibold);
  color: var(--color-text-strong);
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.progress-meter__title strong {
  color: var(--color-success);
  font-weight: var(--weight-semibold);
}

.progress-meter__pct {
  font-size: var(--font-xs);
  color: var(--color-text);
}

.progress-meter__bar {
  display: flex;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--color-surface-muted);
  overflow: hidden;
  gap: 2px;
}

.progress-meter__seg { height: 100%; }
.progress-meter__seg--success { background: var(--color-success); }
.progress-meter__seg--info    { background: var(--color-info); }
.progress-meter__seg--danger  { background: var(--color-danger); }
.progress-meter__seg--warning { background: var(--color-warning); }

.progress-meter__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  font-size: var(--font-xs);
  color: var(--color-text);
}

.progress-meter__legend span {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

/* The dot carries the tone; the words are body text (#1404). */
.progress-meter__legend i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.progress-meter__legend .l-success i { color: var(--color-success); }
.progress-meter__legend .l-info i    { color: var(--color-info); }
.progress-meter__legend .l-danger i  { color: var(--color-danger); }
.progress-meter__legend .l-warning i { color: var(--color-warning); }

.progress-meter__legend strong {
  color: var(--color-text);
  font-weight: var(--weight-normal);
  margin-right: 2px;
}

/* ─────────────────────────────────────────────────────────────────────
   Tag — pending tone (pulse) — adds to the canonical Tag family
   ───────────────────────────────────────────────────────────────────── */

.tag--pending {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: color-mix(in srgb, var(--color-info) 12%, transparent);
  color: var(--color-info);
}
.tag--pending::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  animation: portal-pulse 1.6s ease-in-out infinite;
}
@keyframes portal-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}

/* ─────────────────────────────────────────────────────────────────────
   Doc-rows (overview list) — flat row rhythm, mirrors team-side
   .clickable-row in main.css
   ───────────────────────────────────────────────────────────────────── */

.doc-rows { list-style: none; margin: 0; padding: 0; }

.doc-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  column-gap: var(--space-4);
  padding: var(--space-4);
  border-bottom: 1px solid var(--color-border-subtle);
  transition: background var(--transition-fast);
}
.doc-row:last-child { border-bottom: 0; }
.doc-row:hover     { background: var(--color-surface-dim); }

.doc-row--focused {
  background: var(--color-surface-dim);
  box-shadow: var(--shadow-focus);
}

.doc-row__heading {
  min-width: 0;
  text-decoration: none;
  color: inherit;
  display: block;
}

.doc-row__label {
  display: block;
  color: var(--color-text);
}
.doc-row__heading:hover .doc-row__label { color: var(--color-accent); }

.doc-row__sub {
  font-size: var(--font-xs);
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
}

.doc-row__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
}

.doc-row__upload { grid-column: 1 / -1; margin-top: var(--space-3); }
.doc-row__upload > summary {
  cursor: pointer;
  font-size: var(--font-sm);
  color: var(--color-accent);
  list-style: none;
  display: inline-flex;
  align-items: center;
}
.doc-row__upload > summary::-webkit-details-marker { display: none; }

.doc-row__next {
  grid-column: 1 / -1;
  margin: var(--space-2) 0 0;
  font-size: var(--font-xs);
  color: var(--color-text);
}

/* ─────────────────────────────────────────────────────────────────────
   Upload — inline (row-folded) + dropzone (top-of-detail) variants
   Both are visual shells around the existing upload_form macro;
   the form, the hidden `requirement_hint`, and the POST target are
   unchanged.
   ───────────────────────────────────────────────────────────────────── */

.upload-inline {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface-dim);
}

.upload-inline__picker {
  flex: 1;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--font-sm);
  color: var(--color-subtle);
  cursor: pointer;
  min-width: 0;
}

.upload-inline__picker svg {
  width: 18px;
  height: 18px;
  color: var(--color-muted);
  flex-shrink: 0;
}

.upload-inline__picker em {
  font-style: normal;
  color: var(--color-text);
  font-weight: var(--weight-medium);
}

.upload-inline__input {
  /* Visually hidden; clicking the label triggers the picker */
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip: rect(0 0 0 0);
  overflow: hidden;
}

.dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-6);
  text-align: center;
  border: 1.5px dashed color-mix(in srgb, var(--color-accent) 35%, var(--color-border));
  background: color-mix(in srgb, var(--color-accent) 4%, var(--color-surface));
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: border-color var(--transition-normal), background var(--transition-normal);
}

.dropzone:hover {
  border-color: var(--color-accent);
  background: color-mix(in srgb, var(--color-accent) 7%, var(--color-surface));
}

.dropzone__icon {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--color-accent) 12%, transparent);
  color: var(--color-accent);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.dropzone__icon svg { width: 20px; height: 20px; }

.dropzone__title {
  font-size: var(--font-base);
  font-weight: var(--weight-semibold);
  color: var(--color-text-strong);
}

.dropzone__title em {
  color: var(--color-accent);
  font-style: normal;
}

.dropzone__hint {
  font-size: var(--font-sm);
  color: var(--color-muted);
}

.dropzone--compact {
  flex-direction: row;
  text-align: left;
  padding: var(--space-4) var(--space-5);
}
.dropzone--compact .dropzone__icon { width: 32px; height: 32px; }
.dropzone--compact .dropzone__icon svg { width: 16px; height: 16px; }
.dropzone--compact .dropzone__title { flex: 1; }

.dropzone__input {
  /* Visually hidden — the whole dropzone acts as the click target. */
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip: rect(0 0 0 0);
  overflow: hidden;
}

/* ─────────────────────────────────────────────────────────────────────
   Upload hint fieldset (radio over open rows + "I'm not sure")
   Used in the bottom-of-overview disclosure.
   ───────────────────────────────────────────────────────────────────── */

.portal-upload__hint {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  background: var(--color-surface);
  margin-bottom: var(--space-4);
}

.portal-upload__hint > legend {
  padding: 0 var(--space-2);
  font-size: var(--font-sm);
  font-weight: var(--weight-semibold);
  color: var(--color-text-strong);
}

.portal-upload__hint-opt {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--font-sm);
  cursor: pointer;
  margin-top: 2px;
}
.portal-upload__hint-opt:hover { background: var(--color-hover); }
.portal-upload__hint-opt input { accent-color: var(--color-accent); }
.portal-upload__hint-opt--unsure {
  color: var(--color-subtle);
  font-style: italic;
}

.portal-upload__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-3);
  font-size: var(--font-sm);
}

/* Bottom-of-overview disclosure that hides the open_rows uploader */
.hint-disclose {
  margin-top: var(--space-6);
}

.hint-disclose > summary {
  cursor: pointer;
  padding: var(--space-3) 0;
  font-size: var(--font-sm);
  color: var(--color-subtle);
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.hint-disclose > summary::-webkit-details-marker { display: none; }
.hint-disclose > summary::before {
  content: "›";
  transition: transform var(--transition-normal);
  font-weight: var(--weight-bold);
}
.hint-disclose[open] > summary::before { transform: rotate(90deg); }
.hint-disclose > summary:hover { color: var(--color-accent); }

/* ─────────────────────────────────────────────────────────────────────
   Section headers inside detail surface
   ───────────────────────────────────────────────────────────────────── */

.portal-section {
  margin-top: var(--space-6);
}

.portal-section__head {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.portal-section__head h2 {
  font-size: var(--font-base);
  font-weight: var(--weight-semibold);
  color: var(--color-text-strong);
  letter-spacing: 0;
}

.portal-section__head .meta {
  font-size: var(--font-xs);
  color: var(--color-text);
}

/* Attempt bar header above the previous-attempts scroller */
.attempt-bar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.attempt-bar__label {
  font-size: var(--font-xs);
  font-weight: var(--weight-semibold);
  color: var(--color-text-strong);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.attempt-bar__meta {
  font-size: var(--font-xs);
  color: var(--color-muted);
}

.attempt-bar__sep {
  flex: 1;
  height: 1px;
  background: var(--color-border);
}

/* ─────────────────────────────────────────────────────────────────────
   Page grid + page card — detail surface
   ───────────────────────────────────────────────────────────────────── */

.portal-doc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--space-4);
}

.portal-doc-grid--scroller {
  display: flex;
  grid-template-columns: none;
  gap: var(--space-3);
  overflow-x: auto;
  padding-bottom: var(--space-2);
  scroll-snap-type: x mandatory;
}
.portal-doc-grid--scroller .portal-page-card {
  flex: 0 0 140px;
  scroll-snap-align: start;
}

/* The redo alert's reason is the composed redo message (intro + bullet
   lines) — keep the bullets on their own lines under the bold title. */
.portal-doc-grid__reason {
  white-space: pre-line;
}

.portal-doc-grid__empty {
  grid-column: 1 / -1;
  color: var(--color-muted);
}

.portal-doc-grid--muted .portal-page-card { opacity: 0.7; }

.portal-page-card {
  position: relative;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: border-color var(--transition-normal),
              box-shadow var(--transition-normal);
}

.portal-page-card__thumb {
  position: relative;
  display: block;
  aspect-ratio: 3 / 4;
  background: var(--color-surface-muted);
  overflow: hidden;
}

.portal-page-card__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.portal-page-card__thumb--missing {
  background: var(--color-surface-dim);
  border-bottom: 1px dashed var(--color-border);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-muted);
  flex-direction: column;
  gap: var(--space-2);
  text-align: center;
  padding: var(--space-4);
  font-size: var(--font-xs);
}

.portal-page-card__thumb--missing svg {
  width: 28px;
  height: 28px;
  opacity: 0.6;
}

/* An upload the arq worker is still decoding (#1129) — the team's Processing
   card shimmer (main.css `.page-card-thumb .page-card-processing`), filling the
   thumb. Replaced by the image when docs_page.derivatives_ready refetches the
   grid. */
.portal-page-card__thumb--processing {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-muted);
  font-size: var(--font-xs);
  background: linear-gradient(
    90deg,
    var(--color-surface-dim) 25%,
    var(--color-surface-muted) 50%,
    var(--color-surface-dim) 75%
  );
  background-size: 200% 100%;
  animation: shimmer 1.5s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .portal-page-card__thumb--processing {
    animation: none;
  }
}

/* The decode gave up: the missing card's layout, in the redo's danger tone —
   this one is the couple's to act on (upload again). */
.portal-page-card__thumb--failed {
  color: var(--color-danger);
}

.portal-page-card--decode-failed {
  border-color: color-mix(in srgb, var(--color-danger) 45%, transparent);
}

.portal-page-card__body {
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  flex: 1;
}

.portal-page-card__num {
  font-size: var(--font-xs);
  color: var(--color-text);
}

.portal-page-card__reason {
  margin: 0;
  font-size: var(--font-xs);
  color: var(--color-danger);
  line-height: 1.4;
  /* The reason is the composed redo message — intro line + bullet lines. */
  white-space: pre-line;
  padding: var(--space-2) var(--space-3);
  background: color-mix(in srgb, var(--color-danger) 8%, transparent);
  border-radius: var(--radius-xs);
}

/* A missing slot's description is a request, not a fault (#1098) — so it takes
   the card's own `missing` warning tone rather than the redo reason's danger
   red, which would read to the couple as something they got wrong. The label
   above it is what stops the operator's noun phrase landing unattributed. */
.portal-page-card--missing .portal-page-card__reason {
  color: var(--color-warning-text);
  background: color-mix(in srgb, var(--color-warning) 10%, transparent);
}

.portal-page-card__reason-label {
  margin: 0 0 var(--space-1);
  font-size: var(--font-xs);
  color: var(--color-text);
}

.portal-page-card__action {
  margin-top: var(--space-1);
  width: 100%;
  justify-content: center;
}

/* Status variants — class name matches what the template emits from
   `slot.status.value | replace('_','-')`. Existing classes still apply. */

.portal-page-card--accepted .portal-page-card__thumb::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    transparent 65%,
    color-mix(in srgb, var(--color-success) 25%, transparent) 100%
  );
  pointer-events: none;
}

.portal-page-card__badge {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  background: var(--color-success);
  color: var(--color-surface);
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-md);
}
.portal-page-card__badge svg { width: 12px; height: 12px; }

.portal-page-card--pending-review,
.portal-page-card--under-review { position: relative; }
.portal-page-card--pending-review::after,
.portal-page-card--under-review::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius-md);
  border: 1.5px solid color-mix(in srgb, var(--color-info) 35%, transparent);
  pointer-events: none;
}

.portal-page-card--needs-redo {
  border-color: color-mix(in srgb, var(--color-danger) 45%, transparent);
}
.portal-page-card--needs-redo .portal-page-card__thumb::after {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    135deg,
    transparent 0 8px,
    color-mix(in srgb, var(--color-danger) 8%, transparent) 8px 10px
  );
  pointer-events: none;
}

.portal-page-card--missing { border-style: dashed; }

/* An old copy (#1365) — core/page_status.py couple_statuses — is faded like
   a replaced page. */
.portal-page-card--superseded,
.portal-page-card--old-copy {
  opacity: 0.75;
}
.portal-page-card--superseded .portal-page-card__thumb img,
.portal-page-card--old-copy .portal-page-card__thumb img {
  filter: grayscale(0.6);
}
.portal-page-card--superseded .portal-page-card__reason {
  background: var(--color-surface-muted);
  color: var(--color-subtle);
}

/* ─────────────────────────────────────────────────────────────────────
   Page modal — left as-is, just remove old wrappers
   ───────────────────────────────────────────────────────────────────── */

.portal-page-modal__reason {
  color: var(--color-danger);
  margin-bottom: var(--space-5);
  white-space: pre-line;
}

/* Same tone split as the card's reason (#1098): a missing description is a
   request, not a fault, so it must not inherit the redo red this head was
   built for. */
.portal-page-modal__reason--missing {
  color: var(--color-warning-text);
}

/* ─────────────────────────────────────────────────────────────────────
   Checklist + Messages (#1026)

   The couple's landing page carries both halves of what they need on
   arrival: the requirement rows (what they owe us) and the conversation
   with their case manager (how to reach us). Two columns above the
   breakpoint, stacked below it with the checklist first — the list is
   why they came; the panel is how they ask about it.
   ───────────────────────────────────────────────────────────────────── */

.portal-checklist-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 22rem);
  gap: var(--space-6);
  align-items: start;
}

.portal-checklist-layout--single {
  grid-template-columns: minmax(0, 1fr);
}

.portal-messages {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  /* Sticks beside a long checklist so the conversation stays reachable
     without scrolling back up. */
  position: sticky;
  top: var(--space-5);
}

.portal-messages__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.portal-messages__title {
  font-size: var(--font-base);
  font-weight: var(--weight-semibold);
  margin: 0;
}

.portal-messages__manager {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.portal-messages__avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--radius-pill);
  background: var(--color-accent-50);
  color: var(--color-accent);
  font-size: var(--font-xxs);
  font-weight: var(--weight-semibold);
  flex-shrink: 0;
}

/* The label over the name and the name are both body text (#1404). */
.portal-messages__manager-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  font-size: var(--font-xs);
  color: var(--color-text);
}

/* Segmented filter — the shared mechanism (comments.js), the couple's two
   chips. Styled here rather than reusing `.case-timeline-seg`, which is a
   team-desk toolbar control sized for the case header. */
.portal-messages__filter {
  display: flex;
  gap: var(--space-1);
}

.portal-messages__filter .timeline-filter__btn {
  flex: 1;
  padding: var(--space-1) var(--space-2);
  /* --font-xs, not the --font-xxs the avatar and eyebrow use: these are
     tappable controls, and 9.6px is below any floor for a couple on a phone.
     12px is also what the chips rendered at while the old name fell away. */
  font-size: var(--font-xs);
  color: var(--color-muted);
  background: var(--color-surface-muted);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.portal-messages__filter .timeline-filter__btn:hover {
  color: var(--color-text);
}

.portal-messages__filter .timeline-filter__btn[aria-pressed="true"] {
  background: var(--color-surface);
  border-color: var(--color-border);
  color: var(--color-text);
  font-weight: var(--weight-medium);
}

/* The feed scrolls inside the panel: the conversation grows without pushing
   the checklist's own scroll position around.

   The rail is built for a panel that scrolls its own `.timeline-entries`
   (`main.css`: `.page-timeline .timeline-entries { overflow-y: auto }`). Two
   nested scrollers means "scroll to the newest message" has two answers and
   picks the wrong one, so the inner scroll is released here and this element
   is the single scroll container. */
.portal-messages__body {
  max-height: 26rem;
  overflow-y: auto;
}

.portal-messages__body .timeline-entries {
  overflow-y: visible;
}

.portal-messages__composer {
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-2);
}

@media (max-width: 900px) {
  .portal-checklist-layout {
    grid-template-columns: minmax(0, 1fr);
  }
  .portal-messages {
    position: static;
  }
  .portal-messages__body {
    max-height: 20rem;
  }
}

/* Page thread (#497) — the shared rail component (main.css) mounted below the
   upload section. The rail is built for a scrolling panel; here it flows with
   the page, so the feed loses its own scroll and the section takes the
   spacing. The composer keeps its top rule as the seam between feed and
   reply box. */
.portal-page-thread {
  margin-top: var(--space-8);
}

.portal-page-thread__timeline .timeline-entries {
  overflow-y: visible;
}

.portal-page-thread__timeline .page-composer {
  margin-top: var(--space-4);
}

/* The audience chip ("Shared" / "Internal note") tells a team reader which side
   a message is on. On the couple's side every message is shared, so the chip
   says nothing — hidden here rather than branching the shared partial. The
   redo / resolved / alternative chips are their own modifiers and stay.

   Keyed on `portal-thread__timeline`, which BOTH couple-facing threads carry
   (the page modal's and the Messages panel's) — one rule, not one per surface,
   because the reason is the audience and not the surface. */
.portal-thread__timeline .rail-meta-chip--audience {
  display: none;
}

/* ─────────────────────────────────────────────────────────────────────
   The conversation, narrowed (#1370) — the Drive's right pane and the
   Messages page. One conversation (the Messages panel's markup), drawn on
   the portal's page grey with no white panel of its own, so its white cards
   stand out by their borders the way Journey's do (V1-DriveC2, V1-Messages).
   Spacing per the canvas: 8 px under the title, 16 px between blocks, no
   divider above the composer.
   ───────────────────────────────────────────────────────────────────── */

.portal-conversation {
  gap: var(--space-5);
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  position: static;
}

.portal-conversation .portal-messages__head {
  gap: var(--space-3);
}

.portal-conversation .portal-messages__body {
  max-height: none;
  overflow-y: visible;
}

.portal-conversation .portal-messages__composer {
  border-top: 0;
  padding-top: 0;
}

/* The page font on every control in the shell. A browser draws a button, an
   input, a select and a textarea in its own face (Arial) unless told
   otherwise, and a shared class (a Drive chip, a thread filter, the
   composer's ×) cannot know it is on the portal: one rule here, rather than
   one per class as each turns up. `:where` keeps it at the shell's own
   specificity, so a component that names a family still wins. */
.portal-shell :where(button, input, select, textarea) {
  font-family: inherit;
}

.portal-conversation__narrow {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* A document's name can be long; the buttons size to their words and wrap
   rather than splitting the row into equal, clipped thirds. */
.portal-conversation .portal-messages__filter {
  flex-wrap: wrap;
}

.portal-conversation .portal-messages__filter .timeline-filter__btn {
  flex: 0 1 auto;
}

.portal-conversation__count {
  margin: 0;
  font-size: var(--font-xs);
  color: var(--color-text);
}

/* Narrowing hides what is not about the subject in view (comments.js). A
   combined redo goes with its last visible line. */
[data-conversation] [data-about].is-narrowed-out,
[data-conversation] .rail-redo-group.is-narrowed-out {
  display: none;
}

/* The Drive's right pane, as the couple's conversation (`CUSTOMER_PANE`,
   core/drive/audiences.py): the selected file's preview, then the
   conversation, scrolling as one column on the page grey. */
.drive-inspector--conversation {
  overflow-y: auto;
  gap: var(--space-5);
  padding: var(--space-5);
  background: var(--color-team-surface);
}

.drive-inspector--conversation .drive-inspector__content {
  flex: 0 0 auto;
  overflow: visible;
  padding: 0;
}

.drive-inspector--conversation .drive-inspector__content:empty {
  display: none;
}

/* The selected file's preview, then its name and one line (V1-DriveC2): a
   fixed 240 px stage rather than the team pane's 3:4 page, so the
   conversation under it stays in view. */
.drive-inspector--conversation .drive-inspector__preview {
  aspect-ratio: auto;
  height: 15rem;
  margin-bottom: var(--space-3);
}

.drive-inspector--conversation .drive-inspector__title {
  margin: 0;
}

.drive-inspector--conversation .drive-inspector__subtitle {
  margin: var(--space-1) 0 0;
}

.drive-inspector--conversation .portal-conversation {
  flex: 1 0 auto;
}

/* The Messages page: the subjects on the left in the Drive's folder rows, the
   conversation in view on the right. Two panes that scroll on their own, the
   Drive's frame without its tree. */
.portal-conversations {
  display: flex;
  align-items: stretch;
  height: 100%;
  min-height: 0;
  overflow: hidden;
  background: var(--color-surface);
}

.portal-conversations__subjects {
  flex: 0 0 17rem;
  min-height: 0;
  overflow-y: auto;
  border-right: 1px solid var(--color-border-subtle);
}

.portal-conversations__subjects .drive-folders {
  padding: var(--space-5);
}

.portal-conversations__thread {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-5);
  background: var(--color-team-surface);
}

.portal-conversations__thread .portal-conversation {
  max-width: 45rem;
}

.portal-previous {
  margin-top: var(--space-6);
}

.portal-previous summary {
  cursor: pointer;
  color: var(--color-muted);
  margin-bottom: var(--space-4);
}

/* ─────────────────────────────────────────────────────────────────────
   Mobile (Discipline-gate-3) — keep the existing breakpoints
   ───────────────────────────────────────────────────────────────────── */

@media (max-width: 540px) {
  .portal-doc-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .progress-meter__top { flex-direction: column; align-items: flex-start; gap: var(--space-1); }
  .doc-row { grid-template-columns: 1fr; }
  .doc-row__actions { grid-column: 1 / -1; justify-content: flex-start; }
  .upload-inline { flex-wrap: wrap; }
  .dropzone { padding: var(--space-5); }
  /* The page view used to become a fixed full-screen overlay here, which
     covered the phone's top bar and its menu. Since #1371 it sits under the
     bars like every other screen (the phone section below). */
}

@media (max-width: 360px) {
  /* A working list beats a crammed grid — keep the existing fallback. */
  .portal-doc-grid:not(.portal-doc-grid--scroller) {
    display: flex;
    flex-direction: column;
  }
  .portal-doc-grid:not(.portal-doc-grid--scroller) .portal-page-card {
    flex-direction: row;
    align-items: stretch;
  }
  .portal-doc-grid:not(.portal-doc-grid--scroller) .portal-page-card__thumb {
    width: 30%;
    flex-shrink: 0;
    aspect-ratio: auto;
    border-bottom: none;
    border-right: 1px solid var(--color-border);
  }
}


/* ─────────────────────────────────────────────────────────────────────
   Journey — the couple's landing page — and the journey strip (#1369)

   Journey sets the step list beside the case cards, so it widens the
   shell's reading column. The strip is the shared stage bar, flat, in the
   page gutter above the Drive: its first dot lines up with the title.
   ───────────────────────────────────────────────────────────────────── */

.portal-shell.portal-journey-page .container {
  max-width: 1080px;
}

.portal-journey {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  align-items: flex-start;
  margin-top: var(--space-5);
}

.portal-journey__steps {
  flex: 999 1 560px;
  min-width: 0;
  margin: 0;
}

.portal-journey__aside {
  flex: 1 1 280px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.portal-journey__aside .portal-journey__card {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.portal-journey__write {
  align-self: flex-start;
}

.portal-journey__needs-count {
  margin: var(--space-3) 0 0;
  font-size: var(--font-xs);
  color: var(--color-text);
}

.portal-journey__needs {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  overflow: hidden;
}

.portal-journey__needs li + li {
  border-top: 1px solid var(--color-border-subtle);
}

.portal-journey__need {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 48px;
  padding: var(--space-2) var(--space-4);
  color: var(--color-text);
  text-decoration: none;
  transition: background var(--transition-fast);
}

.portal-journey__need:hover {
  background: var(--color-hover);
  color: var(--color-text);
}

.portal-journey__need-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.portal-journey__need-text {
  font-size: var(--font-xs);
}

.portal-journey__need-chev {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--color-muted);
}

.portal-journey__facts {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin: 0;
}

.portal-journey__facts div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
}

/* Label beside value, both body text, as the team's definition lists
   (`.status-dl`) read (#1404). */
.portal-journey__facts dt,
.portal-journey__facts dd {
  font-size: var(--font-xs);
  color: var(--color-text);
}

.portal-journey__facts dd {
  margin: 0;
  text-align: right;
}

/* The Journey card's sharing line (#1367) — whose documents this party shares,
   linking to where the answer changes. */
.portal-journey__sharing {
  margin: var(--space-3) 0 0;
  font-size: var(--font-xs);
  color: var(--color-text);
}

.portal-journey__sharing a {
  color: var(--color-accent);
}

/* ── Sharing with the partner (#1367) ──
   /portal/cases/{id}/sharing — one card for the viewer's own answer, one for
   the partner's, then the way on to the case. */
.portal-sharing {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.portal-sharing__intro {
  margin-bottom: var(--space-4);
}

@media (max-width: 640px) {
  .portal-sharing-page .portal-sharing__intro,
  .portal-sharing-page .portal-sharing,
  .portal-sharing-page .portal-sharing__continue {
    margin-left: var(--space-4);
    margin-right: var(--space-4);
  }
}

.portal-sharing__title {
  margin: 0 0 var(--space-2);
  font-size: var(--font-lg);
  font-weight: var(--weight-semibold);
}

.portal-sharing__intro p {
  margin: 0;
  color: var(--color-text);
}

.portal-sharing__ask {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: 420px; /* layout width: one email address, not the card's width */
}

/* The address and its button on one row; the field takes what is left. */
.portal-sharing__ask-row {
  display: flex;
  gap: var(--space-2);
}

.portal-sharing__ask-row .btn {
  flex-shrink: 0;
}

.portal-sharing__head {
  margin: 0;
  font-size: var(--font-base);
  font-weight: var(--weight-semibold);
}

.portal-sharing__state,
.portal-sharing__sent {
  margin: 0;
  font-size: var(--font-xs);
  color: var(--color-text);
}

.portal-sharing__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.portal-sharing__continue {
  margin: 0;
}

.portal-journey-strip {
  flex: 0 0 auto;
  padding: var(--space-4) var(--space-ws-gutter);
  border-bottom: 1px solid var(--color-border-subtle);
}

/* ─────────────────────────────────────────────────────────────────────
   One body style (PV-24 1b, #1404)

   The team app's body-text rule holds on the portal too: every run that is
   not a heading, a label or a control reads at --font-xs, normal weight, the
   one line height and --color-text (.claude/skills/ux/rules/typography.md).
   The portal's own classes carry it where they are defined; the rules below
   are for the shared components the portal mounts — the Drive's rows, the
   conversation's meta line, the page thread's empty state, the alert, the
   back link — scoped to the portal shell so the team's copies do not move.
   ───────────────────────────────────────────────────────────────────── */

/* Nothing set a line height on the portal's content, so every run that does
   not set its own took the browser's "normal" (the team's has this since
   /ux body-text-style, main.css `.team-shell .main-content`). */
.portal-shell .main-content {
  line-height: var(--line-height-normal);
}

.portal-shell .back-link {
  font-size: var(--font-xs);
}

.portal-shell .main-content .alert {
  font-size: var(--font-xs);
  line-height: var(--line-height-normal);
}

.portal-shell .main-content .alert strong {
  font-weight: var(--weight-normal);
}

/* The Drive's rail and the Messages subjects: a row's name and its line in
   body text. Two body lines are taller than the team's 30 px content floor,
   so the portal's shared row height (folder rows and file rows, which must
   line up) is two lines plus the row padding. */
.portal-shell .drive-workspace,
.portal-conversations {
  --drive-row-min-h: calc(2 * var(--font-xs) * var(--line-height-normal) + 2 * var(--space-2));
}

.portal-shell .drive-folder-band {
  font-weight: var(--weight-normal);
  color: var(--color-text);
}

.portal-shell .drive-folder-row__name {
  font-size: var(--font-xs);
  font-weight: var(--weight-normal);
  line-height: var(--line-height-normal);
}

.portal-shell .drive-folder-row__sub,
.portal-shell .drive-folder-row__status,
.portal-shell .drive-folder-row__count,
.portal-shell .drive-folder-row__count[data-zero] {
  line-height: var(--line-height-normal);
  color: var(--color-text);
}

.portal-shell .drive-folder-row.is-active .drive-folder-row__count {
  font-weight: var(--weight-normal);
}

/* The conversation: who wrote it and when, in body text; the chips beside
   them keep their own voice, as a tag does. */
.portal-shell .rail-meta,
.portal-shell .rail-meta-author,
.portal-shell .rail-meta-sep {
  font-weight: var(--weight-normal);
  color: var(--color-text);
}

.portal-shell .rail-body {
  line-height: var(--line-height-normal);
}

.portal-shell .page-timeline-empty-title,
.portal-shell .page-timeline-empty-msg {
  font-size: var(--font-xs);
  font-weight: var(--weight-normal);
  color: var(--color-text);
}

/* ─────────────────────────────────────────────────────────────────────
   The phone (#1371) — approved screens RETURN/V1-Phone-*.

   Below 640 px the shell trades its rail for a top bar and a menu behind
   a burger, opening from the left (the website's pattern, drawn in these
   tokens); the page header band gives its name to the bar. A phone opens
   the Drive like a laptop does (#1418), in the Drive's own narrow mode,
   which starts at this same 640 px.

   A few pieces are not phone-only: the document screen's head (its status
   once, at the top), the page's preview and the full-size view serve the
   laptop's checklist pages too.
   ───────────────────────────────────────────────────────────────────── */

.portal-phone-bar,
.portal-phone-menu,
.portal-phone-veil {
  display: none;
}

/* The document screen's head: on a laptop the page header names the
   document, so only its status shows here. */
.portal-doc-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

.portal-doc-head__title {
  display: none;
  white-space: normal;
}

/* The two-line row's second line and the fold are the phone's; the laptop's
   card carries a tag, and its list scrolls rather than folding. */
.portal-page-card__line,
.portal-doc-files__unfold {
  display: none;
}

.portal-doc-files__empty {
  margin: 0;
  font-size: var(--font-xs);
  color: var(--color-text);
}

/* The document's conversation (V1-Phone-C2): no box of its own, on the page
   grey, the placeholder naming the document instead of the "About …" banner. */
.portal-doc-conversation {
  margin-top: var(--space-8);
}

.portal-doc-conversation [data-composer-about] {
  display: none;
}

.portal-doc-conversation .drive-group__head {
  padding: 0;
}

/* The document list's groups — Needs you · With us · Case folders. */
.portal-docs-groups {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.portal-docs-group .doc-rows {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}

.portal-docs-group__head {
  padding: 0;
}

/* Waiting to be sorted (#1382): a file with no screen of its own yet, so its
   row is not a link and does not light up like one. On a document's screen it
   sits under the files, with the list's gap above it. */
.doc-row--waiting:hover { background: transparent; }
.doc-row--waiting .doc-row__heading:hover .doc-row__label { color: var(--color-text-strong); }

#portal-doc-grid > .portal-docs-group--waiting {
  margin-top: var(--space-6);
}

/* One page (V1-Phone-Page): the page itself, large, opening full size. */
.portal-page-preview {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 22rem;
  margin: 0 0 var(--space-4);
  padding: 0;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--color-surface-muted);
  overflow: hidden;
  cursor: zoom-in;
}

.portal-page-preview img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.portal-page-preview__hint {
  position: absolute;
  right: var(--space-3);
  bottom: var(--space-3);
}

.portal-page-modal__head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.portal-page-modal__sent {
  margin: var(--space-2) 0 var(--space-5);
  font-size: var(--font-xs);
  color: var(--color-text);
}

.portal-page-modal__copies-head {
  margin-top: var(--space-6);
  padding: 0;
}

.portal-page-modal .portal-page-thread {
  margin-top: var(--space-6);
}

/* Full size (V1-Phone-Full): dark, one file's pages side by side to swipe
   between, the browser's own pinch to zoom, × to close. */
.portal-fullsize {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  flex-direction: column;
  background: var(--color-text-strong);
  color: var(--color-surface);
}

.portal-fullsize[hidden] {
  display: none;
}

body.portal-fullsize-open {
  overflow: hidden;
}

.portal-fullsize__head,
.portal-fullsize__foot {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
}

.portal-fullsize__foot {
  padding-bottom: var(--space-5);
}

.portal-fullsize__title {
  flex: 1;
  min-width: 0;
  font-size: var(--font-sm);
}

.portal-fullsize__hint {
  flex: 1;
  text-align: center;
  font-size: var(--font-xs);
}

.portal-fullsize__close,
.portal-fullsize__step {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.portal-fullsize__step:disabled {
  opacity: 0.3;
  cursor: default;
}

.portal-fullsize__track {
  flex: 1;
  min-height: 0;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  touch-action: pan-x pinch-zoom;
  scrollbar-width: none;
}

.portal-fullsize__page {
  flex: 0 0 100%;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0 var(--space-3);
  scroll-snap-align: center;
}

.portal-fullsize__page img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

@media (max-width: 640px) {
  /* ── The shell: the document scrolls; the rail and sub-nav step aside ── */
  body.portal-shell {
    height: auto;
    min-height: 100vh;
    overflow: visible;
    background: var(--color-team-surface);
  }

  .portal-shell .nav-rail,
  .portal-shell .nav-panel {
    display: none;
  }

  .portal-shell .layout {
    display: block;
    min-height: 0;
  }

  .portal-shell .layout .main,
  .portal-shell .layout.no-panel .main {
    height: auto;
    margin: 0;
    border-radius: 0;
    overflow: visible;
  }

  .portal-shell .main-detail {
    height: auto;
  }

  .portal-shell .main-content {
    overflow: visible;
    padding: var(--space-5);
  }

  .portal-shell .container {
    max-width: none;
  }

  /* The header band's title is the bar's now; kept for assistive tech. */
  .portal-shell .mh {
    position: absolute;
    width: 1px;
    height: 1px;
    min-height: 0;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }

  .portal-shell .back-link {
    display: none;
  }

  /* Edge-to-edge pages — the Messages page, a person's library — keep a
     bounded region whose panes scroll on their own, under the bar. */
  body.portal-shell.is-flush {
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
  }

  .portal-shell.is-flush .layout {
    flex: 1;
    min-height: 0;
    display: flex;
  }

  .portal-shell.is-flush .layout .main {
    flex: 1;
    min-width: 0;
    min-height: 0;
    overflow: clip;
  }

  .portal-shell.is-flush .main-detail {
    height: 100%;
  }

  .portal-shell.is-flush .main-content {
    overflow-y: auto;
    padding: 0;
  }

  /* ── The top bar ── */
  .portal-phone-bar {
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 60px;
    padding: var(--space-2) var(--space-3) var(--space-2) var(--space-5);
    box-sizing: border-box;
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border-subtle);
  }

  .portal-phone-bar__start {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
  }

  .portal-phone-bar__title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--font-lg);
    font-weight: var(--weight-bold);
    line-height: var(--line-height-tight);
    color: var(--color-text-strong);
  }

  .portal-phone-bar__back {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-width: 0;
    min-height: 44px;
    font-size: var(--font-sm);
    color: var(--color-accent);
    text-decoration: none;
  }

  .portal-phone-bar__back svg {
    flex: none;
  }

  .portal-phone-bar__back-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .portal-phone-bar__action {
    flex: none;
  }

  .portal-phone-bar__burger {
    position: relative;
    flex: none;
    display: inline-flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    gap: var(--space-1);
    width: 44px;
    height: 44px;
    padding: 0 var(--space-2);
    border: 0;
    background: transparent;
    cursor: pointer;
  }

  .portal-phone-bar__burger-line {
    display: block;
    width: 16px;
    height: 2px;
    background: var(--color-text-strong);
  }

  .portal-phone-bar__burger-line:nth-child(2) { width: 18px; }
  .portal-phone-bar__burger-line:nth-child(3) { width: 20px; }

  /* ── The menu, from the left ── */
  .portal-phone-menu {
    position: fixed;
    top: var(--space-2);
    bottom: var(--space-2);
    left: var(--space-2);
    z-index: var(--z-modal);
    display: flex;
    flex-direction: column;
    width: calc(100% - 2 * var(--space-2));
    box-sizing: border-box;
    overflow-y: auto;
    background: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    visibility: hidden;
    transform: translateX(calc(-100% - var(--space-4)));
    transition: transform var(--transition-normal), visibility var(--transition-normal);
  }

  body.portal-menu-open .portal-phone-menu {
    visibility: visible;
    transform: none;
  }

  .portal-phone-veil {
    display: block;
    position: fixed;
    inset: 0;
    z-index: var(--z-dropdown);
    background: color-mix(in srgb, var(--color-text-strong) 40%, transparent);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition-normal);
  }

  body.portal-menu-open .portal-phone-veil {
    opacity: 1;
    pointer-events: auto;
  }

  body.portal-menu-open {
    overflow: hidden;
  }

  .portal-phone-menu__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-3) var(--space-3) var(--space-5);
    border-bottom: 1px solid var(--color-border-subtle);
  }

  .portal-phone-menu__title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--font-lg);
    font-weight: var(--weight-bold);
    color: var(--color-text-strong);
  }

  .portal-phone-menu__close {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--color-text-strong);
    cursor: pointer;
  }

  .portal-phone-menu__list {
    list-style: none;
    margin: 0;
    padding: var(--space-3);
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
  }

  .portal-phone-menu__item {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    min-height: 56px;
    padding: var(--space-3) var(--space-4);
    box-sizing: border-box;
    border-radius: var(--radius-md);
    color: var(--color-text);
    text-decoration: none;
  }

  .portal-phone-menu__item.is-active {
    background: var(--color-accent-50);
  }

  .portal-phone-menu__icon {
    flex: none;
    width: 22px;
    height: 22px;
    color: var(--color-muted);
  }

  .portal-phone-menu__item.is-active .portal-phone-menu__icon,
  .portal-phone-menu__item.is-active .portal-phone-menu__name {
    color: var(--color-accent);
  }

  .portal-phone-menu__text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
  }

  .portal-phone-menu__name {
    font-size: var(--font-base);
    font-weight: var(--weight-semibold);
  }

  .portal-phone-menu__sub {
    font-size: var(--font-xs);
    color: var(--color-muted);
  }

  .portal-phone-menu__item .badge {
    position: static;
  }

  .portal-phone-menu__manager {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin: var(--space-3) var(--space-5) 0;
    padding-top: var(--space-5);
    border-top: 1px solid var(--color-border-subtle);
  }

  .portal-phone-menu__foot {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-top: auto;
    padding: var(--space-4) var(--space-5);
    border-top: 1px solid var(--color-border-subtle);
  }

  .portal-phone-menu__user {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--font-sm);
  }

  .portal-phone-menu__signout {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    font-size: var(--font-sm);
    color: var(--color-accent);
  }

  /* ── All documents (V1-Phone-All) — the rows only: the conversation is
     Messages, in the menu; upload is the bar's. ── */
  .portal-checklist-layout__side,
  .doc-row__upload,
  .doc-row__next {
    display: none;
  }

  .doc-row {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .doc-row__actions {
    grid-column: auto;
  }

  /* ── One document (V1-Phone-C2): one status at the top and nowhere else,
     the files on two lines, folding past five. ── */
  .portal-doc-head {
    margin-bottom: var(--space-8);
  }

  .portal-doc-head__title {
    display: block;
  }

  #portal-doc-grid .progress-meter {
    display: none;
  }

  /* The verdict sentence, quiet under the status rather than a second
     banner beside it. */
  #portal-doc-grid > .alert {
    margin-top: calc(var(--space-6) * -1);
    margin-bottom: var(--space-8);
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--color-text);
    font-size: var(--font-xs);
  }

  #portal-doc-grid > .alert .alert-icon {
    display: none;
  }

  .portal-doc-files {
    margin-top: 0;
  }

  .portal-doc-files .portal-doc-grid {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-2);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
  }

  .portal-doc-files .portal-page-card {
    flex-direction: row;
    align-items: center;
    gap: var(--space-3);
    min-height: 56px;
    padding: var(--space-1) var(--space-2);
    box-sizing: border-box;
    border: 0;
    border-radius: var(--radius-sm);
  }

  .portal-doc-files .portal-page-card::after {
    display: none;
  }

  .portal-doc-files .portal-page-card__thumb {
    position: static;
    flex: none;
    width: 40px;
    height: 40px;
    aspect-ratio: auto;
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
  }

  /* The whole row opens the page. */
  .portal-doc-files a.portal-page-card__thumb::before {
    content: "";
    position: absolute;
    inset: 0;
  }

  .portal-doc-files .portal-page-card__thumb::after,
  .portal-doc-files .portal-page-card__badge,
  .portal-doc-files .portal-page-card__thumb--missing span {
    display: none;
  }

  .portal-doc-files .portal-page-card__thumb--missing svg {
    width: 18px;
    height: 18px;
  }

  .portal-doc-files .portal-page-card__body {
    flex: 1;
    min-width: 0;
    gap: var(--space-1);
    padding: 0;
  }

  .portal-doc-files .portal-page-card__body > .tag,
  .portal-doc-files .portal-page-card--needs-redo .portal-page-card__reason {
    display: none;
  }

  .portal-doc-files .portal-page-card__line {
    display: block;
    font-size: var(--font-xs);
    color: var(--color-text);
  }

  .portal-doc-files .portal-page-card__action {
    position: relative;
    width: auto;
    align-self: flex-start;
  }

  .portal-doc-grid--folded > .portal-page-card:nth-child(n + 5) {
    display: none;
  }

  .portal-doc-files__unfold {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    width: 100%;
    min-height: 44px;
    margin-top: var(--space-1);
    border: 0;
    border-top: 1px solid var(--color-border-subtle);
    background: transparent;
    font-family: var(--font);
    font-size: var(--font-sm);
    color: var(--color-accent);
    cursor: pointer;
  }

  .portal-doc-files__unfold[hidden] {
    display: none;
  }

  .portal-doc-add > summary {
    min-height: 44px;
  }

  /* ── One page (V1-Phone-Page) ── */
  .portal-page-preview {
    height: 26rem;
  }

  /* ── Messages (V1-Phone-Messages): the subjects a sideways row of filters
     above the conversation, the composer pinned to the bottom. ── */
  .portal-conversations {
    flex-direction: column;
  }

  .portal-conversations__subjects {
    flex: 0 0 auto;
    overflow-x: auto;
    overflow-y: hidden;
    border-right: 0;
    border-bottom: 1px solid var(--color-border-subtle);
  }

  .portal-conversations__subjects .drive-folders {
    display: flex;
    flex-direction: row;
    gap: var(--space-1);
    width: max-content;
    padding: var(--space-3) var(--space-5);
  }

  .portal-conversations__subjects .drive-folder-band,
  .portal-conversations__subjects .drive-folder-row__icon,
  .portal-conversations__subjects .drive-folder-row__sub,
  .portal-conversations__subjects .drive-folder-row__count,
  .portal-conversations__subjects .drive-folder-row__chev {
    display: none;
  }

  .portal-conversations__subjects .drive-folder-row {
    flex: none;
    height: auto;
    min-height: 36px;
    padding: var(--space-1) var(--space-3);
    border: 1px solid transparent;
    background: var(--color-surface-muted);
    font-size: var(--font-xs);
  }

  .portal-conversations__subjects .drive-folder-row,
  .portal-conversations__subjects .drive-folder-row.is-active .drive-folder-row__name {
    color: var(--color-text);
  }

  .portal-conversations__subjects .drive-folder-row.is-active {
    background: var(--color-surface);
    border-color: var(--color-border);
  }

  .portal-conversations__subjects .drive-folder-row__name {
    white-space: nowrap;
  }

  .portal-conversations__thread {
    flex: 1 1 auto;
    padding: var(--space-5) var(--space-5) 0;
  }

  .portal-conversations__thread .portal-messages__head {
    display: none;
  }

  .portal-conversations__thread .portal-messages__composer {
    position: sticky;
    bottom: 0;
    margin: 0 calc(var(--space-5) * -1);
    padding: var(--space-3) var(--space-5) var(--space-4);
    background: var(--color-surface);
    border-top: 1px solid var(--color-border-subtle);
  }

  /* ── Journey (V1-Phone-Journey): the step list and the case manager card;
     the service, town hall and date are a laptop's side column. ── */
  .portal-journey {
    margin-top: 0;
  }

  .portal-journey__aside .portal-journey__card + .portal-journey__card {
    display: none;
  }
}

/* ─────────────────────────────────────────────────────────────────────
   The themed portal (#1408) — component rules.

   Brand-neutral: every colour, case and tracking below is a semantic token
   (design-system.css), so the same rules serve any theme, and with no theme
   they render in the platform's own palette. A theme's values live only in
   static/css/themes/<name>.css, linked after this file on the portal host
   when `settings.portal_theme` is set (core.template_globals.portal_theme_for).

   Settled on the mockup (Rasmus, 2026-10-20/21,
   claude.ai/artifact/SPTVGddZwY728dZQBEmPzs): the portal keeps its rounded
   corners; headings are the sans; a selected row is a wash with a short
   straight marker bar; lists get hairlines and alternate rows; group labels
   are small, bold and quiet; a redo message is told apart by its chip and a
   faint wash; the Drive sidebar keeps its full-size preview.
   ───────────────────────────────────────────────────────────────────── */

.portal-shell .nav-rail { box-shadow: none; }
.portal-shell .nav-rail-btn,
.portal-shell .nav-rail-help { color: var(--color-rail-icon); }
.portal-shell .nav-rail-btn:hover,
.portal-shell .nav-rail-help:hover {
  background: color-mix(in srgb, var(--color-rail-active-bg) 12%, transparent);
  color: var(--color-rail-active-bg);
}
.portal-shell .nav-rail-btn.active { background: var(--color-rail-active-bg); color: var(--color-rail-icon-active); box-shadow: none; }
.portal-shell .nav-rail-btn::after,
.portal-shell .nav-rail-help::after { background: var(--color-rail); color: var(--color-rail-active-bg); }
.portal-shell .nav-rail-avatar { color: var(--color-avatar-text); }
.portal-shell .nav-rail .badge--pin { box-shadow: var(--shadow-rail-badge); }

/* The theme's logo mark tops the rail: templates/themes/<theme>/brand_mark.html,
   inline SVG with fill="currentColor" (lint: svg-raw-color). */
.portal-brand-mark { display: flex; justify-content: center; color: var(--color-rail-mark); margin-bottom: var(--space-5); }
.portal-brand-mark svg { width: 30px; height: auto; }

.portal-shell .mh { background: var(--color-bg); border-bottom-color: var(--color-border); }
.portal-shell .mh-title { font-size: var(--font-xl); font-weight: var(--weight-semibold); letter-spacing: -0.015em; }
.portal-shell .mh-note {
  text-transform: var(--text-transform-label); letter-spacing: var(--letter-spacing-label);
  font-size: var(--font-xxs); font-weight: var(--weight-semibold); color: var(--color-accent);
}
.portal-shell .mh-note-icon { color: var(--color-accent); }
.portal-shell .main-content,
.portal-shell .drive-workspace,
.portal-shell .drive-tree-rail { background: var(--color-bg); }
.portal-shell a:not([class]) { color: var(--color-accent); text-underline-offset: 3px; }

/* ── 2. Buttons ── */
.portal-shell .btn {
  text-transform: var(--text-transform-button); letter-spacing: var(--letter-spacing-button);
  font-size: var(--font-xs); font-weight: var(--weight-semibold);
}
.portal-shell .btn-primary { background: var(--color-accent); color: var(--color-surface); padding: var(--space-4) var(--space-5); }
.portal-shell .btn-primary:hover { background: var(--color-accent-hover); }
.portal-shell .btn-ghost,
.portal-shell .btn-link { color: var(--color-accent); }

/* ── 3. Cards ── */
.portal-shell .card,
.portal-journey__card,
.portal-journey__aside .portal-journey__card { border-color: var(--color-border); box-shadow: none; background: var(--color-surface); }

/* ── 4. Journey ── */
.portal-welcome__journey-head > :first-child,
.portal-welcome__journey-head h2,
.portal-welcome__journey-head h3 { font-size: var(--font-lg); font-weight: var(--weight-semibold); letter-spacing: -0.015em; }
.portal-welcome__journey-meta {
  text-transform: var(--text-transform-label); letter-spacing: var(--letter-spacing-label);
  font-size: var(--font-xxs); font-weight: var(--weight-semibold); color: var(--color-muted);
}
.portal-welcome__step-title { font-weight: var(--weight-semibold); letter-spacing: -0.01em; }
.portal-welcome__step-sub { font-size: var(--font-sm); line-height: var(--line-height-relaxed); color: var(--color-subtle); }
.portal-welcome__step-marker { border-color: var(--color-step-line); }
.portal-welcome__step:not(:last-child)::before { background: var(--color-step-line); }
.portal-welcome__step--done .portal-welcome__step-marker { background-color: var(--color-step-done); border-color: var(--color-step-done); }
.portal-welcome__step--done::before { background: var(--color-step-done); }
.portal-welcome__step--active .portal-welcome__step-marker {
  background: var(--color-accent); border-color: var(--color-accent);
  color: var(--color-surface); box-shadow: var(--shadow-step-halo);
}
.portal-welcome__step--final .portal-welcome__step-marker { color: var(--color-accent); border-color: var(--color-accent); }
.portal-journey__needs-count {
  text-transform: var(--text-transform-label); letter-spacing: var(--letter-spacing-label);
  font-size: var(--font-xxs); font-weight: var(--weight-semibold); color: var(--color-accent);
}
.portal-journey__needs { border-color: var(--color-border); background: var(--color-bg); }
.portal-journey__need:hover { background: var(--color-surface-tint); }
.portal-journey__need-name { font-weight: var(--weight-semibold); }
.portal-journey__need-status { color: var(--color-accent); }
.portal-journey__facts dt,
.portal-messages__manager-label {
  text-transform: var(--text-transform-label); letter-spacing: var(--letter-spacing-label);
  font-size: var(--font-xxs); font-weight: var(--weight-semibold); color: var(--color-muted);
}
.portal-messages__manager-name { font-size: var(--font-base); font-weight: var(--weight-semibold); }
.portal-messages__avatar,
.rail-avatar { background: var(--avatar-bg); color: var(--color-avatar-text); border-radius: var(--avatar-border-radius); }
.portal-journey__write { border-color: var(--color-border); color: var(--color-muted); }

/* ── 5. Stage strip ── */
.stage-bar-label {
  text-transform: var(--text-transform-label); letter-spacing: var(--letter-spacing-label);
  font-size: var(--font-xxs); font-weight: var(--weight-semibold);
}
.stage-bar-step.is-done .stage-bar-label { color: var(--color-step-done); }
.stage-bar-step.is-done .stage-bar-dot { background: var(--color-step-done); border-color: var(--color-step-done); }
.stage-bar-step.is-done + .stage-bar-connector,
.stage-bar-connector.is-done { background: var(--color-step-done); }
.stage-bar-step.is-active .stage-bar-label { color: var(--color-accent); }
.stage-bar-step.is-active .stage-bar-dot { background: var(--color-accent); border-color: var(--color-accent); }

/* ── 6. Selection: one look for a selected folder, file, document or chip —
   a wash and a short straight bar inside the left edge (never bending round
   a corner); on a phone, where folders are chips, an outline. ── */
.portal-shell .drive-folder-row.is-active,
.portal-case-nav .docs-nav-item.active,
.portal-shell .drive-group > .drive-list-row.drive-selected {
  position: relative; background: var(--color-selected); color: var(--color-text); box-shadow: none;
}
.portal-shell .drive-folder-row.is-active::before,
.portal-case-nav .docs-nav-item.active::before,
.portal-shell .drive-group > .drive-list-row.drive-selected::before {
  content: ""; position: absolute; left: 0; top: var(--space-3); bottom: var(--space-3);
  width: 3px; border-radius: 0 var(--radius-pill) var(--radius-pill) 0; background: var(--color-selected-marker);
}
.portal-shell .drive-folder-row.is-active .drive-folder-row__name,
.portal-case-nav .docs-nav-item.active,
.portal-shell .drive-list-row.drive-selected .drive-list-row__name { color: var(--color-text); font-weight: var(--weight-semibold); }
.portal-shell .drive-folder-row.is-active .drive-folder-row__count,
.portal-shell .drive-folder-row.is-active .drive-folder-row__chev { color: var(--color-subtle); }
.portal-shell .drive-folder-row.is-active .drive-folder-row__icon,
.portal-case-nav .docs-nav-item.active .docs-nav-icon { color: var(--color-selected-marker); }
@media (max-width: 640px) {
  .portal-shell .drive-folder-row.is-active::before { display: none; }
  .portal-shell .drive-folder-row.is-active { box-shadow: var(--shadow-chip-selected); }
}

/* ── 7. Lists: labels, hairlines, alternate rows ── */
.portal-shell .drive-folder-band,
.portal-case-nav .docs-nav-label {
  text-transform: var(--text-transform-label); letter-spacing: var(--letter-spacing-label);
  font-size: var(--font-xxs); font-weight: var(--weight-bold); color: var(--color-label);
}
.drive-folder-row__name { font-weight: var(--weight-medium); }
@media (min-width: 641px) {
  .portal-shell .drive-folder-band ~ .drive-folder-row { border-radius: 0; border-bottom: 1px solid var(--color-border); }
  .portal-shell .drive-folder-band + .drive-folder-row { border-top: 1px solid var(--color-border); }
}
.drive-group__head > :first-child,
.drive-group__name { font-weight: var(--weight-semibold); letter-spacing: -0.01em; }
.drive-group > .drive-list-row { border-radius: 0; border-bottom: 1px solid var(--color-border); padding-inline: var(--space-3); }
.drive-group > .drive-group__head + .drive-list-row { border-top: 1px solid var(--color-border); }
.drive-group > .drive-list-row:nth-of-type(even) { background: var(--color-surface-tint); }
.drive-group > .drive-list-row:hover { background: var(--color-surface-tint-strong); }

/* ── 8. Tags and chips ── */
.tag {
  text-transform: var(--text-transform-label); letter-spacing: var(--letter-spacing-label);
  font-size: var(--font-xxs); font-weight: var(--weight-semibold);
}
.tag--pending { background: var(--color-info-tint); color: var(--color-info); border-color: transparent; }
.tag--missing { background: var(--color-surface-muted); color: var(--color-subtle); border-color: transparent; }
.tag--danger { background: var(--color-danger-tint); color: var(--color-danger); border-color: transparent; }
.rail-meta-chip,
.rail-meta-pill {
  text-transform: var(--text-transform-label); letter-spacing: var(--letter-spacing-label);
  font-size: var(--font-xxs); font-weight: var(--weight-semibold);
}
.rail-meta-chip--redo { background: var(--color-danger-tint); color: var(--color-accent-hover); border-color: transparent; }

/* ── 9. Messages ── */
.portal-messages__title,
.portal-conversations__thread h2 { font-size: var(--font-lg); font-weight: var(--weight-semibold); letter-spacing: -0.015em; }
.rail-card { border-color: var(--color-border); box-shadow: none; }
.rail-meta-author { font-weight: var(--weight-semibold); }
.rail-card--redo { border-left: 1px solid var(--color-border); background: var(--color-attention-wash); }
.page-composer-bar,
.composer-wrap { border-color: var(--color-border); background: var(--color-surface); }
/* The filter chips stay at --font-xs: tappable controls, kept off 9.6 px for phones. */
.portal-messages__filter .timeline-filter__btn { background: transparent; border-color: var(--color-border); font-weight: var(--weight-semibold); }
.portal-messages__filter .timeline-filter__btn[aria-pressed="true"] { background: var(--color-selected); border-color: var(--color-selected); color: var(--color-text); }

/* ── 10. Drive sidebar: the selected file at full size ── */
.portal-shell .drive-inspector__preview { background: var(--color-surface-tint); border: 1px solid var(--color-border); border-radius: var(--radius-md); }
.portal-shell .drive-inspector__content .drive-inspector__title { font-size: var(--font-base); font-weight: var(--weight-semibold); color: var(--color-text); }
.portal-shell .drive-inspector__content .drive-inspector__subtitle { color: var(--color-muted); }

/* ── 11. "New message below" (#1419, drive-inspector.js): when the selected
   file has an unread message or an open redo request out of view under its
   preview, a pill at the bottom of the sidebar scrolls to it and leaves once
   it is in view. ── */
.drive-inspector__jump {
  position: sticky; bottom: var(--space-4); z-index: var(--z-sticky);
  display: flex; justify-content: center; pointer-events: none;
  margin-top: calc(-1 * var(--space-8));
}
.drive-inspector__jump[hidden] { display: none; }
.drive-inspector__jump-btn {
  pointer-events: auto;
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: 0; border-radius: var(--radius-pill); cursor: pointer;
  background: var(--color-text); color: var(--color-surface);
  font: inherit; font-size: var(--font-xs); font-weight: var(--weight-semibold);
  box-shadow: var(--shadow-md);
  transition: background var(--transition-fast);
}
.drive-inspector__jump-btn svg { width: 14px; height: 14px; }
.drive-inspector__jump-btn:hover { background: var(--color-subtle); }
.drive-inspector__jump-btn:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.drive-inspector__jump--redo .drive-inspector__jump-btn { background: var(--color-accent); }
.drive-inspector__jump--redo .drive-inspector__jump-btn:hover { background: var(--color-accent-hover); }
@media (prefers-reduced-motion: no-preference) {
  .drive-inspector__jump:not([hidden]) .drive-inspector__jump-btn { animation: drive-jump-in var(--transition-slower) ease-out; }
}
@keyframes drive-jump-in { from { opacity: 0; transform: translateY(var(--space-2)); } to { opacity: 1; transform: none; } }
/* Below 640 px the pane stacks under the list and grows with its content, so
   the page scrolls, not the pane. Left a scroll container (overflow-x: hidden
   turns overflow-y to auto), it would hold the sticky pill at its own far
   end, under the message; `clip` cuts the same overflow without being one.
   A phone opens the Drive (#1418), so this is the phone's layout as well as
   a narrowed laptop window's. */
@media (max-width: 640px) {
  .drive-inspector--conversation { overflow-x: clip; overflow-y: visible; }
}

/* ── 12. Phone chrome ── */
.portal-phone-bar { background: var(--color-bg); border-bottom-color: var(--color-border); }
.portal-phone-bar__title,
.portal-phone-menu__title { font-size: var(--font-lg); font-weight: var(--weight-semibold); letter-spacing: -0.015em; }
.portal-phone-menu { background: var(--color-bg); }
.portal-phone-menu__item.is-active { background: var(--color-selected); color: var(--color-text); }
.portal-phone-menu__signout {
  color: var(--color-accent); text-transform: var(--text-transform-button); letter-spacing: var(--letter-spacing-button);
  font-size: var(--font-xs); font-weight: var(--weight-semibold);
}

/* ── 13. Pages with no rail (set your password, link no longer valid):
   centred on a card, the way sign-in is. ── */
.portal-shell .layout:not(:has(.nav-rail-btn)) .nav-rail { display: none; }
.portal-shell .layout:not(:has(.nav-rail-btn)) .main { margin-left: 0; }
.portal-shell .layout:not(:has(.nav-rail-btn)) .container {
  max-width: 440px; margin: var(--space-8) auto;
  background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--radius-auth-card); padding: var(--space-8) var(--space-6);
}
.portal-shell .layout:not(:has(.nav-rail-btn)) .page-header h1 { font-size: var(--font-xl); font-weight: var(--weight-semibold); letter-spacing: -0.015em; }

/* ── 14. Sign-in on the portal host (base_auth + auth-shell--portal) ── */
.auth-shell--portal .card.auth-card { border-color: var(--color-border); box-shadow: none; }
.auth-shell--portal .auth-head h1 { font-size: var(--font-xl); font-weight: var(--weight-semibold); letter-spacing: -0.015em; }
.auth-shell--portal .btn {
  text-transform: var(--text-transform-button); letter-spacing: var(--letter-spacing-button);
  font-size: var(--font-xs); font-weight: var(--weight-semibold);
}

/* ── 15. The theme's wordmark (static/themes/<name>/wordmark.svg, via
   partials/brand_logo.html) — at the head of the portal-host sign-in card,
   and above the heading of the two rail-less pages, which draw it only when a
   theme supplies one. With no theme, sign-in keeps the platform logo. ── */
.auth-logo img.auth-logo__wordmark { width: 200px; height: auto; }
.portal-shell .auth-logo { display: flex; justify-content: center; margin-bottom: var(--space-6); }
.portal-shell .auth-logo img.auth-logo__wordmark { width: auto; height: 92px; }
