/* Vault interface system.
   ---------------------------------------------------------------------------
   One accent, used only for the primary action and the current location.
   Danger, warning and success are a separate state language and never borrow
   the accent's job, because colour has to stay free to mean "this password is
   in a breach corpus".

   Neutrals are cooled toward the accent rather than left as stock grey. Type
   is a system stack on purpose: a self-hosted privacy tool with a strict CSP
   has no business fetching a font from a third party on every page load. */

:root {
  color-scheme: light;

  --canvas: #eff1f5;
  --surface: #ffffff;
  --surface-subtle: #e9ecf3;
  --surface-pressed: #dde2ec;
  --surface-raised: #f7f8fb;
  --ink: #131721;
  --ink-soft: #3b4351;
  --ink-faint: #5a6376;
  --line: #d6dae3;
  --line-strong: #b9c0ce;
  --accent: #24479b;
  --accent-strong: #1a3577;
  --accent-soft: #e4ebfb;
  --danger: #a62b1c;
  --danger-strong: #7f2114;
  --danger-soft: #f9e5e2;
  --warning: #7d5407;
  --warning-soft: #f8eed7;
  --success: #1b6740;
  --success-soft: #dff0e6;
  --focus: #2f5cc4;
  --on-accent: #ffffff;

  --shadow: 0 1px 2px rgb(15 18 24 / 7%), 0 10px 30px -14px rgb(15 18 24 / 20%);
  --shadow-lift: 0 2px 6px rgb(15 18 24 / 10%), 0 20px 46px -18px rgb(15 18 24 / 26%);

  --radius-sm: 0.35rem;
  --radius-md: 0.55rem;
  --radius-lg: 0.85rem;

  --font:
    "Segoe UI Variable", "Aptos", "Segoe UI", system-ui, -apple-system,
    sans-serif;
  --mono:
    "Cascadia Code", "Cascadia Mono", ui-monospace, "SF Mono", Menlo,
    Consolas, monospace;

  /* Row height for entry lists. Settings swaps this for compact density. */
  --row-pad: 0.6rem;

  /* How long a line of body copy is allowed to get, in `ch` so the limit is a
     character count rather than a pixel width and holds at every type size.

     One token because it was previously five guesses (60, 62, 66, 68 and 70),
     each set where a rule happened to be written. All five landed near half the
     width of the 64rem containers they sit in, so every panel on the site read
     as a short column of text with an empty right-hand half, and paragraphs
     broke two or three words before they had to. Tune here, not per rule. */
  --measure: 84ch;

  font-family: var(--font);
  line-height: 1.5;
  text-rendering: optimizeLegibility;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --canvas: #0f1218;
    --surface: #171b24;
    --surface-subtle: #1d222d;
    --surface-pressed: #262c38;
    --surface-raised: #1d222d;
    --ink: #eef1f6;
    --ink-soft: #b3bccb;
    --ink-faint: #8d97a8;
    --line: #262c38;
    --line-strong: #39414f;
    --accent: #7ba3f5;
    --accent-strong: #9dbcff;
    --accent-soft: #1b2740;
    --danger: #f4948a;
    --danger-strong: #ffb0a7;
    --danger-soft: #3b201c;
    --warning: #e0af62;
    --warning-soft: #372c17;
    --success: #6fcb9b;
    --success-soft: #17321f;
    --focus: #9dbcff;
    --on-accent: #0f1218;

    --shadow: 0 1px 2px rgb(0 0 0 / 42%), 0 12px 32px -14px rgb(0 0 0 / 70%);
    --shadow-lift: 0 2px 6px rgb(0 0 0 / 48%), 0 22px 50px -18px rgb(0 0 0 / 78%);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --canvas: #0f1218;
  --surface: #171b24;
  --surface-subtle: #1d222d;
  --surface-pressed: #262c38;
  --surface-raised: #1d222d;
  --ink: #eef1f6;
  --ink-soft: #b3bccb;
  --ink-faint: #8d97a8;
  --line: #262c38;
  --line-strong: #39414f;
  --accent: #7ba3f5;
  --accent-strong: #9dbcff;
  --accent-soft: #1b2740;
  --danger: #f4948a;
  --danger-strong: #ffb0a7;
  --danger-soft: #3b201c;
  --warning: #e0af62;
  --warning-soft: #372c17;
  --success: #6fcb9b;
  --success-soft: #17321f;
  --focus: #9dbcff;
  --on-accent: #0f1218;

  --shadow: 0 1px 2px rgb(0 0 0 / 42%), 0 12px 32px -14px rgb(0 0 0 / 70%);
  --shadow-lift: 0 2px 6px rgb(0 0 0 / 48%), 0 22px 50px -18px rgb(0 0 0 / 78%);
}

:root[data-density="compact"] {
  --row-pad: 0.35rem;
}

/* ===========================================================================
   Base
   =========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--font);
  font-size: 1rem;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0;
  line-height: 1.2;
  letter-spacing: -0.011em;
  text-wrap: balance;
  font-weight: 600;
}

h1 { font-size: 1.75rem; }
h2 { font-size: 1.25rem; }
h3 { font-size: 1.02rem; }
h4 { font-size: 0.94rem; }

p { margin: 0; }

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

a:hover { color: var(--accent-strong); }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

code,
kbd,
samp,
pre {
  font-family: var(--mono);
  font-size: 0.9em;
}

time,
.tabular {
  font-variant-numeric: tabular-nums;
}

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 1.5rem 0;
}

svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.icon-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: 0.5rem;
  top: -3rem;
  z-index: 100;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  padding: 0.5rem 0.9rem;
  box-shadow: var(--shadow);
  transition: top 0.12s ease;
}

.skip-link:focus {
  top: 0.5rem;
}

/* ===========================================================================
   Forms
   =========================================================================== */

label {
  display: block;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--ink-soft);
  margin-bottom: 0.3rem;
}

input,
select,
textarea {
  font: inherit;
  font-size: 0.94rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  padding: 0.5rem 0.65rem;
  width: 100%;
  max-width: 100%;
}

input[type="checkbox"],
input[type="radio"] {
  width: auto;
  accent-color: var(--accent);
  inline-size: 1rem;
  block-size: 1rem;
}

textarea {
  resize: vertical;
  min-height: 3rem;
}

input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  border-color: var(--accent);
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

input::placeholder,
textarea::placeholder {
  color: var(--ink-faint);
}

input:disabled,
select:disabled,
textarea:disabled {
  background: var(--surface-subtle);
  color: var(--ink-faint);
}

fieldset {
  border: 0;
  margin: 0;
  padding: 0;
  min-inline-size: 0;
}

legend {
  padding: 0;
  font-weight: 600;
}

.form-row {
  display: flex;
  flex-direction: column;
  gap: 0;
  min-width: 0;
}

.form-row + .form-row {
  margin-top: 0.85rem;
}

.field-hint {
  font-size: 0.84rem;
  color: var(--ink-faint);
  margin: 0 0 0.55rem;
  max-width: var(--measure);
}

.field-suffix {
  font-size: 0.84rem;
  color: var(--ink-faint);
}

.numeric-field {
  max-width: 7rem;
}

.checkbox-label {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-weight: 400;
  color: var(--ink);
  margin: 0;
}

.inline-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.7rem;
}

.inline-form > * {
  min-width: 0;
}

.inline-form > input,
.inline-form > select {
  flex: 1 1 12rem;
  width: auto;
}

/* A labelled field and its action on one line. */
.field-row {
  display: flex;
  align-items: flex-end;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.field-row > .form-row {
  flex: 1 1 12rem;
}

.field-row > button,
.field-row > .button {
  flex: none;
}

/* Two columns where a card has two jobs, collapsing to one when there is not
   room for both. Keeps a card from stacking everything down its left edge. */
.resource-card-header {
  margin-bottom: 1.25rem;
}

.card-columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: 1.75rem;
  align-items: start;
}

.card-column > h3 {
  margin-bottom: 0.6rem;
}

.card-divider {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 1.25rem 0;
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
  margin-top: 1.1rem;
}

.filter-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.85rem;
  align-items: end;
}

.filter-form .form-row + .form-row {
  margin-top: 0;
}

.filter-form button {
  align-self: end;
}

/* ===========================================================================
   Buttons
   =========================================================================== */

/* An author display value beats the user agent's [hidden] rule, so every
   button this stylesheet reaches would stay visible while carrying the
   attribute that says it is not. Progressive enhancement ships a control
   hidden and lets script reveal it; without this the reader gets a live
   looking button that does nothing. Stated once here rather than per class,
   because the rule below is what creates the hazard. */
button[hidden],
.button[hidden] {
  display: none;
}

button,
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  font: inherit;
  font-size: 0.9rem;
  font-weight: 500;
  line-height: 1.2;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  padding: 0.5rem 0.85rem;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}

button:hover,
.button:hover {
  background: var(--surface-subtle);
  border-color: var(--accent);
  color: var(--ink);
}

button:active,
.button:active {
  background: var(--surface-pressed);
}

button:disabled,
.button[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
}

/* The primary action of a page. A page has at most one, and it is the only
   place the accent is allowed to fill a shape. */
.button-primary,
button.button-primary,
button[type="submit"].button-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

.button-primary:hover,
button.button-primary:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: var(--on-accent);
}

.button-danger,
button.button-danger {
  background: var(--danger);
  border-color: var(--danger);
  color: var(--on-accent);
}

.button-danger:hover,
button.button-danger:hover {
  background: var(--danger-strong);
  border-color: var(--danger-strong);
  color: var(--on-accent);
}

