:root {
  --bg: #14161a;
  --panel: #1c1f26;
  --panel-2: #232733;
  --border: #333a49;
  --text: #e6e9ef;
  --muted: #9aa4b8;
  --accent: #4f8cff;
  --danger: #e5534b;
  --ok: #3fb950;
  --warn: #d29922;
  --radius: 8px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 20px;
  background: #0f1115;
  border-bottom: 1px solid var(--border);
}

.brand { display: flex; align-items: center; gap: 10px; font-weight: 600; }

.dot {
  width: 12px; height: 12px; border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), #9b6bff);
}

.topbar-right { display: flex; align-items: center; gap: 12px; font-size: 13px; }

/* An inline, non-modal warning inside a card.
   Distinct from a form field error: nothing is wrong with what was typed, the
   account is simply in a state that needs a step or two to resolve. */
.callout {
  background: #2a2413;
  border: 1px solid #4a3f16;
  color: #f0e2b6;
  border-radius: 8px;
  padding: 10px 12px;
  margin-top: 12px;
  font-size: 13px;
}
.callout p { margin: 0; }

.view { max-width: 1000px; margin: 0 auto; padding: 24px 20px 60px; }


h1 { font-size: 22px; margin: 0 0 4px; }
h2 { font-size: 16px; margin: 0 0 12px; }
h3 { font-size: 14px; margin: 0 0 8px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px;
  margin-bottom: 20px;
}

.muted { color: var(--muted); }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.small { font-size: 12px; }
.row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.spread { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
.stack { display: flex; flex-direction: column; gap: 10px; }

label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 4px; }

input, select, textarea {
  width: 100%;
  padding: 8px 10px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font: inherit;
}

textarea { min-height: 120px; resize: vertical; font-family: ui-monospace, Menlo, monospace; font-size: 12px; }

