:root {
  --bg: #1a1b26;
  --bg-surface: #24283b;
  --bg-hover: #2f3350;
  --text: #c0caf5;
  --text-dim: #8b93b8;
  --border: #3b4261;
  --accent: #7aa2f7;
  --cyan: #7dcfff;
  --green: #9ece6a;
  --yellow: #e0af68;
  --red: #f7768e;
  --orange: #ff9e64;
  --purple: #bb9af7;
  --font-mono: "SF Mono", "Cascadia Code", "Fira Code", Menlo, Consolas, monospace;
}

[data-theme="light"] {
  --bg: #f5f5f5;
  --bg-surface: #ffffff;
  --bg-hover: #e8e8e8;
  --text: #343b58;
  --text-dim: #6b6f7b;
  --border: #d0d0d0;
  --accent: #2569d6;
  --cyan: #007197;
  --green: #587539;
  --yellow: #8c6c3e;
  --red: #c64343;
  --orange: #965027;
  --purple: #7847bd;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* Base link color: without this, links inside dialogs and other unstyled
   containers fall back to the browser default dark blue, which is unreadable
   on the dark theme. Scoped rules below override where needed. */
a {
  color: var(--accent);
}

[hidden] {
  display: none !important;
}

/* One thin, theme-aware scrollbar for every scroll region, so a long
   credential list, a dialog, and the decoder all read the same instead of
   falling back to the platform's default bar. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
::-webkit-scrollbar-track {
  background: transparent;
}
::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 6px;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--text-dim);
}

body {
  font-family: var(--font-mono);
  background: var(--bg);
  color: var(--text);
  height: 100vh;
  /* Mobile browsers count the URL bar into 100vh, so the footer would sit
     below the visible area with no way to scroll to it. dvh tracks the
     actually visible height. */
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-surface);
  flex-shrink: 0;
  /* Anchor the mobile menu dropdown, which is absolutely positioned. */
  position: relative;
}

header h1 {
  font-size: 14px;
  font-weight: 600;
  color: var(--accent);
}

/* The mark is 46:51, so give it that ratio instead of a square box that
   would letterbox it and add phantom padding next to the title. */
.header-logo {
  width: 26px;
  height: 29px;
  flex-shrink: 0;
}

.header-left {
  display: flex;
  align-items: center;
  /* Wordmark reads as its own unit, then the links with their own rhythm. */
  gap: 28px;
}

.header-left .header-logo + h1 {
  margin-left: -18px;
}

.header-links {
  display: flex;
  gap: 20px;
  font-size: 11px;
}

/* The menu toggle only exists for narrow screens; the media query below
   reveals it and turns the link row into a dropdown. */
.header-menu-toggle {
  display: none;
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font-size: 12px;
  padding: 4px 10px;
  cursor: pointer;
}

.header-links a {
  color: var(--text-dim);
  text-decoration: none;
}

.header-links a:hover {
  color: var(--accent);
  text-decoration: underline;
}

.header-right {
  display: flex;
  align-items: center;
  gap: 12px;
}

.btn {
  font-family: var(--font-mono);
  font-size: 11px;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg-surface);
  color: var(--text);
  cursor: pointer;
}

.btn:hover {
  background: var(--bg-hover);
}