/* Turning something off is not the loudest thing on the page. */
.button-danger-quiet,
button.button-danger-quiet {
  background: transparent;
  border-color: var(--line-strong);
  color: var(--danger);
}

.button-danger-quiet:hover,
button.button-danger-quiet:hover {
  background: var(--danger-soft);
  border-color: var(--danger);
  color: var(--danger);
}

.button-copy {
  padding: 0.3rem 0.6rem;
  font-size: 0.8rem;
  font-family: var(--mono);
}

.icon-button {
  padding: 0.45rem;
  width: 2.25rem;
  height: 2.25rem;
}

.icon-button svg {
  width: 1.15rem;
  height: 1.15rem;
}

.icon-button-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

.icon-button-primary:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: var(--on-accent);
}

.danger-link {
  color: var(--danger);
}

.secondary-link {
  font-size: 0.88rem;
  margin-top: 0.75rem;
}

/* ===========================================================================
   Shell
   =========================================================================== */

.workspace-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.workspace-header,
.site-header {
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 1rem;
  max-width: 84rem;
  margin: 0 auto;
  padding: 0 1rem;
  height: 3.25rem;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
  flex: none;
}

.brand:hover {
  color: var(--ink);
}

.brand-mark {
  display: grid;
  place-items: center;
  width: 1.65rem;
  height: 1.65rem;
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--accent);
}

.brand-icon {
  width: 1rem;
  height: 1rem;
}

.workspace-nav .workspace-destination {
  color: var(--ink-soft);
  text-decoration: none;
  font-size: 0.92rem;
  font-weight: 500;
  padding: 0.35rem 0.7rem;
  border-radius: var(--radius-md);
  flex: none;
}

.workspace-nav .workspace-destination:hover {
  background: var(--surface-subtle);
  color: var(--ink);
}

.workspace-nav .workspace-destination[aria-current="page"] {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}

/* Search lives in the chrome because it is how you navigate, not a feature of
   one page. */
.shell-search {
  flex: 1 1 auto;
  max-width: 24rem;
  min-width: 0;
  margin: 0;
  position: relative;
}

.shell-search-field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--canvas);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  padding: 0.35rem 0.6rem;
  color: var(--ink-faint);
  cursor: text;
}

.shell-search-field svg {
  width: 0.95rem;
  height: 0.95rem;
  flex: none;
}

.shell-search-field input {
  border: 0;
  background: none;
  padding: 0;
  font-size: 0.9rem;
  flex: 1;
  min-width: 0;
}

.shell-search-field input:focus-visible {
  outline: none;
}

.shell-search:focus-within .shell-search-field {
  border-color: var(--focus);
}

.kbd-hint {
  font-family: var(--mono);
  font-size: 0.7rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: 0.05rem 0.3rem;
  color: var(--ink-faint);
  background: var(--surface-subtle);
  flex: none;
}

.shell-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  flex: none;
}

/* The account menu. A native <details>, so it announces its expanded state
   and works with JavaScript off. */
.nav-menu {
  position: relative;
  flex: none;
}

.nav-menu > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.25rem;
  border-radius: 50%;
}

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

.nav-menu > summary:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.avatar {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  display: grid;
  place-items: center;
  font-size: 0.78rem;
  font-weight: 600;
}

.nav-menu-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 0.5rem);
  min-width: 14rem;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lift);
  padding: 0.4rem;
  z-index: 40;
}

.nav-menu-panel p {
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding: 0.5rem 0.6rem 0.25rem;
}

.nav-menu-panel ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-menu-panel a {
  display: block;
  padding: 0.4rem 0.6rem;
  border-radius: var(--radius-md);
  color: var(--ink-soft);
  text-decoration: none;
  font-size: 0.9rem;
}

.nav-menu-panel a:hover {
  background: var(--surface-subtle);
  color: var(--ink);
}

.nav-menu-panel hr {
  margin: 0.35rem 0.6rem;
}

/* The New button and the type menu beside it. Offering the choice must not
   cost the reader who did not need one, so the caret is a second target
   inside the same shape rather than a menu the button turns into. */
.split-button {
  display: inline-flex;
  flex: none;
}

.split-button > .button-primary {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

/* Laid out as a flex parent so the caret takes the button's height rather
   than its own content's, which keeps the seam straight however long the
   button's label runs in the reader's language. */
.split-button > .nav-menu {
  display: flex;
}

.split-button > .nav-menu > summary {
  padding: 0.5rem;
  border: 1px solid var(--accent);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  background: var(--accent);
  color: var(--on-accent);

  /* Pulled onto the button's border so the pair carries one line between
     them rather than two, and that line is shaded to read as a seam. */
  margin-left: -1px;
  border-left-color: var(--accent-strong);
}

.split-button > .nav-menu > summary:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
}

.split-button > .nav-menu > summary svg {
  width: 1rem;
  height: 1rem;
}

/* ===========================================================================
   Choosing an entry type
   =========================================================================== */

/* Offered when the address names no template. Laid out rather than folded
   into a control, because this is the whole content of the page at that
   point and there is nothing for a reader to do here but read it. */
.type-choices h2 {
  font-size: 1rem;
  margin-bottom: 0.75rem;
}

.type-choices ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 0.75rem;
}

.type-choices a {
  display: block;
  height: 100%;
  padding: 0.9rem 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: var(--ink);
}

.type-choices a:hover {
  border-color: var(--accent);
  background: var(--surface-subtle);
}

.type-choices a:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.type-choices strong {
  display: block;
  font-size: 0.95rem;
}

.type-choices small {
  display: block;
  margin-top: 0.2rem;
  color: var(--ink-faint);
  font-size: 0.82rem;
  line-height: 1.45;
}

.workspace-main {
  flex: 1;
  width: 100%;
}

.workspace-main:focus-visible {
  outline: none;
}

.workspace-footer,
.public-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 1.1rem;
  max-width: 84rem;
  margin: 0 auto;
  padding: 1.5rem 1rem;
  color: var(--ink-faint);
  font-size: 0.82rem;
}

/* The footer is chrome, not content. A link here reads as its neighbour's
   weight rather than borrowing the accent, which is spent on the primary
   action and the current location. */
.workspace-footer a,
.public-footer a {
  color: var(--ink-faint);
}

.workspace-footer a:hover,
.public-footer a:hover {
  color: var(--ink);
}

.public-nav ul {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 0 auto;
  padding: 0;
}

.public-nav a {
  text-decoration: none;
  color: var(--ink-soft);
  font-size: 0.92rem;
  padding: 0.4rem 0.6rem;
  border-radius: var(--radius-md);
}

.public-nav a.button-primary {
  color: var(--on-accent);
}

.public-main {
  min-height: calc(100vh - 6rem);
}

/* ===========================================================================
   Page structure
   =========================================================================== */

.page-stack {
  max-width: 64rem;
  margin: 0 auto;
  padding: 1.75rem 1rem 3rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.page-heading {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.page-heading-actions {
  flex-direction: row;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

.eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
}

.page-summary {
  color: var(--ink-soft);
  max-width: var(--measure);
  font-size: 0.95rem;
}

.section-heading {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 0.85rem;
}

.section-heading > div {
  flex: 1;
  min-width: 0;
}

.section-heading p {
  color: var(--ink-faint);
  font-size: 0.87rem;
  margin-top: 0.2rem;
  max-width: var(--measure);
}

.compact-heading {
  margin-bottom: 0.5rem;
}

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
}

.panel > header {
  margin-bottom: 0.85rem;
}

.panel > header p {
  color: var(--ink-faint);
  font-size: 0.87rem;
  margin-top: 0.25rem;
  max-width: var(--measure);
}

.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  color: var(--ink-faint);
  flex-wrap: wrap;
}

.breadcrumb a {
  text-decoration: none;
}

.breadcrumb a:hover {
  text-decoration: underline;
}

.count-badge {
  font-family: var(--mono);
  font-size: 0.78rem;
  background: var(--surface-subtle);
  color: var(--ink-soft);
  border-radius: 999px;
  padding: 0.1rem 0.5rem;
  font-variant-numeric: tabular-nums;
  flex: none;
}

/* ===========================================================================
   Messages, status, empty states
   =========================================================================== */

.form-message {
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  background: var(--accent-soft);
  color: var(--ink);
  border-radius: var(--radius-md);
  padding: 0.7rem 0.9rem;
  font-size: 0.92rem;
}

.form-message-error {
  border-left-color: var(--danger);
  background: var(--danger-soft);
  color: var(--ink);
}

span.form-message-error {
  display: block;
  border: 0;
  background: none;
  padding: 0.3rem 0 0;
  color: var(--danger);
  font-size: 0.85rem;
}

.risk-notice {
  border: 1px solid var(--warning);
  background: var(--warning-soft);
  border-radius: var(--radius-md);
  padding: 0.8rem 0.95rem;
  font-size: 0.9rem;
  color: var(--ink);
}

.empty-state {
  color: var(--ink-faint);
  font-size: 0.92rem;
  padding: 0.85rem 0;
}