button {
  padding: 8px 14px;
  background: var(--accent);
  color: #06101f;
  border: 0;
  border-radius: 6px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

button.secondary { background: var(--panel-2); color: var(--text); border: 1px solid var(--border); }
button.danger { background: transparent; color: var(--danger); border: 1px solid var(--danger); }
button.link { background: none; color: var(--accent); padding: 2px 4px; font-weight: 500; }
button:disabled { opacity: .5; cursor: not-allowed; }

table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 9px 8px; border-bottom: 1px solid var(--border); vertical-align: top; }
th { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
tbody tr:hover { background: #1f232c; }

.pill { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 11px; border: 1px solid var(--border); color: var(--muted); }
.pill.owner { color: var(--ok); border-color: #24502c; }
.pill.write { color: var(--accent); border-color: #24406f; }
.pill.read { color: var(--muted); }
.pill.admin { color: var(--warn); border-color: #5a4715; }

.banner { padding: 10px 12px; border-radius: 6px; margin-bottom: 12px; font-size: 13px; }
.banner.error { background: #3a1d1b; border: 1px solid #6b2b27; color: #ffc9c5; }
.banner.ok { background: #16301c; border: 1px solid #24502c; color: #b7f0c2; }
.banner.info { background: #1a2436; border: 1px solid #27405f; color: #cfe0ff; }

pre {
  background: #0f1115;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 12px;
  overflow-x: auto;
  font-size: 12px;
  margin: 0;
}

.bucket-row { cursor: pointer; }
.bucket-row.selected { background: #1f2735; }
.empty { color: var(--muted); padding: 14px 8px; font-style: italic; }
.breadcrumb { margin-bottom: 10px; font-size: 13px; }

/* Layout utilities. The client sets classes rather than inline `style`
   attributes, because the Content-Security-Policy forbids inline styles. */
.section-heading { margin-top: 18px; }
.spaced-top { margin-top: 12px; }
.mb-xs { margin-bottom: 2px; }
.grow { flex: 1 1 160px; min-width: 0; }
.fixed-160 { width: 160px; flex: 0 0 160px; }
.narrow { max-width: 480px; margin: 40px auto; }
.tight { margin-bottom: 2px; }
.spaced { margin-left: 8px; }
.inline-check { gap: 6px; margin: 0; }
.checkbox { width: auto; }
.section { margin-top: 18px; }

.divider { border: 0; border-top: 1px solid var(--border); margin: 18px 0 14px; }
.wrap { word-break: break-all; }
.pill.danger { color: var(--danger); border-color: #6b2b27; }

/* ---------------------------------------------------------------------------
   Authentication pages

   The passkey option sits in its own bordered box below the password form, so it
   reads as an alternative route in rather than part of the password flow.
   --------------------------------------------------------------------------- */

.auth-card { margin-bottom: 16px; }
.auth-card h1 { margin-bottom: 2px; }
.auth-card > p.muted { margin-top: 0; }

.auth-actions { margin-top: 18px; }

button.primary.block,
button.passkey.block,
button.secondary.block {
  display: block;
  width: 100%;
  padding: 12px 16px;
}

/* The passkey entry point: a distinct, slightly recessed surface. */
.passkey-box {
  padding: 14px;
  background: var(--surface-2, rgba(255, 255, 255, 0.02));
  border: 1px solid var(--border);
}

button.passkey {
  font-weight: 600;
  letter-spacing: 0.01em;
}

/* Password requirements, ticked live as the invitee types. */
.requirements {
  margin: 4px 0 2px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
}
.requirements-title {
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 8px;
}
.requirements ul { margin: 0; padding-left: 18px; }
.requirements .rule { margin: 4px 0; }
.requirements .rule.ok { color: var(--ok, #7fd18a); }
.requirements .rule.ok::marker { color: var(--ok, #7fd18a); }

.invited { margin: 12px 0 16px; }
.invite-mark { margin-right: 2px; }
.invite-invalid { color: var(--danger); }

/* A divider that separates the password form from the passkey box. */
.auth-card .divider { margin: 16px 0; }

/* ---------------------------------------------------------------------------
   Button feedback

   Every action button reacts to hover, to being pressed, and to keyboard focus.
   A button that does nothing visible on hover reads as decoration, and for
   destructive actions like Revoke or Delete the hover state is the last chance
   to signal what is about to happen.
   --------------------------------------------------------------------------- */

button {
  transition: background-color 120ms ease, border-color 120ms ease,
              color 120ms ease, transform 60ms ease, box-shadow 120ms ease;
  cursor: pointer;
}

button:hover:not(:disabled) {
  border-color: var(--accent, #6ea8fe);
  filter: brightness(1.18);
}

button:active:not(:disabled) {
  transform: translateY(1px);
  filter: brightness(0.94);
}

button:focus-visible {
  outline: 2px solid var(--accent, #6ea8fe);
  outline-offset: 2px;
}

button:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

/* A short-lived acknowledgement after a successful copy. */
button.copied {
  color: var(--ok, #7fd18a);
  border-color: var(--ok, #7fd18a);
}

/* Destructive actions lean red on hover, so the warning lands before the click. */
button.danger:hover:not(:disabled) {
  border-color: var(--danger, #e5534b);
  background: rgba(229, 83, 75, 0.14);
  filter: none;
}

/* ---------------------------------------------------------------------------
   Modal dialogs
   --------------------------------------------------------------------------- */

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(6, 8, 12, 0.72);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  z-index: 50;
}

.modal-dialog {
  background: var(--surface, #161a22);
  border: 1px solid var(--border, #2a303c);
  border-radius: 10px;
  padding: 22px 24px;
  width: 100%;
  max-width: 460px;
  max-height: 88vh;
  overflow-y: auto;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
}

.modal-title { margin: 0 0 12px; font-size: 19px; }

/* The QR sits on its own light plate: QR codes need light-on-dark contrast to
   scan reliably, and a dark theme would otherwise invert or dim it. */
img.qr {
  display: block;
  margin: 12px auto;
  padding: 10px;
  background: #ffffff;
  border-radius: 8px;
}

.backup-codes {
  white-space: pre-line;
  line-height: 1.9;
  padding: 14px 16px;
  border: 1px dashed var(--border, #2a303c);
  border-radius: 8px;
  margin: 12px 0;
  user-select: all;
}

.grow { flex: 1; }

/* ---------------------------------------------------------------------------
   Add API key dialog

   Mirrors the OCI console's three ways to add a key: generate a pair, choose a
   public key file, or paste one.
   --------------------------------------------------------------------------- */

.key-panel { margin-top: 16px; }
.key-panel textarea { width: 100%; font-family: var(--mono, ui-monospace, monospace); font-size: 13px; }

/* Radio rows read as a chooser rather than as form fields. */
label.radio {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 0;
  cursor: pointer;
  font-size: 14px;
}
label.radio input { width: auto; margin: 0; accent-color: var(--accent, #6ea8fe); }
label.radio:hover { color: var(--accent, #6ea8fe); }

/* Drop target, matching the console's "Drop a file or select one". */
.dropzone {
  border: 1px dashed var(--border, #2a303c);
  border-radius: 8px;
  padding: 20px 22px;
  cursor: pointer;
  transition: border-color 120ms ease, background-color 120ms ease;
}
.dropzone:hover,
.dropzone.over {
  border-color: var(--accent, #6ea8fe);
  background: rgba(110, 168, 254, 0.07);
}
.dropzone-title { font-weight: 600; margin-bottom: 4px; }
input.hidden { display: none; }

/* Download tiles, side by side as in the console. */
.downloads {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin: 14px 0;
}
@media (max-width: 560px) { .downloads { grid-template-columns: 1fr; } }

.download-box {
  border: 1px solid var(--border, #2a303c);
  border-radius: 8px;
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.download-title { font-weight: 600; }
.download-box button.download { margin-top: auto; }

button.download:hover:not(:disabled) {
  border-color: var(--accent, #6ea8fe);
  background: rgba(110, 168, 254, 0.12);
}

/* ---------------------------------------------------------------------------
   Change Password dialog
   --------------------------------------------------------------------------- */

/* A password field with its reveal toggle inside the field's right edge. */
.with-reveal { position: relative; display: flex; align-items: center; }
.with-reveal input { width: 100%; padding-right: 62px; }
.with-reveal button.reveal {
  position: absolute;
  right: 6px;
  padding: 4px 8px;
  font-size: 12px;
  background: transparent;
  border: 1px solid transparent;
  color: var(--muted);
}
.with-reveal button.reveal:hover:not(:disabled) {
  color: var(--accent, #6ea8fe);
  border-color: var(--border, #2a303c);
  filter: none;
}

/* Inline form errors, so a mismatch is reported where the form is. */
.form-error {
  color: var(--danger, #e5534b);
  font-size: 13px;
  min-height: 18px;
  margin-top: 10px;
}
.form-error:empty { min-height: 0; margin-top: 0; }