.btn-primary {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

.btn-primary:hover {
  opacity: 0.9;
}

.btn-danger {
  color: var(--red);
  border-color: var(--red);
}

.btn-danger:hover {
  background: rgba(247, 118, 142, 0.1);
}

.main {
  flex: 1;
  overflow-y: auto;
  /* Reserve the scrollbar's width whether or not it is showing, so the content
     does not jump sideways the moment the list grows past one screen. */
  scrollbar-gutter: stable;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Credential Cards */
.section-title {
  font-size: 12px;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-bottom: 4px;
}

.credentials {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* The credential card is a single background slot (the face) plus a body of
   debug detail. It lays itself out from its own width: a dense row on a wide
   list, a wallet-style card with the face as a hero on a phone. */
/* The list is the query container: every card lays itself out from the list
   width, so the whole stack switches between the row and the wallet-card form
   together. A card cannot size itself from its own width, so the container has
   to be the parent. */
.credentials {
  display: flex;
  flex-direction: column;
  gap: 10px;
  container-type: inline-size;
}

.credential-card {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg-surface);
  overflow: hidden;
}

/* The card face carries the issuer-declared appearance. background_image over
   background_color as its base, or a solid color, or a plain tile, painted by
   applyCredentialDisplay. */
.card-face {
  position: relative;
  background-size: cover;
  background-position: center;
  color: #fff;
  cursor: pointer;
}
.card-face.plain {
  background: var(--bg-hover);
  color: var(--text);
}

/* A batch is drawn as a small stack: two sheets peek from the lower right of
   the card face, drawn as offset box-shadows so they sit behind the art and
   are never clipped. The stack is the only cue that a credential was batch
   issued (no count, since a batch cycles and reuses). It shows on the desktop
   list row and in the consent dialog; the mobile hero drops it, where a
   full-width tile has no room for the offset. */
.credential-card.batch .card-face {
  box-shadow:
    3px 4px 0 -1px var(--bg-surface),
    4px 5px 0 -1px color-mix(in srgb, var(--text-dim) 60%, transparent),
    6px 8px 0 -2px var(--bg-surface),
    7px 9px 0 -2px color-mix(in srgb, var(--text-dim) 42%, transparent),
    0 2px 6px rgba(0, 0, 0, 0.3);
}

/* The issuer logo, a monogram from the display name, or a generic glyph, each
   sitting on the face. */
.credential-logo {
  position: absolute;
  object-fit: contain;
  background: rgba(255, 255, 255, 0.94);
  border-radius: 8px;
}
.face-init,
.face-generic {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  color: var(--text-dim);
}
.face-name {
  position: absolute;
  font-weight: 700;
  letter-spacing: -0.01em;
}

/* One accent-blue format pill for every format, so the format reads without
   the noise of a color per type. The face variant overlays the art with a
   scrim (set inline to match the declared text color). */
.format-badge {
  font-family: var(--font-mono, monospace);
  font-weight: 500;
  white-space: nowrap;
}
.format-badge-row {
  font-size: 10px;
  padding: 2px 8px;
  border-radius: 5px;
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--accent);
  flex-shrink: 0;
}
.format-badge-face {
  position: absolute;
  top: 10px;
  right: 10px;
  font-size: 9px;
  padding: 2px 7px;
  border-radius: 5px;
  backdrop-filter: blur(3px);
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
}

.credential-info { min-width: 0; }
/* Only in the list does the card body open the decoder. On a consent
   candidate the click picks the credential, and the decoder has a link of its
   own. */
.credential-card .credential-info { cursor: pointer; }
.credential-card .credential-info:hover .credential-name { color: var(--accent); }

/* The header: the format pill baseline-aligned with the display name, and the
   technical type dimmed beside it (the vct is what a DCQL query matches on). */
.cred-hdr {
  display: flex;
  align-items: baseline;
  gap: 9px;
  flex-wrap: wrap;
}
.credential-name {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.cred-pills {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 8px;
}

/* The meta line: the technical type, the issuer as the format carries it, and
   the claim counts. */
.cred-meta {
  display: flex;
  gap: 8px 16px;
  flex-wrap: wrap;
  margin-top: 8px;
  font-size: 12px;
  color: var(--text-dim);
}
.cred-meta-item { overflow-wrap: anywhere; }
.cred-meta-k {
  text-transform: uppercase;
  font-size: 10px;
  letter-spacing: 0.05em;
  opacity: 0.8;
  margin-right: 3px;
}
.mono { font-family: var(--font-mono, monospace); font-size: 11px; }

/* The About control opens the credential's issuer-declared description. It is an
   ordinary action button carrying an info glyph, so it sits with Show and Revoke
   yet reads as info, and shows only when a description exists. These base rules
   precede the container queries below, which choose the glyph per width (info on
   a phone that flips, a rotating chevron on a wide list that expands in place). */
.about-btn { display: inline-flex; align-items: center; gap: 5px; }
.about-btn .ic { display: block; width: 13px; height: 13px; }
.about-btn[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
.about-btn .ic-chev { transition: transform 0.2s ease; }

/* The description pane: a small label and a bounded body that scrolls with the
   one themed scrollbar every scroll region shares. The Back control belongs to
   the phone flip (the desktop query hides it, since a wide list toggles the pane
   from the About button). */
.cred-desc-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.cred-desc-label { font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-dim); }
.cred-desc-body { font-size: 12px; line-height: 1.55; color: var(--text); overscroll-behavior: contain; }
.cred-desc-back {
  display: inline-flex;
  font-family: var(--font-mono); font-size: 10.5px; color: var(--text-dim);
  border: 1px solid var(--border); background: transparent; border-radius: 7px;
  padding: 3px 9px; cursor: pointer; align-items: center; gap: 5px;
}
.cred-desc-back:hover { color: var(--text); }
.cred-desc-back .ic { width: 13px; height: 13px; display: block; }

/* DESKTOP: a dense row with a fixed card-art thumbnail and the body beside it.
   The art is a stable size, so a card whose body wraps grows a little without
   the art resizing with it. */
@container (min-width: 601px) {
  .cred-front {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 14px;
  }
  .card-face {
    width: 92px;
    aspect-ratio: 8 / 5;
    border-radius: 9px;
    flex: 0 0 auto;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  }
  .credential-logo { top: 5px; left: 5px; width: 20px; height: 20px; padding: 2px; }
  .face-init, .face-generic { font-size: 22px; }
  .face-name, .format-badge-face { display: none; }
  .credential-info { flex: 1; }
  .credential-actions { flex: 0 0 auto; margin-left: 6px; }

  /* The description opens as an inline pane below the row, indented past the
     art thumbnail and held to a readable measure though the row is wide. The
     About button toggles it, so the pane's Back control is not shown. */
  .cred-desc { max-height: 0; overflow: hidden; opacity: 0; transition: max-height 0.26s ease, opacity 0.2s ease; }
  .credential-card.desc-open .cred-desc { max-height: 180px; opacity: 1; }
  .cred-desc-in { margin: 0 14px 0 120px; padding: 11px 0 13px; border-top: 1px solid var(--border); }
  .cred-desc-body { max-height: 120px; max-width: 76ch; overflow-y: auto; }
  /* No Back control on a wide list: the About button opens and closes the pane. */
  .cred-desc-back { display: none; }

  /* The pane expands in place, so the chevron replaces the info glyph and turns
     to point up while it is open. */
  .about-btn .ic-info { display: none; }
  .about-btn .ic-chev { display: block; }
  .about-btn[aria-expanded="true"] .ic-chev { transform: rotate(180deg); }
}

/* MOBILE: a wallet card, the face a full-width hero with the name on it, the
   debug detail stacked below, actions full width. */
@container (max-width: 600px) {
  .card-face {
    width: 100%;
    aspect-ratio: 2.4;
    border-radius: 12px 12px 0 0;
  }
  /* The full-width hero has no room for the offset stack, so a batch reads as a
     plain card on a phone (the stack stays a desktop and consent cue). */
  .credential-card.batch .card-face {
    box-shadow: none;
  }
  .credential-logo { top: 10px; left: 10px; width: 28px; height: 28px; padding: 4px; }
  .face-init, .face-generic { font-size: 26px; }
  .face-name { left: 15px; bottom: 10px; right: 15px; font-size: 18px; }
  .cred-hdr { display: none; }
  .credential-info { padding: 10px 15px 4px; }
  /* Full-width action buttons, sized for touch without eating vertical space. */
  .credential-actions { padding: 8px 15px 11px; gap: 8px; }
  .credential-actions .btn {
    flex: 1;
    font-size: 13px;
    padding: 8px 12px;
    border-radius: 8px;
  }

  /* On a phone a tall hero card turns over to read its description: the card is
     the 3D holder, so the border and the clip move to the two faces. */
  .credential-card {
    background: transparent;
    border: 0;
    overflow: visible;
    transform-style: preserve-3d;
    transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1);
  }
  .credential-card.desc-open { transform: perspective(1000px) rotateY(180deg); }
  .cred-front {
    position: relative;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg-surface);
    overflow: hidden;
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
  }
  .cred-desc {
    position: absolute;
    inset: 0;
    transform: rotateY(180deg);
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg-surface);
    overflow: hidden;
    display: flex;
    flex-direction: column;
  }
  .cred-desc-in { display: flex; flex-direction: column; min-height: 0; flex: 1; padding: 12px 14px; }
  .cred-desc-body { flex: 1; min-height: 0; overflow-y: auto; }

  /* The About button leads the full-width action row and stays its own size so
     Revoke, Show and Delete keep sharing the rest. It flips the card, so it
     reads as info and the chevron is hidden. */
  .credential-actions { flex-wrap: wrap; }
  .credential-actions .about-btn { flex: 0 0 auto; }
  .about-btn .ic-chev { display: none; }
}