.empty-state-large {
  text-align: center;
  padding: 2.5rem 1rem;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.empty-state-large h3 {
  color: var(--ink);
  margin-bottom: 0.35rem;
}

.results-status {
  font-size: 0.85rem;
  color: var(--ink-faint);
  margin-bottom: 0.6rem;
}

.status-badge {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  border-radius: var(--radius-sm);
  padding: 0.12rem 0.4rem;
  background: var(--surface-subtle);
  color: var(--ink-soft);
  white-space: nowrap;
}

.status-badge.is-active {
  background: var(--success-soft);
  color: var(--success);
}

.status-badge.is-expired {
  background: var(--warning-soft);
  color: var(--warning);
}

.status-badge.is-revoked {
  background: var(--danger-soft);
  color: var(--danger);
}

/* The three tones a posture badge reports, kept separate from the lifecycle
   words above: "revoked" is a thing's state, "off" is a judgement about the
   account. */
.status-badge.is-good {
  background: var(--success-soft);
  color: var(--success);
}

.status-badge.is-warning {
  background: var(--warning-soft);
  color: var(--warning);
}

.status-badge.is-bad {
  background: var(--danger-soft);
  color: var(--danger);
}

/* Marking where the reader is, which is the accent's other job. */
.status-badge.is-current {
  background: var(--accent-soft);
  color: var(--accent);
}

.chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.78rem;
  background: var(--surface-subtle);
  color: var(--ink-soft);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.15rem 0.55rem;
  max-width: 100%;
}

/* A scope names a vault, collection or entry by its identifier, which is 36
   characters of monospace that cannot shrink. It wraps rather than pushing
   the card wider than the screen; truncating it would be the other way to
   stop that, and this one still shows the reader the whole value. */
.chip-target {
  font-family: var(--mono);
  min-width: 0;
  word-break: break-all;
}

.chip-remove {
  border: 0;
  background: none;
  padding: 0;
  width: 1rem;
  height: 1rem;
  color: var(--ink-faint);
  border-radius: 50%;
}

.chip-remove:hover {
  background: var(--danger-soft);
  color: var(--danger);
  border: 0;
}

.chip-remove svg {
  width: 0.7rem;
  height: 0.7rem;
}

/* ===========================================================================
   Tables
   =========================================================================== */

.session-table-wrapper,
.table-scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.data-table,
.session-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
}

.data-table caption,
.session-table caption {
  text-align: left;
  padding: 0.75rem 0.9rem;
  color: var(--ink-faint);
  font-size: 0.82rem;
  border-bottom: 1px solid var(--line);
}

.data-table th,
.session-table th {
  text-align: left;
  font-size: 0.72rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--line);
  background: var(--surface-raised);
  white-space: nowrap;
}

.data-table td,
.session-table td {
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--line);
  color: var(--ink-soft);
  vertical-align: top;
}

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

.data-table td time,
.session-table td time {
  font-family: var(--mono);
  font-size: 0.82rem;
  white-space: nowrap;
}

.pager {
  margin-top: 0.9rem;
  font-size: 0.9rem;
}

/* ===========================================================================
   Authentication pages
   =========================================================================== */

.auth-page,
.status-page {
  display: grid;
  place-items: center;
  padding: 3rem 1rem 4rem;
  min-height: calc(100vh - 10rem);
}

.auth-panel,
.status-panel {
  width: 100%;
  max-width: 26rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  padding: 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.status-panel {
  max-width: 32rem;
}

.auth-panel > header,
.status-panel > header {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.auth-panel > header p,
.status-panel > header p {
  color: var(--ink-faint);
  font-size: 0.9rem;
}

.auth-panel form {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.auth-panel form button[type="submit"] {
  margin-top: 0.2rem;
}

.auth-panel > p {
  font-size: 0.88rem;
}

.recovery-codes {
  list-style: none;
  margin: 0;
  padding: 0.9rem;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
  gap: 0.4rem;
  font-family: var(--mono);
  font-size: 0.9rem;
  background: var(--surface-subtle);
  border-radius: var(--radius-md);
}

.totp-qr {
  margin: 0 auto;
  background: #ffffff;
  padding: 0.6rem;
  border-radius: var(--radius-md);
  max-width: 100%;
}

/* The QR arrives as an svg with only a viewBox, so it has no intrinsic
   size and would collapse to nothing; and the line-icon defaults up top
   would stroke pale ink across its modules. Give it a real square and
   let its own fill attributes paint it. */
.totp-qr svg {
  display: block;
  width: 12rem;
  height: 12rem;
  stroke: none;
}

.manual-key {
  font-family: var(--mono);
  font-size: 0.92rem;
  word-break: break-all;
  background: var(--surface-subtle);
  border-radius: var(--radius-md);
  padding: 0.6rem 0.75rem;
  display: block;
}

/* ===========================================================================
   Entry identity
   =========================================================================== */

/* Entry identity is one of 24 hue buckets chosen from a hash of the title, so
   the same entry keeps the same colour on every device with no stored asset
   and no third-party favicon request.

   Buckets rather than an exact hue on a style attribute: the Content-Security
   -Policy sets style-src without 'unsafe-inline', so a style="--h:..." is
   dropped by the browser and every tile silently falls back to one colour. A
   class survives the policy. */
.entry-monogram {
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-md);
  display: grid;
  place-items: center;
  font-weight: 600;
  font-size: 0.85rem;
  flex: none;
  background: var(--surface-subtle);
  color: var(--ink-soft);
}

.hue-0 { background: hsl(0 34% 91%); color: hsl(0 46% 27%); }
.hue-1 { background: hsl(15 34% 91%); color: hsl(15 46% 27%); }
.hue-2 { background: hsl(30 34% 91%); color: hsl(30 46% 27%); }
.hue-3 { background: hsl(45 34% 91%); color: hsl(45 46% 27%); }
.hue-4 { background: hsl(60 34% 91%); color: hsl(60 46% 27%); }
.hue-5 { background: hsl(75 34% 91%); color: hsl(75 46% 27%); }
.hue-6 { background: hsl(90 34% 91%); color: hsl(90 46% 27%); }
.hue-7 { background: hsl(105 34% 91%); color: hsl(105 46% 27%); }
.hue-8 { background: hsl(120 34% 91%); color: hsl(120 46% 27%); }
.hue-9 { background: hsl(135 34% 91%); color: hsl(135 46% 27%); }
.hue-10 { background: hsl(150 34% 91%); color: hsl(150 46% 27%); }
.hue-11 { background: hsl(165 34% 91%); color: hsl(165 46% 27%); }
.hue-12 { background: hsl(180 34% 91%); color: hsl(180 46% 27%); }
.hue-13 { background: hsl(195 34% 91%); color: hsl(195 46% 27%); }
.hue-14 { background: hsl(210 34% 91%); color: hsl(210 46% 27%); }
.hue-15 { background: hsl(225 34% 91%); color: hsl(225 46% 27%); }
.hue-16 { background: hsl(240 34% 91%); color: hsl(240 46% 27%); }
.hue-17 { background: hsl(255 34% 91%); color: hsl(255 46% 27%); }
.hue-18 { background: hsl(270 34% 91%); color: hsl(270 46% 27%); }
.hue-19 { background: hsl(285 34% 91%); color: hsl(285 46% 27%); }
.hue-20 { background: hsl(300 34% 91%); color: hsl(300 46% 27%); }
.hue-21 { background: hsl(315 34% 91%); color: hsl(315 46% 27%); }
.hue-22 { background: hsl(330 34% 91%); color: hsl(330 46% 27%); }
.hue-23 { background: hsl(345 34% 91%); color: hsl(345 46% 27%); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
  .hue-0 { background: hsl(0 28% 22%); color: hsl(0 58% 76%); }
  .hue-1 { background: hsl(15 28% 22%); color: hsl(15 58% 76%); }
  .hue-2 { background: hsl(30 28% 22%); color: hsl(30 58% 76%); }
  .hue-3 { background: hsl(45 28% 22%); color: hsl(45 58% 76%); }
  .hue-4 { background: hsl(60 28% 22%); color: hsl(60 58% 76%); }
  .hue-5 { background: hsl(75 28% 22%); color: hsl(75 58% 76%); }
  .hue-6 { background: hsl(90 28% 22%); color: hsl(90 58% 76%); }
  .hue-7 { background: hsl(105 28% 22%); color: hsl(105 58% 76%); }
  .hue-8 { background: hsl(120 28% 22%); color: hsl(120 58% 76%); }
  .hue-9 { background: hsl(135 28% 22%); color: hsl(135 58% 76%); }
  .hue-10 { background: hsl(150 28% 22%); color: hsl(150 58% 76%); }
  .hue-11 { background: hsl(165 28% 22%); color: hsl(165 58% 76%); }
  .hue-12 { background: hsl(180 28% 22%); color: hsl(180 58% 76%); }
  .hue-13 { background: hsl(195 28% 22%); color: hsl(195 58% 76%); }
  .hue-14 { background: hsl(210 28% 22%); color: hsl(210 58% 76%); }
  .hue-15 { background: hsl(225 28% 22%); color: hsl(225 58% 76%); }
  .hue-16 { background: hsl(240 28% 22%); color: hsl(240 58% 76%); }
  .hue-17 { background: hsl(255 28% 22%); color: hsl(255 58% 76%); }
  .hue-18 { background: hsl(270 28% 22%); color: hsl(270 58% 76%); }
  .hue-19 { background: hsl(285 28% 22%); color: hsl(285 58% 76%); }
  .hue-20 { background: hsl(300 28% 22%); color: hsl(300 58% 76%); }
  .hue-21 { background: hsl(315 28% 22%); color: hsl(315 58% 76%); }
  .hue-22 { background: hsl(330 28% 22%); color: hsl(330 58% 76%); }
  .hue-23 { background: hsl(345 28% 22%); color: hsl(345 58% 76%); }
  }
}

:root[data-theme="dark"] {
  .hue-0 { background: hsl(0 28% 22%); color: hsl(0 58% 76%); }
  .hue-1 { background: hsl(15 28% 22%); color: hsl(15 58% 76%); }
  .hue-2 { background: hsl(30 28% 22%); color: hsl(30 58% 76%); }
  .hue-3 { background: hsl(45 28% 22%); color: hsl(45 58% 76%); }
  .hue-4 { background: hsl(60 28% 22%); color: hsl(60 58% 76%); }
  .hue-5 { background: hsl(75 28% 22%); color: hsl(75 58% 76%); }
  .hue-6 { background: hsl(90 28% 22%); color: hsl(90 58% 76%); }
  .hue-7 { background: hsl(105 28% 22%); color: hsl(105 58% 76%); }
  .hue-8 { background: hsl(120 28% 22%); color: hsl(120 58% 76%); }
  .hue-9 { background: hsl(135 28% 22%); color: hsl(135 58% 76%); }
  .hue-10 { background: hsl(150 28% 22%); color: hsl(150 58% 76%); }
  .hue-11 { background: hsl(165 28% 22%); color: hsl(165 58% 76%); }
  .hue-12 { background: hsl(180 28% 22%); color: hsl(180 58% 76%); }
  .hue-13 { background: hsl(195 28% 22%); color: hsl(195 58% 76%); }
  .hue-14 { background: hsl(210 28% 22%); color: hsl(210 58% 76%); }
  .hue-15 { background: hsl(225 28% 22%); color: hsl(225 58% 76%); }
  .hue-16 { background: hsl(240 28% 22%); color: hsl(240 58% 76%); }
  .hue-17 { background: hsl(255 28% 22%); color: hsl(255 58% 76%); }
  .hue-18 { background: hsl(270 28% 22%); color: hsl(270 58% 76%); }
  .hue-19 { background: hsl(285 28% 22%); color: hsl(285 58% 76%); }
  .hue-20 { background: hsl(300 28% 22%); color: hsl(300 58% 76%); }
  .hue-21 { background: hsl(315 28% 22%); color: hsl(315 58% 76%); }
  .hue-22 { background: hsl(330 28% 22%); color: hsl(330 58% 76%); }
  .hue-23 { background: hsl(345 28% 22%); color: hsl(345 58% 76%); }
}

.entry-monogram-lg {
  width: 2.6rem;
  height: 2.6rem;
  font-size: 1.05rem;
  border-radius: var(--radius-lg);
}

/* ===========================================================================
   Workbench: rail, list, detail
   =========================================================================== */

/* A height, not a minimum. The three columns below each ask to scroll on
   their own, and a minimum grants none of them: the grid row grows to its
   tallest column, so a hundred rows made the whole page scroll and every
   overflow-y here was a declaration with nothing to do. The rail and the
   entry then scrolled away with the list, and putting a selected row on
   screen meant taking the panel that describes it off screen. Bounding the
   frame is what makes the column scroll instead of the document. */
.workbench {
  display: grid;
  grid-template-columns: 13rem minmax(0, 22rem) minmax(0, 1fr);

  /* The row has to be bounded as well as the frame. A height on the grid
     alone leaves one auto row that grows to its tallest column and spills
     out of it, which looks exactly like no bound at all. */
  grid-template-rows: minmax(0, 1fr);
  height: calc(100vh - 3.25rem);
  align-items: stretch;
}

/* Layout that used to sit on style attributes. The Content-Security-Policy
   sets style-src without 'unsafe-inline', so an inline declaration is dropped
   by the browser without an error anyone would notice. The rail's bullets
   came back that way. Everything here is a class for that reason. */
.rail-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.workbench-notice {
  margin: 0.5rem;
}

.workbench-empty {
  margin: 0.75rem;
}

.workbench-more {
  padding: 0 0.8rem 0.8rem;
}

.workbench-article {
  padding: 0;
  max-width: none;
}

.panel-note {
  margin-top: 0.75rem;
}

.danger-note {
  margin: 0.35rem 0 0;
}

.workbench-rail {
  border-right: 1px solid var(--line);
  background: var(--surface-subtle);
  padding: 0.85rem 0.5rem 2rem;
  overflow-y: auto;
}

.rail-group {
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
  padding: 0.85rem 0.55rem 0.35rem;
}

.rail-group:first-child {
  padding-top: 0.15rem;
}

.rail-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.55rem;
  border-radius: var(--radius-md);
  color: var(--ink-soft);
  text-decoration: none;
  font-size: 0.88rem;
  width: 100%;
}

.rail-item:hover {
  background: var(--surface);
  color: var(--ink);
}

.rail-item[aria-current="page"] {
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
  box-shadow: inset 2px 0 0 var(--accent);
}

.rail-item svg {
  width: 0.95rem;
  height: 0.95rem;
  flex: none;
  opacity: 0.75;
}

.rail-count {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

/* The one number allowed to be red: a decision waiting on a human. */
.rail-count-attention {
  color: var(--danger);
  font-weight: 600;
}

.rail-sub {
  padding-left: 1.6rem;
  font-size: 0.85rem;
}

/* min-height because a grid item is allowed to refuse to shrink below its
   content, and this one holds the list that has to scroll instead. Its two
   neighbours get the same minimum from their own overflow-y. */
.workbench-list {
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--surface);
}

.workbench-list-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.8rem;
  border-bottom: 1px solid var(--line);
  font-size: 0.8rem;
  color: var(--ink-faint);
}

.workbench-list-head h2 {
  font-size: 0.9rem;
  color: var(--ink);
}

.entry-rows {
  list-style: none;
  margin: 0;
  padding: 0.3rem;
  overflow-y: auto;
  flex: 1;
}

.entry-row {
  display: grid;
  grid-template-columns: 2rem minmax(0, 1fr) auto;
  gap: 0.6rem;
  align-items: center;
  padding: var(--row-pad) 0.55rem;
  border-radius: var(--radius-md);
  text-decoration: none;
  color: var(--ink);
  position: relative;
}

.entry-row:hover {
  background: var(--surface-subtle);
  color: var(--ink);
}

/* The row is the grid; the link stretches across it so the whole row is
   clickable without nesting a button inside an anchor. */
.entry-row-link {
  text-decoration: none;
  color: inherit;
}

.entry-row-link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.entry-row.is-current {
  background: var(--accent-soft);
}

.entry-row.is-current::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.35rem;
  bottom: 0.35rem;
  width: 2px;
  border-radius: 2px;
  background: var(--accent);
}

.entry-row-main {
  min-width: 0;
}

.entry-row-main strong {
  display: block;
  font-weight: 500;
  font-size: 0.9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.entry-row-main small {
  display: block;
  color: var(--ink-faint);
  font-size: 0.78rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.entry-row-tail {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex: none;
  position: relative;
  z-index: 1;
}

/* Quick copy stays out of the way until the row is reachable, but risk never
   hides behind a hover. */
.entry-row-actions {
  display: flex;
  gap: 0.15rem;
  opacity: 0;
}

.entry-row:hover .entry-row-actions,
.entry-row:focus-within .entry-row-actions,
.entry-row.is-current .entry-row-actions {
  opacity: 1;
}

@media (hover: none) {
  .entry-row-actions {
    opacity: 1;
  }
}

.quick-action {
  font-family: var(--mono);
  font-size: 0.7rem;
  padding: 0.1rem 0.35rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink-soft);
}

.quick-action:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--surface);
}

.risk-dot {
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  background: var(--danger);
  flex: none;
}

.risk-dot-warning {
  background: var(--warning);
}

.workbench-detail {
  padding: 1.25rem 1.5rem 3rem;
  overflow-y: auto;
  min-width: 0;
  background: var(--surface);
}

/* Only meaningful where the detail pane has replaced the list. */
.detail-back {
  display: none;
  margin-bottom: 0.75rem;
  font-size: 0.9rem;
}

.detail-back a {
  text-decoration: none;
}

.workbench-placeholder {
  display: grid;
  place-items: center;
  padding: 4rem 1rem;
  color: var(--ink-faint);
  text-align: center;
}

/* ===========================================================================
   Attention band
   =========================================================================== */

.attention-band {
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  padding: 0.7rem 0.8rem 0.8rem;
}

.attention-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
  margin-bottom: 0.5rem;
}

.attention-head svg {
  width: 0.85rem;
  height: 0.85rem;
}

.attention-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.4rem;
}

.attention-item {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  font-size: 0.88rem;
  flex-wrap: wrap;
}

.attention-icon {
  width: 1.7rem;
  height: 1.7rem;
  border-radius: var(--radius-md);
  display: grid;
  place-items: center;
  flex: none;
}

.attention-icon svg {
  width: 0.9rem;
  height: 0.9rem;
}

.attention-icon-risk {
  background: var(--danger-soft);
  color: var(--danger);
}

.attention-icon-warning {
  background: var(--warning-soft);
  color: var(--warning);
}

.attention-icon-info {
  background: var(--accent-soft);
  color: var(--accent);
}

.attention-text {
  flex: 1;
  min-width: 12rem;
}

.attention-text strong {
  display: block;
  font-weight: 600;
}

.attention-text small {
  color: var(--ink-faint);
  font-size: 0.8rem;
}

.attention-actions {
  display: flex;
  gap: 0.4rem;
  flex: none;
  flex-wrap: wrap;
}

.attention-actions button,
.attention-actions .button {
  padding: 0.3rem 0.65rem;
  font-size: 0.82rem;
}

/* ===========================================================================
   Search results

   Anchored under the chrome's search box rather than floating over the page,
   so the results and the box they came from stay one control.
   =========================================================================== */

.shell-search-panel[hidden] {
  display: none;
}

.shell-search-panel {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 0;
  right: 0;
  z-index: 60;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lift);
  overflow: hidden;
}

.search-results {
  list-style: none;
  margin: 0;
  padding: 0.3rem;
  max-height: 18rem;
  overflow-y: auto;
}

.search-empty {
  padding: 1.4rem 0.9rem;
  text-align: center;
  color: var(--ink-faint);
  font-size: 0.88rem;
}