.credential-actions {
  flex-shrink: 0;
  display: flex;
  gap: 6px;
  /* Keeps the actions on the right edge, on their own row too. */
  margin-left: auto;
}

.pager {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 8px;
  font-size: 11px;
  color: var(--text-dim);
}

.pager[hidden] {
  display: none;
}

.pager button[disabled] {
  opacity: 0.4;
  cursor: default;
}

/* Actions Bar */
.actions-bar {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}

.actions-bar input[type="text"] {
  font-family: var(--font-mono);
  font-size: 11px;
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--text);
  flex: 1;
  min-width: 200px;
}

.actions-bar input[type="text"]::placeholder {
  color: var(--text-dim);
}

/* Pointer to the built-in demo issuer and verifier, at the moment someone
   wants to run a flow but has no offer or request URI at hand. */
.actions-hint {
  margin-top: 6px;
  font-size: 11px;
  color: var(--text-dim);
}

.actions-hint a {
  color: var(--accent);
  text-decoration: none;
}

.actions-hint a:hover {
  text-decoration: underline;
}

.cert-links {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  margin: 4px 0;
  font-size: 11px;
}

.cert-links a {
  color: var(--accent);
  text-decoration: none;
}

.cert-links a:hover {
  text-decoration: underline;
}
#trust-list-links {
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}
.cert-links .trust-list-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
}
.cert-links .trust-list-name {
  color: var(--text-dim);
}
.cert-links .trust-list-desc {
  flex-basis: 100%;
  color: var(--text-dim);
  opacity: 0.75;
}
.cert-links .copy-btn {
  background: none;
  border: none;
  color: var(--text-dim);
  cursor: pointer;
  font-size: 11px;
  padding: 0 2px;
}
.cert-links .copy-btn:hover {
  color: var(--accent);
}