.search-foot {
  border-top: 1px solid var(--line);
  padding: 0.45rem 0.9rem;
  display: flex;
  gap: 0.9rem;
  flex-wrap: wrap;
  font-size: 0.75rem;
  color: var(--ink-faint);
  background: var(--surface-raised);
}

.search-foot span {
  display: flex;
  align-items: center;
  gap: 0.3rem;
}

/* ===========================================================================
   Toast
   =========================================================================== */

.toast {
  position: fixed;
  left: 50%;
  bottom: 1.25rem;
  transform: translateX(-50%);
  z-index: 70;
  background: var(--ink);
  color: var(--canvas);
  border-radius: var(--radius-md);
  padding: 0.55rem 0.85rem;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-size: 0.87rem;
  box-shadow: var(--shadow-lift);
  max-width: calc(100vw - 2rem);
}

:root[data-theme="dark"] .toast {
  background: var(--surface-pressed);
  color: var(--ink);
  border: 1px solid var(--line-strong);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .toast {
    background: var(--surface-pressed);
    color: var(--ink);
    border: 1px solid var(--line-strong);
  }
}

.toast[hidden] {
  display: none;
}

.toast button {
  background: none;
  border: 0;
  padding: 0 0.15rem;
  color: var(--accent-strong);
  font-weight: 600;
  font-size: 0.87rem;
}

.toast button:hover {
  background: none;
  color: var(--accent-strong);
  text-decoration: underline;
}

/* Countdown ring. The only motion in the product, and it exists to track
   time that is genuinely running out. */
.ring {
  width: 1.05rem;
  height: 1.05rem;
  flex: none;
  transform: rotate(-90deg);
}

.ring[hidden] {
  display: none;
}

.ring circle {
  fill: none;
  stroke-width: 2.5;
}

.ring .ring-track {
  stroke: var(--line-strong);
}

.ring .ring-head {
  stroke: var(--accent);
  stroke-linecap: round;
}

.ring-hot .ring-head {
  stroke: var(--warning);
}

.ring-lg {
  width: 2rem;
  height: 2rem;
}

/* ===========================================================================
   Entry detail
   =========================================================================== */

.entry-page {
  max-width: 48rem;
}

.entry-header {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.entry-header > div:first-child {
  flex: 1;
  min-width: 12rem;
}

.entry-header-actions {
  display: flex;
  gap: 0.4rem;
  flex: none;
}

.entry-kind {
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
}

.entry-field-list {
  margin: 0;
  display: grid;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.entry-value-row {
  background: var(--surface);
  padding: 0.6rem 0.85rem;
  display: grid;
  grid-template-columns: 8rem minmax(0, 1fr);
  gap: 0.85rem;
  align-items: center;
  min-height: 2.75rem;
}

.entry-value-row dt {
  color: var(--ink-faint);
  font-size: 0.82rem;
}

.entry-value-row dd {
  margin: 0;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.entry-plain-value {
  font-size: 0.92rem;
  overflow-wrap: anywhere;
}

.concealed-value {
  font-family: var(--mono);
  color: var(--ink-faint);
  letter-spacing: 0.12em;
  font-size: 0.9rem;
}

.secret-output {
  font-family: var(--mono);
  font-size: 0.92rem;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.secret-output[hidden] {
  display: none;
}

.field-actions {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin-left: auto;
}

.field-actions button {
  padding: 0.25rem 0.55rem;
  font-size: 0.8rem;
}

.entry-totp {
  display: flex;
  align-items: center;
  gap: 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 0.9rem 1.1rem;
  background: var(--surface-raised);
  flex-wrap: wrap;
}

.totp-code {
  font-family: var(--mono);
  font-size: 1.7rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  font-variant-numeric: tabular-nums;
}

.entry-totp .entry-kind {
  display: block;
}

/* Who else can read this. The question a human-plus-agent vault has to be
   able to answer, standing on the entry itself. */
.entry-reach {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.entry-reach-head {
  padding: 0.55rem 0.85rem;
  background: var(--surface-raised);
  border-bottom: 1px solid var(--line);
  font-size: 0.85rem;
  color: var(--ink-soft);
}

.entry-reach ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.entry-reach li {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.85rem;
  border-bottom: 1px solid var(--line);
  font-size: 0.88rem;
  flex-wrap: wrap;
}

.entry-reach li:last-child {
  border-bottom: 0;
}

.entry-reach li form {
  margin-left: auto;
}

.entry-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.25rem;
  font-size: 0.82rem;
  color: var(--ink-faint);
}

.entry-meta strong {
  color: var(--ink-soft);
  font-weight: 500;
}

/* Edit */

.entry-edit-fields {
  display: grid;
  gap: 1rem;
}

.entry-edit-field {
  display: grid;
  gap: 0.35rem;
}

.entry-edit-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: 0.7rem;
}

.entry-edit-label-row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 0.7rem;
}

.entry-edit-field-tools {
  display: flex;
  align-items: flex-end;
  gap: 0.4rem;
  flex: none;
}

.entry-secret-action {
  width: auto;
  font-size: 0.82rem;
  padding: 0.25rem 0.45rem;
}

.field-generate {
  width: auto;
  font-size: 0.82rem;
  padding: 0.25rem 0.55rem;
  white-space: nowrap;
}

.entry-edit-field [data-generate-status] {
  margin: 0;
}

.entry-edit-field [data-generate-status]:empty {
  display: none;
}

.entry-replacement[hidden] {
  display: none;
}

.entry-secondary {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.entry-secondary > h2,
.entry-additional-fields > h2 {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 1rem;
  padding: 0.7rem 0.95rem;
  margin: 0;
  border-bottom: 1px solid var(--line);
}

.entry-additional-fields {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.entry-additional-fields > .field-hint {
  padding: 0.7rem 0.95rem 0;
  margin: 0;
}

.entry-secondary-content,
.entry-additional-fields-list {
  padding: 0.95rem;
}

.entry-additional-fields-list {
  display: grid;
  gap: 1rem;
}

.revision-list,
.entry-list,
.collection-list,
.action-list,
.security-list,
.template-grid,
.settings-link-grid,
.admin-links,
.resource-grid {
  list-style: none;
  margin: 0;
  padding: 0;
}

.revision-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.7rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.9rem;
  flex-wrap: wrap;
}

.revision-list li:last-child {
  border-bottom: 0;
}

.revision-list small {
  display: block;
  color: var(--ink-faint);
  font-family: var(--mono);
  font-size: 0.78rem;
}

/* ===========================================================================
   Legacy list surfaces kept until their pages are retired
   =========================================================================== */

.entry-list li,
.collection-list li,
.action-list li {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}

.entry-list li:last-child,
.collection-list li:last-child,
.action-list li:last-child {
  border-bottom: 0;
}

.entry-link {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex: 1;
  min-width: 0;
  text-decoration: none;
  color: var(--ink);
}

.entry-link strong {
  display: block;
  font-weight: 500;
}

.entry-link small {
  color: var(--ink-faint);
  font-size: 0.8rem;
}

.action-list-main {
  flex: 1;
  min-width: 12rem;
}

.action-list-main strong {
  display: block;
}

.action-list-actions {
  display: flex;
  gap: 0.4rem;
  flex: none;
}

.action-queue .queue-bulk-action {
  margin-bottom: 0.85rem;
}

.collection-tabs {
  display: flex;
  gap: 0.3rem;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--line);
  padding-bottom: 0.5rem;
}

.collection-tabs a {
  text-decoration: none;
  font-size: 0.9rem;
  color: var(--ink-soft);
  padding: 0.3rem 0.65rem;
  border-radius: var(--radius-md);
}

.collection-tabs a:hover {
  background: var(--surface-subtle);
  color: var(--ink);
}

.collection-tabs a.is-current {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}

.template-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.5rem;
}

.template-grid a {
  display: block;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  text-decoration: none;
  color: var(--ink);
  background: var(--surface);
  font-size: 0.92rem;
}

.template-grid a:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.workspace-entry-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.4rem;
}

.workspace-entry-card {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: var(--row-pad) 0.6rem;
  border-radius: var(--radius-md);
  flex-wrap: wrap;
}

.workspace-entry-card:hover {
  background: var(--surface-subtle);
}

.workspace-entry-link {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex: 1;
  min-width: 0;
  text-decoration: none;
  color: var(--ink);
}

.workspace-entry-main {
  min-width: 0;
}

.workspace-entry-main strong {
  display: block;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.workspace-entry-main small {
  display: block;
  color: var(--ink-faint);
  font-size: 0.8rem;
}

.workspace-entry-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: none;
  font-size: 0.8rem;
  color: var(--ink-faint);
}

.workspace-filter-panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1rem 1.25rem;
}

.workspace-query-field label {
  margin-bottom: 0.3rem;
}

.credential-copy-status {
  font-size: 0.78rem;
  color: var(--ink-faint);
  flex-basis: 100%;
}

/* ===========================================================================
   Vaults, collections, deleted
   =========================================================================== */

.resource-grid {
  display: grid;
  gap: 1rem;
}

.resource-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.1rem 1.25rem;
}

.resource-card-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.7rem;
  flex-wrap: wrap;
  margin-bottom: 0.75rem;
}

.resource-type {
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
}

.collection-section {
  margin-top: 0.9rem;
}

.collection-form {
  margin-top: 0.75rem;
}

.collection-add {
  margin-top: 0.85rem;
}

/* The destructive form is the quietest thing in its column until it is used,
   so it reads as available rather than as a warning the page opens with. */
.vault-danger {
  margin-top: 1.1rem;
  background: none;
  border-color: var(--line);
}

.vault-danger label {
  color: var(--ink-faint);
  font-weight: 400;
}

.create-panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.1rem 1.25rem;
}

/* ===========================================================================
   Security
   =========================================================================== */

/* Two-step verification is the one fact the rest of this page leans on, so it
   is read above the sections on every view rather than folded into one of
   them. Its state is carried by the badge word as well as by the stripe: a
   left edge that is only a colour says nothing to a reader who cannot see
   it. */
.posture-card {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  flex-wrap: wrap;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--line-strong);
  border-radius: var(--radius-lg);
  padding: 1rem 1.25rem;
}

.posture-card.is-good {
  border-left-color: var(--success);
}

.posture-card.is-warning {
  border-left-color: var(--warning);
}

.posture-card.is-bad {
  border-left-color: var(--danger);
}

.posture-body {
  flex: 1;
  min-width: 14rem;
}

.posture-title {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.posture-body p {
  color: var(--ink-soft);
  font-size: 0.88rem;
  margin-top: 0.35rem;
  max-width: var(--measure);
}

/* The way between the three sections, and the summary of what is in each, so
   the state of a section nobody is looking at is still on the page. */
.security-views {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.7rem;
}

.security-views a {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.8rem 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: var(--ink-soft);
}

.security-views a:hover {
  border-color: var(--line-strong);
  background: var(--surface-raised);
}

.security-views a.is-current {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.security-view-name {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.security-view-value {
  font-size: 1.55rem;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}

.security-view-note {
  font-size: 0.82rem;
  color: var(--ink-faint);
}

.security-views a.is-current .security-view-name,
.security-views a.is-current .security-view-value {
  color: var(--accent);
}

/* Attention is a word before it is a colour: the note already reads "1
   expired" or "autofill off", and this only makes it hard to skip past. The
   accent keeps its own job, so a tile that is both current and asking for
   attention still shows where the reader is. */
.security-views a.needs-attention:not(.is-current) {
  border-color: var(--warning);
}

.security-views a.needs-attention .security-view-note {
  color: var(--warning);
  font-weight: 600;
}

.security-section {
  scroll-margin-top: 4rem;
}

.security-subpanel {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 0.9rem 1rem;
  background: var(--surface-raised);
  margin-bottom: 1rem;
}

.security-subpanel h3 {
  margin-bottom: 0.3rem;
}

.security-list {
  display: grid;
  gap: 0.7rem;
}

.security-item {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 0.9rem 1rem;
  background: var(--surface);
}

.security-item-head {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.security-item-identity {
  flex: 1;
  min-width: 12rem;
}

.security-item-title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* A card names its subject at the same size whichever section it is in. The
   heading level differs because the sections nest differently, and a reader
   should not be told a browser matters more than an agent key because of
   it. */
.security-item-title h3,
.security-item-title h4 {
  font-size: 0.98rem;
}

.security-item-detail {
  color: var(--ink-faint);
  font-size: 0.82rem;
  margin-top: 0.2rem;
}

.security-item-detail code {
  font-size: 0.82rem;
}

/* A card's own actions are secondary to the section's: the accent belongs to
   the section, not to every row inside it. */
.security-item-actions {
  display: flex;
  gap: 0.4rem;
  flex: none;
  flex-wrap: wrap;
}

.security-item-actions button {
  padding: 0.3rem 0.65rem;
  font-size: 0.82rem;
}

.security-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.5rem;
  margin: 0.75rem 0 0;
  font-size: 0.82rem;
}

.security-meta dt {
  color: var(--ink-faint);
}

.security-meta dd {
  margin: 0;
  color: var(--ink-soft);
}

/* Permissions and scope are one decision read together. Stacked down a
   column they read as two independent lists, which is the misreading the
   sentence above them exists to correct. */
.access-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 0.75rem 1.5rem;
  margin-top: 0.75rem;
}

.access-group {
  margin-top: 0.75rem;
}

.access-grid .access-group {
  margin-top: 0;
}

.access-label {
  font-size: 0.78rem;
  color: var(--ink-faint);
  font-weight: 600;
  margin-bottom: 0.35rem;
}

.key-editor {
  margin-top: 1rem;
  border-top: 1px solid var(--line);
  padding-top: 0.9rem;
}

.key-editor > h4 {
  margin: 0 0 0.75rem;
  color: var(--ink-soft);
}

/* The three ways to change a key sit beside each other rather than in a
   column down the left edge. */
.key-editor-body {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1.25rem;
  align-items: start;
}

/* The grid that replaces two separate chip lists: the authorisation is their
   intersection, which is what a grid renders and two lists do not. */
.grant-matrix-wrapper {
  overflow-x: auto;
}

.grant-matrix {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
}

.grant-matrix th,
.grant-matrix td {
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--line);
  text-align: center;
}

.grant-matrix thead th {
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
  white-space: nowrap;
}

.grant-matrix tbody th {
  text-align: left;
  font-weight: 500;
  color: var(--ink);
}

.grant-matrix tbody th small {
  display: block;
  color: var(--ink-faint);
  font-weight: 400;
  font-size: 0.78rem;
}

.grant-matrix tr:last-child th,
.grant-matrix tr:last-child td {
  border-bottom: 0;
}

/* Nobody audits a matrix; everybody reads a sentence. It leads the card, so
   it carries the gap that used to sit above the timestamps. */
.grant-sentence {
  margin-top: 0.75rem;
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  border-radius: var(--radius-md);
  padding: 0.75rem 0.9rem;
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--ink);
}

.grant-sentence strong {
  color: var(--accent);
}

/* One click selects the whole token, which is the only chance to copy it. */
.token-callout {
  border: 1px solid var(--accent);
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-bottom: 1rem;
}

.token-callout > h3 {
  background: var(--accent-soft);
  color: var(--ink);
  padding: 0.65rem 0.9rem;
  border-bottom: 1px solid var(--accent);
  font-size: 0.95rem;
}

.token-callout > p {
  padding: 0.65rem 0.9rem 0;
  color: var(--ink-soft);
  font-size: 0.88rem;
}

.token-value {
  display: block;
  margin: 0.65rem 0.9rem 0.9rem;
  font-family: var(--mono);
  font-size: 0.9rem;
  background: var(--surface-subtle);
  border-radius: var(--radius-md);
  padding: 0.65rem 0.8rem;
  word-break: break-all;
  user-select: all;
}

.create-key-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: 0.7rem;
  align-items: end;
}

.toggle-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
}

.toggle-state,
.setting-state {
  font-size: 0.85rem;
  color: var(--ink-soft);
}

/* ===========================================================================
   Settings, tools, activity, administration
   =========================================================================== */

.settings-section + .settings-section {
  margin-top: 0;
}

.theme-choices {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.7rem;
  margin-bottom: 1rem;
}

.theme-choice {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  padding: 0.65rem 0.75rem;
  cursor: pointer;
  font-weight: 400;
  color: var(--ink);
  margin: 0;
}

.theme-choice:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.theme-choice strong {
  display: block;
  font-size: 0.9rem;
  font-weight: 600;
}

.theme-choice small {
  color: var(--ink-faint);
  font-size: 0.78rem;
}

.theme-preview {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  flex: none;
  overflow: hidden;
  display: flex;
}

.theme-preview-light {
  background: #f7f8fb;
}

.theme-preview-dark {
  background: #171b24;
}

.generator-settings {
  display: grid;
  gap: 1.2rem;
}

.generator-styles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 0.7rem;
}

.generator-styles legend {
  margin-bottom: 0.5rem;
}

.generator-style {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  padding: 0.65rem 0.75rem;
  cursor: pointer;
  font-weight: 400;
  color: var(--ink);
  margin: 0;
}

.generator-style:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.generator-style input {
  margin-top: 0.2rem;
  flex: none;
}

.generator-style strong {
  display: block;
  font-size: 0.9rem;
  font-weight: 600;
}

.generator-style small {
  color: var(--ink-faint);
  font-size: 0.78rem;
}

.generator-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.85rem;
  align-items: start;
}

.generator-grid .field-hint {
  margin-bottom: 0;
}

.generator-sets {
  display: grid;
  gap: 0.45rem;
}

.generator-sets legend {
  margin-bottom: 0.35rem;
}

.generator-sets label {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  font-weight: 400;
  color: var(--ink);
  margin: 0;
}

.generator-sets input {
  margin-top: 0.2rem;
  flex: none;
}

.generator-sets small {
  display: block;
  color: var(--ink-faint);
  font-size: 0.78rem;
  max-width: var(--measure);
}

.generator-sample {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface-subtle);
  padding: 0.85rem;
}

.generator-sample-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.generator-sample-row input {
  font-family: var(--mono);
  letter-spacing: 0.01em;
}

.generator-sample-row button {
  width: auto;
  flex: none;
  white-space: nowrap;
}

.generator-sample .field-hint {
  margin: 0.5rem 0 0;
}

.generator-sample [data-generator-sample-status]:empty {
  display: none;
}

.theme-preview-system i {
  display: block;
  width: 50%;
  height: 100%;
}

.theme-preview-system i:first-child {
  background: #f7f8fb;
}

.theme-preview-system i:last-child {
  background: #171b24;
}

.settings-link-grid,
.admin-links {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 0.6rem;
}

.settings-link-grid a,
.admin-links a {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.75rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  text-decoration: none;
  color: var(--ink);
  background: var(--surface);
}

.settings-link-grid a:hover,
.admin-links a:hover {
  border-color: var(--accent);
  background: var(--surface-subtle);
}

.settings-link-grid svg,
.admin-links svg {
  width: 1.15rem;
  height: 1.15rem;
  color: var(--ink-faint);
  flex: none;
}