.app-footer {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 6px 16px;
  font-size: 11px;
  color: var(--text-dim);
  border-top: 1px solid var(--border);
  background: var(--bg-surface);
}
.app-footer a {
  color: var(--text-dim);
  text-decoration: none;
}
.app-footer a:hover {
  color: var(--accent);
  text-decoration: underline;
}
.footer-row {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 16px;
}

.footer-disclaimer {
  font-size: 10px;
  color: var(--text-dim);
  opacity: 0.85;
  text-align: center;
}

#demo-note {
  color: var(--yellow);
}

#pending-banner {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 8px 20px;
  font-size: 12px;
  color: var(--text);
  background: rgba(122, 162, 247, 0.14);
  border-bottom: 1px solid var(--accent);
}

#pending-banner[hidden] {
  display: none;
}

#pending-banner button {
  flex-shrink: 0;
  margin-left: auto;
}

#demo-banner {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 10px 20px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text);
  background: rgba(224, 175, 104, 0.12);
  border-bottom: 1px solid var(--yellow);
}

#demo-banner[hidden] {
  display: none;
}

#demo-banner a {
  color: var(--accent);
}

#demo-banner button {
  flex-shrink: 0;
  margin-left: auto;
}

.cli-install-label {
  font-size: 11px;
  color: var(--text-dim);
  margin-top: 10px;
}
.cli-install pre {
  font-family: var(--font-mono);
  font-size: 11px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 6px 8px;
  margin: 4px 0;
  overflow-x: auto;
  user-select: all;
}

/* Dialog Forms */
.dialog-hint {
  font-size: 12px;
  color: var(--text-dim);
  margin-bottom: 12px;
}

.form-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 12px;
  align-items: center;
}

.form-label {
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
}

.form-input {
  font-family: var(--font-mono);
  font-size: 11px;
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--text);
  width: 100%;
}

.form-input::placeholder {
  color: var(--text-dim);
}

.form-textarea {
  height: 72px;
  resize: vertical;
  align-self: start;
}

.form-checkbox {
  font-size: 11px;
  display: flex;
  align-items: center;
  gap: 6px;
}

.form-checkbox input[type="checkbox"] {
  accent-color: var(--accent);
}

.form-label-top {
  align-self: start;
  margin-top: 6px;
}

/* A divider heading that spans both columns of the form grid. */
.form-section-label {
  grid-column: 1 / -1;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-dim);
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

/* A note that spans the whole form width, so it does not shift the label and
   input columns of the grid. */
.form-hint {
  grid-column: 1 / -1;
  margin: 0;
  font-size: 11px;
  line-height: 1.4;
  color: var(--text-dim);
}

/* A color or image field: the text input carries the value, with a picker or an
   upload beside it as a convenience. */
.form-color-row,
.form-image-row {
  display: flex;
  gap: 8px;
  align-items: center;
  min-width: 0;
}
.form-color-row .form-input,
.form-image-row .form-input {
  flex: 1;
  min-width: 0;
}
.form-color-swatch {
  flex: 0 0 auto;
  width: 34px;
  height: 26px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  cursor: pointer;
}
.form-file {
  flex: 0 0 auto;
  font-size: 10px;
  color: var(--text-dim);
  max-width: 96px;
}

.claims-editor {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-self: start;
  min-width: 0;
}

.claims-mode {
  display: flex;
  gap: 14px;
}

#issue-claim-rows {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 240px;
  overflow-y: auto;
}

.claim-row {
  display: flex;
  gap: 6px;
  align-items: center;
}

.claim-row input[id^="issue-claim-key-"] {
  flex: 1;
}

.claim-row input[id^="issue-claim-value-"] {
  flex: 2;
}

.claim-row .claim-ns {
  display: none;
  flex: 1;
}

#issue-claim-rows.show-ns .claim-ns {
  display: block;
}

.claim-row .claim-sd {
  display: none;
  align-items: center;
  gap: 3px;
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
  cursor: pointer;
}

#issue-claim-rows.show-sd .claim-sd {
  display: flex;
}