.settings-link-grid a:hover svg,
.admin-links a:hover svg {
  color: var(--accent);
}

.settings-link-grid strong,
.admin-links strong {
  display: block;
  font-size: 0.92rem;
  font-weight: 600;
}

.settings-link-grid small,
.admin-links small {
  color: var(--ink-faint);
  font-size: 0.8rem;
}

.settings-link-grid .danger-link svg {
  color: var(--danger);
}

.tool-page,
.status-page .page-stack {
  max-width: 48rem;
}

.tool-panel,
.activity-panel,
.admin-panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
  display: grid;
  gap: 0.85rem;
}

.tool-panel p,
.activity-panel > p {
  color: var(--ink-soft);
  font-size: 0.92rem;
  max-width: var(--measure);
}

.activity-panel ul {
  color: var(--ink-soft);
  font-size: 0.9rem;
  margin: 0;
  padding-left: 1.2rem;
}

/* One target cell: what kind of thing it was, then either a link to it or
   what became of it. The kind is set back so the identifier a reader is
   scanning for stays the loudest thing in the cell. */
.activity-target {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.activity-target-kind {
  color: var(--ink-soft);
  font-size: 0.85rem;
}

.tool-form {
  display: grid;
  gap: 0.85rem;
}

.activity-page,
.admin-page {
  max-width: 76rem;
}

.activity-panel-inline {
  margin-top: 1rem;
  border-top: 1px solid var(--line);
  padding-top: 0.9rem;
}

.activity-panel-inline > h4 {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.5rem;
  color: var(--ink-soft);
}

.activity-log {
  font-family: var(--mono);
  font-size: 0.82rem;
  background: var(--surface-subtle);
  border-radius: var(--radius-md);
  padding: 0.75rem;
  overflow-x: auto;
  margin: 0.6rem 0 0;
}

.activity-result {
  font-weight: 600;
}

.danger-zone {
  border: 1px solid var(--danger);
  border-radius: var(--radius-lg);
  padding: 1.1rem 1.25rem;
  background: var(--danger-soft);
}

.danger-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.85rem;
  flex-wrap: wrap;
}

.cf-turnstile {
  min-height: 65px;
}

/* ===========================================================================
   Responsive
   =========================================================================== */

@media (max-width: 64rem) {
  .workbench {
    grid-template-columns: 11rem minmax(0, 1fr);
  }

  /* The detail panel becomes its own page below this width; the list keeps
     the two columns that matter. */
  .workbench-detail {
    display: none;
  }

  .workbench[data-view="detail"] .workbench-rail,
  .workbench[data-view="detail"] .workbench-list {
    display: none;
  }

  .workbench[data-view="detail"] {
    grid-template-columns: minmax(0, 1fr);
  }

  .workbench[data-view="detail"] .workbench-detail {
    display: block;
  }

  .workbench[data-view="detail"] .detail-back {
    display: block;
  }
}

@media (max-width: 48rem) {
  .site-nav {
    height: auto;
    padding: 0.5rem 0.75rem;
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  .shell-search {
    order: 3;
    flex-basis: 100%;
    max-width: none;
  }

  /* The hints name keys this viewport has none of, and the foot is nothing
     but hints. */
  .kbd-hint,
  .search-foot {
    display: none;
  }

  /* One column stacked down the page, so the page is the scroll again and
     the viewport-height frame would only cut it off. This is also where
     100vh is least trustworthy, with a browser chrome that comes and goes. */
  .workbench {
    grid-template-columns: minmax(0, 1fr);
    height: auto;
  }

  .workbench-rail {
    border-right: 0;
    border-bottom: 1px solid var(--line);
    padding-bottom: 0.75rem;
  }

  .workbench-list {
    border-right: 0;
  }

  .page-stack {
    padding: 1.25rem 0.85rem 3rem;
  }

  .panel,
  .tool-panel,
  .activity-panel,
  .admin-panel,
  .resource-card,
  .create-panel {
    padding: 1rem;
  }

  .entry-value-row {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.3rem;
  }

  .page-heading-actions {
    flex-direction: column;
    align-items: stretch;
  }

  h1 {
    font-size: 1.5rem;
  }

  /* The two actions you came for stay under the thumb. */
  .entry-detail-actions {
    position: sticky;
    bottom: 0;
    background: var(--surface);
    border-top: 1px solid var(--line);
    padding: 0.7rem 0;
    display: flex;
    gap: 0.5rem;
  }

  .entry-detail-actions > * {
    flex: 1;
  }
}

/* ===========================================================================
   Marketing front door
   ---------------------------------------------------------------------------
   The signed-out root. It is the one surface whose job is to explain rather
   than to operate, so it is also the one most likely to drift from the rest
   of the system: a landing page is where a stock font, a hosted icon set and
   a scroll animation usually arrive together.

   None of them are here. The CSP would refuse the first two and the motion
   rule refuses the third, so the identity comes from palette, spacing and
   layout. Every colour below is a token already defined above.

   Two layout rules carry the page. Nothing is centred except the closing
   call to action, because a column of centred paragraphs has no edge for the
   eye to return to and reads as a poster rather than a product. And no
   heading is clamped to a character count: a heading in a narrow column
   beside an empty half is the failure this page had before, so headings sit
   in a real column with the paragraph that belongs to them alongside.
   =========================================================================== */

.marketing {
  display: flex;
  flex-direction: column;
}

.marketing-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 0 1.25rem;
  width: 100%;
}

.marketing-band {
  padding: 4rem 0;
  border-top: 1px solid var(--line);
}

.marketing-band-quiet {
  background: var(--surface-subtle);
}

/* --- Hero ----------------------------------------------------------------
   Asymmetric on purpose: the argument reads down the left, and the artifact
   that proves it sits to the right at the height of the headline. The wash
   behind it is a token gradient rather than an image, so it costs no request
   and inverts with the theme. */

.marketing-hero {
  padding: 4.5rem 0 5rem;
  background: radial-gradient(
    58rem 30rem at 76% 20%,
    var(--accent-soft),
    transparent 62%
  );
}

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  gap: 3.5rem;
  align-items: center;
}

.hero-eyebrow {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  margin: 0;
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.hero-eyebrow::before {
  content: "";
  width: 1.6rem;
  height: 2px;
  border-radius: 1px;
  background: var(--accent);
}

.hero-pitch h1 {
  font-size: clamp(2.1rem, 4.4vw, 3.3rem);
  line-height: 1.06;
  letter-spacing: -0.025em;
  margin: 1.1rem 0 0;
  text-wrap: balance;
}

.hero-lede {
  font-size: clamp(1rem, 1.3vw, 1.1rem);
  color: var(--ink-soft);
  max-width: 34rem;
  margin: 1.2rem 0 0;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem;
  margin: 1.9rem 0 0;
}

.hero-actions .button {
  padding: 0.65rem 1.3rem;
  font-size: 0.95rem;
}

.hero-trust {
  list-style: none;
  margin: 1.7rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.35rem;
  font-size: 0.8rem;
  color: var(--ink-faint);
}

/* Every item carries its own marker rather than only the ones following a
   sibling. A separator that belongs to the gap turns into a stray leading
   bullet the moment the row wraps, which it does at narrow widths. */
.hero-trust li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.hero-trust li::before {
  content: "";
  width: 4px;
  height: 4px;
  flex: none;
  border-radius: 50%;
  background: var(--line-strong);
}

/* --- One entry, two readers ---------------------------------------------
   The pitch as a picture rather than a paragraph. One stored entry is named
   once at the top, then read down two lanes whose rows line up, so the only
   cell that differs is the password and the eye finds it without being told
   where to look. The strip beneath both lanes is the shared authorization
   service: the architecture stated as structure, not as an arrow. */

.entry-compare {
  margin: 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 1rem;
  box-shadow: var(--shadow-lift);
  overflow: hidden;
}

.entry-compare-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.85rem 1.15rem;
  border-bottom: 1px solid var(--line);
  background: var(--surface-raised);
}

.entry-compare-head svg {
  width: 1.1rem;
  height: 1.1rem;
  flex: none;
  fill: none;
  stroke: var(--ink-faint);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.entry-compare-title {
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--ink);
}

.entry-compare-tag {
  margin-left: auto;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.entry-lanes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.entry-lane {
  display: flex;
  flex-direction: column;
  padding: 1.1rem 1.15rem 1rem;
  min-width: 0;
}

.entry-lane + .entry-lane {
  border-left: 1px solid var(--line);
}

.entry-lane-who {
  display: block;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
}

.entry-lane-via {
  display: block;
  margin-top: 0.15rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--ink-faint);
  overflow-wrap: anywhere;
}

.entry-lane-body {
  margin: 1rem 0 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

/* Scoped to the lane, because the workbench has its own .entry-row and these
   rules are the later ones. Unscoped, the figure's two-column grid and its
   negative margins reached every row in the workbench list and every search
   result, which is what pushed both into scrolling sideways. The whole block
   is scoped rather than only the rule that did the damage: the rest are inert
   today because dt, dd, is-focus and is-withheld appear nowhere else, which is
   a fact about the markup rather than about these selectors. */
.entry-lane-body .entry-row {
  display: grid;
  grid-template-columns: 4.9rem minmax(0, 1fr);
  align-items: center;
  gap: 0.5rem;
  min-height: 2.1rem;
  padding: 0.25rem 0.5rem;
  margin: 0 -0.5rem;
  border-radius: var(--radius-sm);
}

.entry-lane-body .entry-row dt {
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.entry-lane-body .entry-row dd {
  margin: 0;
  min-width: 0;
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--ink);
  overflow-wrap: anywhere;
}

/* The one row the whole page is about. Both lanes mark it, so the pair reads
   as a comparison rather than as one lane being flagged. */
.entry-lane-body .entry-row.is-focus {
  background: var(--surface-pressed);
}

.entry-lane-body .entry-row.is-withheld {
  background: var(--success-soft);
}

.entry-lane-body .entry-row.is-withheld dd {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex-wrap: wrap;
}

.entry-null {
  color: var(--ink-faint);
}

.entry-chip {
  font-family: var(--font);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--success);
  border: 1px solid var(--success);
  border-radius: var(--radius-sm);
  padding: 0.08rem 0.4rem;
}