.status-list-editor {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.status-protected {
  background: rgba(122, 162, 247, 0.18);
  color: var(--accent);
}

.status-badge {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 7px;
  border-radius: 9px;
  font-size: 10px;
  font-weight: 600;
  vertical-align: middle;
  /* A badge is a unit: it wraps to the next line whole. */
  white-space: nowrap;
}

/* Any badge or chip that carries an explanatory tooltip invites a hover. */
.status-badge[title],
.who-chip[title] {
  cursor: help;
}

/* In the card's pill row the gap sets the spacing and the pills read as a
   rounded set. */
.cred-pills .status-badge {
  margin-left: 0;
  padding: 2px 9px;
  border-radius: 20px;
  font-size: 11px;
  display: inline-flex;
  gap: 4px;
  align-items: center;
}

.status-none {
  background: color-mix(in srgb, var(--text-dim) 16%, transparent);
  color: var(--text-dim);
}

/* Pill icons render as pseudo-elements so the badge text stays the plain word
   (Active, Protected, Signed), which keeps text-based automation matching. */
.pill-ico { flex: 0 0 auto; }
.ico-dot::before { content: "● "; }
.ico-block::before { content: "⊘ "; }
.ico-half::before { content: "◐ "; }
.ico-circle::before { content: "○ "; }
.ico-clock::before { content: "◷ "; }
.ico-check::before { content: "✓ "; }
.ico-x::before { content: "✗ "; }
.ico-warn::before { content: "⚠ "; }

.status-active {
  background: rgba(158, 206, 106, 0.2);
  color: var(--green);
}

.status-revoked {
  background: rgba(247, 118, 142, 0.2);
  color: var(--red);
}

.status-external {
  background: rgba(125, 125, 125, 0.15);
  color: var(--text-dim);
}

.status-valid {
  background: rgba(125, 125, 125, 0.15);
  color: var(--text-dim);
}

.status-unheld-key {
  background: rgba(224, 175, 104, 0.2);
  color: var(--yellow);
}

.status-expiring {
  background: rgba(224, 175, 104, 0.2);
  color: var(--yellow);
}

.status-expired {
  background: rgba(247, 118, 142, 0.2);
  color: var(--red);
}

.templates-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 200px;
  overflow-y: auto;
  margin-bottom: 12px;
}

.template-row {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 4px 6px;
  border: 1px solid var(--border);
  border-radius: 4px;
}

.template-row-name {
  font-family: var(--font-mono);
  font-size: 12px;
}

.template-row-meta {
  flex: 1;
  font-size: 11px;
  color: var(--text-dim);
}

#issue-add-claim {
  align-self: flex-start;
}

.form-error {
  color: var(--red);
  font-size: 11px;
  margin-top: 8px;
  white-space: pre-wrap;
  word-break: break-all;
}

.form-error:empty {
  display: none;
}

/* Consent Dialog */
.consent-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.6);
  z-index: 100;
  align-items: center;
  justify-content: center;
}

.consent-overlay.active {
  display: flex;
}

.consent-dialog {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 20px;
  max-width: 600px;
  width: 90%;
  max-height: 80vh;
  overflow-y: auto;
}

/* A small kicker, so the who block leads. */
.consent-title {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  margin-bottom: 10px;
}

/* Who is asking: the self-asserted name when the request carried one, the
   authenticated client_id (or the issuer) beneath it, and the request-object
   authentication state. */
.who {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 11px 12px;
  margin-bottom: 12px;
}
.who-nm {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.who-name { font-weight: 600; font-size: 14px; overflow-wrap: anywhere; }
.who-cid { font-size: 11.5px; color: var(--text-dim); margin-top: 4px; overflow-wrap: anywhere; }
.who-chip {
  margin-left: auto;
  font-size: 10.5px;
  font-weight: 600;
  padding: 1px 8px;
  border-radius: 20px;
  white-space: nowrap;
}
.who-ok { background: rgba(122, 162, 247, 0.16); color: var(--accent); }
.who-bad { background: rgba(247, 118, 142, 0.2); color: var(--red); }

.consent-purpose {
  font-size: 12px;
  border-left: 3px solid var(--accent);
  border-radius: 4px;
  padding: 6px 10px;
  margin-bottom: 12px;
  background: var(--bg);
}

.consent-purpose-label {
  display: block;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  margin-bottom: 2px;
}

/* A consent credential is a block: the reused card, then the disclosed fields.
   The query-group wrapper is also a .consent-credential but only holds the
   candidate list, so it stays a plain stacking block. */
.consent-credential {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px;
  margin-bottom: 12px;
  background: var(--bg);
}

/* The same card the list renders, laid out compactly in the dialog. The list's
   responsive layout is scoped to the list container, so the dialog sizes the
   face itself (a fixed thumbnail) rather than the row/hero the list switches
   between. */
.consent-credential .credential-card,
.candidate .credential-card {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  padding: 0;
  overflow: visible;
}
.consent-credential .card-face,
.candidate .card-face {
  width: 58px;
  aspect-ratio: 8 / 5;
  border-radius: 7px;
  flex: 0 0 auto;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
}
.consent-credential .credential-info,
.candidate .credential-info { flex: 1; min-width: 0; }
.consent-credential .face-name,
.candidate .face-name,
.consent-credential .format-badge-face,
.candidate .format-badge-face { display: none; }
/* The logo is a small corner badge, so the face's color or art shows behind it
   (the list sizes it through a container query that does not reach the dialog). */
.consent-credential .credential-logo,
.candidate .credential-logo {
  top: 5px;
  left: 5px;
  width: 16px;
  height: 16px;
  padding: 2px;
}
.consent-credential .face-init,
.candidate .face-init,
.consent-credential .face-generic,
.candidate .face-generic { font-size: 18px; }
/* The dialog card is narrow, so its meta keeps only what helps pick the right
   credential (id, iat, type) and drops the long issuer that would wrap it tall. */
.consent-credential .cred-m-iss,
.candidate .cred-m-iss { display: none; }

/* The header above the disclosed fields (and the offer's claim list). */
.cl-hd {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-dim);
  font-weight: 600;
  margin: 12px 0 6px;
}
.cl-count {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
  color: var(--text-dim);
}