.entry-lane-foot {
  margin: auto 0 0;
  padding-top: 0.9rem;
  font-size: 0.76rem;
  line-height: 1.45;
  color: var(--ink-soft);
}

.entry-compare-foot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.85rem 1.15rem;
  border-top: 1px solid var(--line);
  background: var(--surface-raised);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--ink-soft);
  text-align: center;
}

.entry-compare-foot svg {
  width: 1rem;
  height: 1rem;
  flex: none;
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- Band heading ---------------------------------------------------------
   Heading and its opening paragraph as two columns of one header, so a band
   opens across the full width instead of stacking short lines against an
   empty right half. */

.band-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: 0.9rem 3.5rem;
  align-items: start;
  margin-bottom: 2.5rem;
}

.band-head .eyebrow {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  margin: 0;
}

.band-head .eyebrow::before {
  content: "";
  width: 1.6rem;
  height: 2px;
  border-radius: 1px;
  background: var(--accent);
}

.band-head h2 {
  font-size: clamp(1.5rem, 2.6vw, 2rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin: 0.75rem 0 0;
  text-wrap: balance;
}

.band-head-intro {
  color: var(--ink-soft);
  margin: 0;
  align-self: center;
}

/* --- Feature grid -------------------------------------------------------- */

.marketing-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1.75rem 2.5rem;
}

/* Six features across four tracks leaves a short second row. Thirds fill both
   rows, and the wider column keeps each heading on one line. */
.marketing-grid-thirds {
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
}

.marketing-feature > h3 {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.98rem;
  margin: 0 0 0.4rem;
}

.marketing-feature svg {
  width: 1.15rem;
  height: 1.15rem;
  flex: none;
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.marketing-feature p {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.9rem;
}

/* --- Notes --------------------------------------------------------------
   A qualifier the page owes the reader, set as a margin note rather than as
   another paragraph competing with the body. */

.marketing-note {
  margin: 1.75rem 0 0;
  padding-left: 1rem;
  border-left: 2px solid var(--line-strong);
  color: var(--ink-faint);
  font-size: 0.85rem;
  max-width: var(--measure);
}

/* --- Tool table ----------------------------------------------------------
   The MCP surface, split the way the tool descriptions split it. The chip
   carries the same word a model reads before it chooses, so the page and the
   boundary say the same thing. */

.tool-scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.tool-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.87rem;
}

.tool-table caption {
  text-align: left;
  color: var(--ink-faint);
  font-size: 0.8rem;
  padding: 0.85rem 1.1rem 0.6rem;
}

.tool-table th,
.tool-table td {
  text-align: left;
  padding: 0.65rem 1.1rem;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}

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

.tool-table th {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  font-weight: 600;
  background: var(--surface-raised);
  border-bottom-color: var(--line);
}

.tool-table td:first-child {
  font-family: var(--mono);
  color: var(--ink);
  white-space: nowrap;
}

.tool-table td:last-child {
  color: var(--ink-soft);
}

.disclosure-chip {
  display: inline-block;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 0.15rem 0.45rem;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  font-family: var(--font);
}

.disclosure-chip.is-redacted {
  background: var(--success-soft);
  color: var(--success);
  border: 1px solid var(--success);
}

.disclosure-chip.is-disclosing {
  background: var(--warning-soft);
  color: var(--warning);
  border: 1px solid var(--warning);
}

/* --- Transports ----------------------------------------------------------
   The request beside the rule that governs every rejection of it. */

.transport-split {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: 2.5rem;
  align-items: start;
}

.marketing-code {
  margin: 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.1rem 1.2rem;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 0.82rem;
  line-height: 1.65;
  color: var(--ink-soft);
}

.marketing-code b {
  color: var(--ink);
  font-weight: 600;
}

.transport-split .marketing-note {
  margin: 0;
}

/* --- Limits -------------------------------------------------------------
   Set as cards rather than a bulleted column: the band is the one a reader
   scans for the disqualifying line, and a card gives each limit an edge to
   stop at. */

.limits-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  gap: 1rem;
}

.limits-list li {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.1rem 1.2rem;
  color: var(--ink-soft);
  font-size: 0.88rem;
  line-height: 1.55;
}

.limits-list strong {
  display: block;
  color: var(--ink);
  font-weight: 600;
  font-size: 0.95rem;
  margin-bottom: 0.35rem;
}

/* --- Closing -------------------------------------------------------------
   The one centred block on the page. A final call to action has no body copy
   to anchor and nothing following it, so centring reads as an ending rather
   than as a layout that gave up. */

.marketing-close {
  text-align: center;
  padding: 5rem 0;
}

.marketing-close h2 {
  font-size: clamp(1.6rem, 3.2vw, 2.15rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin: 0;
  text-wrap: balance;
}

.marketing-close-intro {
  color: var(--ink-soft);
  max-width: 46ch;
  margin: 1rem auto 0;
}

.marketing-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.7rem;
  margin: 1.9rem 0 0;
}

.marketing-actions .button {
  padding: 0.65rem 1.3rem;
  font-size: 0.95rem;
}
/* ===========================================================================
   Roadmap
   ---------------------------------------------------------------------------
   A public page, but not a second design system: the sections are the panels
   and section headings every signed-in page already uses, so the only rules
   here are the list inside one and the mark on the section that is current.

   The accent appears once, on that section and on the steps completed inside
   it, which is the accent doing its usual job: saying where you are. The
   sections say what they are in words, so nothing depends on seeing it.
   =========================================================================== */

.roadmap-group.is-current {
  border-color: var(--accent);
  border-left-width: 3px;
}

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

.roadmap-list > li {
  border-top: 1px solid var(--line);
  padding: 0.85rem 0;
}

.roadmap-list > li:first-child {
  border-top: 0;
  padding-top: 0;
}

.roadmap-list > li:last-child {
  padding-bottom: 0;
}

.roadmap-list h3 {
  margin: 0;
  font-size: 0.98rem;
}

.roadmap-list p {
  margin: 0.3rem 0 0;
  max-width: var(--measure);
  color: var(--ink-soft);
  font-size: 0.9rem;
}

/* One segment per slice rather than a filled bar, because a bar's width has to
   be computed per item and a computed width is an inline style the policy drops
   silently. Segments are equal by layout, so the markup carries no measurement
   at all. The count beside them is what a screen reader gets; the segments are
   the same fact for eyes.

   The number of them is whatever the epic currently holds, which grows: an epic
   re-plan took one item from six segments to eleven. Both axes therefore wrap.
   A strip that cannot wrap is one re-plan away from pushing its own panel wider
   than the viewport, and it fails at the narrowest width, where nobody is
   looking when they add a segment. */
.roadmap-list .roadmap-progress {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.55rem;
  margin-top: 0.55rem;
  color: var(--ink-faint);
  font-size: 0.82rem;
}

.progress-steps {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.2rem;
}

.progress-steps span {
  width: 1.5rem;
  height: 0.3rem;
  border-radius: var(--radius-sm);
  background: var(--line-strong);
}

.progress-steps span.is-done {
  background: var(--accent);
}

.roadmap-note {
  margin: 0;
  color: var(--ink-faint);
  font-size: 0.82rem;
}

@media (max-width: 46rem) {
  .progress-steps span {
    width: 1.1rem;
  }
}

/* The marketing page collapses in two stages. First the side-by-side pairs
   fold, because the artifact and the code block each need their own width
   before the text does. Only below that does the entry comparison give up
   its two lanes, which is the last thing to go: the lanes lining up is the
   argument. */

@media (max-width: 62rem) {
  .hero-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 2.75rem;
  }

  .hero-lede {
    max-width: none;
  }

  .band-head {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.9rem;
    margin-bottom: 2rem;
  }

  .band-head h2 {
    margin-top: 0.6rem;
  }

  .transport-split {
    grid-template-columns: minmax(0, 1fr);
    gap: 1.5rem;
  }
}

@media (max-width: 46rem) {
  .marketing-hero {
    padding: 3rem 0 3.5rem;
  }

  .marketing-band {
    padding: 2.75rem 0;
  }

  .marketing-close {
    padding: 3.5rem 0;
  }
}

/* The lanes fold last, and well after everything else, because them sitting
   side by side is the comparison the figure exists to make: stacked, the two
   password rows are a screen apart and the reader has to hold one in their
   head to compare it with the other.

   So they hold until a lane can no longer set a label beside a secret, which
   is a little under 35rem. Folding them with the rest at 46rem gave a 660px
   lane whose value cell was 535px of empty space, and threw the comparison
   away to get it. */
@media (max-width: 35rem) {
  .entry-lanes {
    grid-template-columns: minmax(0, 1fr);
  }

  .entry-lane + .entry-lane {
    border-left: 0;
    border-top: 1px solid var(--line);
  }

  .entry-lane-foot {
    padding-top: 0.7rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

@media print {
  .workspace-header,
  .site-header,
  .workspace-footer,
  .public-footer,
  .toast,
  .workbench-rail {
    display: none !important;
  }
}