/* The disclosed fields, as selective-disclosure checkboxes. */
.consent-claims {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
}
.consent-claim {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  font-size: 12px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
}
.consent-claim:last-child { border-bottom: none; }
.consent-claim input[type="checkbox"] {
  accent-color: var(--accent);
  flex: 0 0 auto;
}
.consent-claim-name {
  color: var(--accent);
  flex: 0 0 42%;
  overflow-wrap: anywhere;
}
.consent-claim-value {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The offer preview lists claim names only (no value, not selectable). */
.offer-claims .consent-claim { cursor: default; }
.offer-description {
  font-size: 12px;
  color: var(--text-dim);
  margin-top: 4px;
}

/* The consent dialog's credential selection: one quiet row on the main
   screen, the set options and candidate rows on the Edit screen. */
.consent-selection-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: var(--text-dim);
  margin-bottom: 12px;
}

.consent-selection-row .btn {
  margin-left: auto;
}

.link-btn {
  font-family: var(--font-mono);
  font-size: 11px;
  border: none;
  background: none;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
  padding: 0;
}

.consent-sets {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 12px;
  margin-bottom: 12px;
  background: var(--bg);
}

.consent-section-label {
  display: block;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  margin-bottom: 6px;
}

.consent-set-option {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  padding: 3px 0;
}

.consent-set-option input[type="radio"] {
  accent-color: var(--accent);
}

.query-chip {
  font-size: 10px;
  padding: 1px 7px;
  border-radius: 10px;
  border: 1px solid var(--border);
  color: var(--text);
}

.auto-chip {
  font-size: 9px;
  padding: 1px 6px;
  border-radius: 10px;
  border: 1px solid var(--accent);
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  /* A surface behind it so it reads over a candidate card's background art,
     the way the credential list's buttons do. */
  background: var(--bg-surface);
}

/* The query group's header: the query id and how many credentials match. */
.consent-credential-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.query-id-label {
  font-size: 11px;
  font-weight: 600;
}

.candidate-count {
  font-size: 10px;
  color: var(--text-dim);
  margin-left: auto;
}

.candidate {
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 7px 9px;
  margin-bottom: 6px;
  cursor: pointer;
  background: var(--bg-surface);
}

.candidate:hover {
  background: var(--bg-hover);
}

/* The selected candidate is highlighted rather than the others dimmed: the
   honest pills (Revoked, Bound to another key) carry that on their own. */
.candidate.selected {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, var(--bg-surface));
  cursor: default;
}

/* A candidate holds the reused card between its radio and the decoder link. */
.candidate-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  flex-wrap: wrap;
}

.candidate-row input[type="radio"] {
  accent-color: var(--accent);
  flex-shrink: 0;
}

/* The wallet's own choice at the top, the decoder link at the bottom, so the
   auto chip keeps the corner to itself. */
.candidate-actions {
  flex-shrink: 0;
  align-self: stretch;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  gap: 8px;
}
/* The auto chip stays pinned to the top right, out of the flow, so the Show
   button below it centers beside the card on its own. */
.candidate-actions .auto-chip {
  position: absolute;
  top: 0;
  right: 0;
}

/* The Show link matches the credential list's action button (.btn.btn-sm). */
.candidate-decode {
  white-space: nowrap;
  text-decoration: none;
}

.consent-buttons {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 16px;
}

.error-detail {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 12px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text);
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 300px;
  overflow-y: auto;
  margin-top: 12px;
}

/* Activity Log */
/* The activity log lives in a drawer pinned above the footer: the credentials
   own the main scroll, and the log is a bounded, collapsible region of its own,
   so it is a glance away without ever pushing the cards down. */
.activity-drawer {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-top: 1px solid var(--border);
  background: var(--bg-surface);
}
.drawer-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 16px;
  cursor: pointer;
  user-select: none;
}
.drawer-head:hover {
  background: var(--bg-hover);
}
.drawer-head .section-title {
  margin: 0;
}
.drawer-head-right {
  display: flex;
  align-items: center;
  gap: 10px;
}
.drawer-chev {
  color: var(--text-dim);
  font-size: 15px;
  line-height: 1;
  transition: transform 0.15s;
}
.activity-drawer.collapsed .drawer-chev {
  transform: rotate(-90deg);
}
.drawer-body {
  overflow-y: auto;
  scrollbar-gutter: stable;
  max-height: 32vh;
  padding: 4px 16px 12px;
}
.activity-drawer.collapsed .drawer-body {
  display: none;
}

.log-entries {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.log-entry {
  font-size: 11px;
  border-radius: 4px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  overflow: hidden;
  /* Keep each entry its natural height inside the bounded, scrolling drawer,
     so a full log overflows and scrolls rather than squeezing every row flat. */
  flex-shrink: 0;
}

.log-header {
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 6px 10px;
}

.log-entry.has-details .log-header {
  cursor: pointer;
  user-select: none;
}

.log-entry.has-details .log-header:hover {
  background: var(--bg-hover);
}

.log-chevron {
  color: var(--text-dim);
  width: 10px;
  flex-shrink: 0;
  transition: transform 0.15s;
}

.log-entry.expanded .log-chevron {
  transform: rotate(90deg);
}

.log-details {
  display: none;
  border-top: 1px solid var(--border);
  padding: 10px 12px;
}

.log-entry.expanded .log-details {
  display: block;
}

.log-fields {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 3px 12px;
}

.log-key {
  color: var(--yellow);
  font-weight: 600;
}

.log-value {
  color: var(--text);
  word-break: break-all;
  min-width: 0;
}

.log-value pre {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 8px;
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 300px;
  overflow-y: auto;
}

.log-time {
  color: var(--text-dim);
  min-width: 65px;
}

.log-action {
  font-weight: 600;
  min-width: 90px;
}

.log-action.presentation { color: var(--accent); }
.log-action.issuance { color: var(--green); }

.log-detail {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.log-status {
  font-weight: 600;
}

.log-status.success { color: var(--green); }
.log-status.failure { color: var(--red); }
.log-status.warning { color: var(--yellow); white-space: nowrap; }

.empty-state {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px;
  color: var(--text-dim);
  font-size: 13px;
}

/* Responsive */
@media (max-width: 768px) {
  body {
    overflow: auto;
    height: auto;
    min-height: 100vh;
  }

  .main {
    overflow: visible;
    flex: none;
  }

  header {
    flex-wrap: wrap;
    gap: 8px;
  }

  /* Narrow screens: the six links do not fit on one line, so the toggle (which
     sits with Theme on the right) reveals them as a dropdown. It is absolutely
     positioned below the header so it never reflows the logo, wordmark or the
     Menu/Theme buttons on the top row. */
  .header-menu-toggle {
    display: inline-flex;
    align-items: center;
  }

  .header-links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 14px;
    padding: 14px 20px;
    font-size: 14px;
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
    z-index: 50;
  }

  .header-links.open {
    display: flex;
  }

  #pending-banner {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 8px 20px;
  font-size: 12px;
  color: var(--text);
  background: rgba(122, 162, 247, 0.14);
  border-bottom: 1px solid var(--accent);
}

#pending-banner[hidden] {
  display: none;
}

#pending-banner button {
  flex-shrink: 0;
  margin-left: auto;
}

#demo-banner {
    align-items: flex-start;
    flex-wrap: wrap;
  }
}

/* Conformance dialog: what incoming requests are held to. */
.conformance-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  margin: 12px 0;
}
.conformance-table td {
  padding: 5px 8px;
  border-bottom: 1px solid var(--border);
}
.conformance-table tr:last-child td {
  border-bottom: none;
}
.conformance-table td:first-child {
  color: var(--text-dim);
  width: 55%;
}
.conformance-table td:last-child {
  color: var(--text);
}
/* Green is reserved for something actually being enforced; anything that is
   off reads as neutral rather than as a failure, and plain settings stay
   plain so the enforcement rows are the ones that stand out. */
.conformance-table td.conf-on {
  font-weight: 600;
  color: var(--green);
}
.conformance-table td.conf-off {
  color: var(--text-dim);
}

/* Editable conformance controls. */
.conf-input {
  background: var(--bg-surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 3px 8px;
  font-size: 12px;
  font-family: inherit;
}
.conf-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text);
  cursor: pointer;
}
/* Keep the read-only demo controls legible: an accent-coloured box reads
   clearly checked or unchecked, and the label text stays full strength rather
   than the browser's greyed-out default. */
.conf-toggle input,
.conf-input {
  accent-color: var(--accent);
}
.conf-toggle input {
  cursor: pointer;
}
.conf-toggle:has(input:disabled),
.conf-input:disabled {
  cursor: default;
  color: var(--text);
  opacity: 1;
}

/* Credential offer details in the consent dialog. */
.offer-facts {
  font-size: 11px;
  margin: 8px 0 4px;
}
.offer-facts > div {
  display: flex;
  gap: 8px;
  padding: 2px 0;
}
.offer-fact-name {
  color: var(--text-dim);
  min-width: 120px;
}
/* The error and submission-result dialogs share the consent overlay but keep
   a prominent title of their own (the consent title is a small kicker). */
.dialog-title {
  font-size: 15px;
  font-weight: 700;
  margin-bottom: 8px;
}
.dialog-message {
  font-size: 13px;
  color: var(--text);
  line-height: 1.5;
  margin-bottom: 12px;
}

/* Trust & certificates dialog: two labelled sections, each item on its own
   row with a one-line explanation, so the dialog can be scanned instead of
   read start to finish. */
.trust-section {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 12px;
  margin-bottom: 10px;
  background: var(--bg);
}
.trust-section-title {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  margin-bottom: 4px;
}
.trust-section-hint {
  font-size: 11px;
  color: var(--text-dim);
  margin-bottom: 8px;
  line-height: 1.5;
}
.trust-items {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 6px 14px;
  align-items: baseline;
  margin: 0;
}
.trust-items dt {
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}
.trust-items dd {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
/* A trust-list group lists several entries under one term. */
.trust-items dd .trust-links + .trust-item-hint {
  margin-bottom: 4px;
}
.trust-links {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  font-size: 11px;
}
.trust-links a {
  color: var(--accent);
  text-decoration: none;
}
.trust-links a:hover {
  text-decoration: underline;
}
.trust-item-hint {
  font-size: 11px;
  color: var(--text-dim);
  line-height: 1.45;
}
@media (max-width: 480px) {
  .trust-items {
    grid-template-columns: 1fr;
    gap: 2px;
  }
  .trust-items dt {
    margin-top: 6px;
  }
}

/* Credentials an issuer deferred, still being collected. */
.deferred-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 12px;
}
/* An awaiting item reads like a credential row: the card face the offer
   declared on the left, dimmed while the credential is still on its way, and
   the detail beside it. */
.deferred-item {
  display: flex;
  align-items: center;
  gap: 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  background: var(--bg);
}
.deferred-item .card-face {
  width: 76px;
  aspect-ratio: 8 / 5;
  border-radius: 8px;
  flex: 0 0 auto;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  /* The credential is not here yet, so the art is held back until it arrives. */
  opacity: 0.6;
  filter: saturate(0.85);
}
.deferred-item .card-face .face-name,
.deferred-item .card-face .format-badge-face {
  display: none;
}
.deferred-item .credential-logo {
  top: 5px;
  left: 5px;
  width: 18px;
  height: 18px;
  padding: 2px;
}
.deferred-item .face-init,
.deferred-item .face-generic {
  font-size: 18px;
}
.deferred-body {
  flex: 1;
  min-width: 0;
}
.deferred-item-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}
.deferred-awaiting {
  margin-left: 0;
  background: color-mix(in srgb, var(--yellow) 18%, transparent);
  color: var(--yellow);
}
.deferred-status {
  display: flex;
  align-items: center;
  gap: 6px;
}
.deferred-spinner {
  flex: 0 0 auto;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--text-dim) 45%, transparent);
  border-top-color: var(--accent);
  animation: deferred-spin 0.9s linear infinite;
}
@keyframes deferred-spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .deferred-spinner { animation: none; }
}
.deferred-name {
  font-size: 13px;
  font-weight: 600;
}
.deferred-meta {
  font-size: 11px;
  color: var(--text-dim);
  word-break: break-all;
}
.deferred-error {
  color: var(--red);
}
.deferred-actions {
  display: flex;
  gap: 6px;
  margin-top: 6px;
}
.btn-sm {
  font-size: 10px;
  padding: 2px 8px;
}

/* Transaction code entry, for an offer that requires one. */
.offer-tx-code {
  margin: 10px 0 4px;
}
.offer-tx-code label {
  display: block;
  font-size: 11px;
  color: var(--text-dim);
  margin-bottom: 4px;
}
.offer-tx-code input[type="text"] {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 2px;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--text);
  width: 100%;
}
.offer-tx-code input[type="text"]::placeholder {
  letter-spacing: normal;
  color: var(--text-dim);
}
.offer-tx-code input.input-error {
  border-color: var(--red);
}
.offer-description {
  font-size: 11px;
  line-height: 1.5;
  color: var(--text-dim);
  margin-top: 4px;
}
