/* extensions.css — Settings → Extensions → "Installed extensions" card.
   One row per record the extension pipeline left behind (AXP, unmanaged
   repo install, legacy hermes-integration). The row vocabulary: a kind
   glyph, title + version, a chip strip (signature / sandbox / health /
   update), a muted meta line, an optional detail drawer and an action bar
   (update policy on the left, the buttons as one group on the right). Rows
   are as wide as the card. Rendered by extensions_list.js;
   colour tokens come from base.css (--extension, --accent, --warn, --err). */

/* "Install an extension" — one input + one button on a line; the hint
   below carries validation, the switch-off notice or nothing. */
.ext-install-form {
  display: flex;
  gap: .5rem;
  margin-top: .7rem;
}
.ext-install-input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: .86rem;
  color: var(--text);
  background: var(--surface-control);
  border: 1px solid var(--outline-subtle);
  border-radius: 8px;
  padding: .45rem .65rem;
}
.ext-install-input:focus { outline: none; border-color: var(--accent) }
.ext-install-input:disabled { opacity: .55; cursor: not-allowed }
.ext-install-hint {
  margin: .45rem 0 0;
  font-size: .78rem;
  color: var(--muted);
}
.ext-install-hint[data-state="error"] { color: var(--err) }

/* Header count — sits left of the chevron in the card summary. */
.ext-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  height: 1.35rem;
  padding: 0 .45rem;
  border-radius: 999px;
  font-size: .72rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--extension);
  background: color-mix(in srgb, var(--extension) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--extension) 35%, transparent);
}
.ext-count[data-updates] {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
}

/* Intro copy on the left, "Check for updates" on the right. */
.ext-toolbar {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
}
.ext-toolbar .section-intro { flex: 1; margin: 0 }
.ext-schedule-link,
.au-sub .au-link {
  color: var(--accent);
  text-decoration: none;
}
/* The in-row link may wrap: next to the row's update button and switch a
   phone has no room for the whole phrase, and keeping it on one line pushed
   the Settings dialog past the screen edge. The link closing an intro moves
   to the next line whole while it fits one, and wraps inside itself on a
   phone narrower than the phrase (it ran 12 px past a 320 px screen). */
.ext-schedule-link { display: inline-block; max-width: 100% }
.ext-schedule-link:hover,
.au-sub .au-link:hover { text-decoration: underline }
.ext-check-btn { margin-top: .1rem }

.ext-status {
  margin: 0;
  font-size: .78rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.ext-status[data-state="ok"] { color: var(--accent) }
.ext-status[data-state="warn"] { color: var(--warn) }
.ext-status[data-state="error"] { color: var(--err) }

/* One row per line, as wide as the card. A row carries a title, a chip
   strip, a meta line and an action bar; at half the card's width every one
   of them wrapped, and the buttons ended up scattered over two lines. */
.ext-list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: .6rem;
}
.ext-empty {
  margin: 0;
  padding: 1.1rem .5rem;
  text-align: center;
  color: var(--muted);
  font-size: .82rem;
  line-height: 1.5;
  border: 1px dashed var(--border2);
  border-radius: 10px;
}

/* ── Row ─────────────────────────────────────────────────────────── */
.ext-row {
  position: relative;
  padding: .9rem 1.1rem .8rem;
  border: 1px solid var(--border2);
  border-left-width: 3px;
  border-left-color: var(--extension);
  border-radius: 10px;
  background: var(--surface-flat);
  transition: border-color .12s, background .12s, opacity .15s;
}
.ext-row:hover { background: var(--surface-flat-hover) }
.ext-row[data-kind="unmanaged"] { border-left-color: var(--warn) }
.ext-row[data-kind="legacy"] { border-left-color: var(--muted2) }
.ext-row[data-unfinished] { border-left-color: var(--err) }
.ext-row[data-update] { border-color: color-mix(in srgb, var(--accent) 45%, var(--border2)) }
.ext-row[data-busy] { opacity: .55; pointer-events: none }

.ext-row-head {
  display: flex;
  align-items: flex-start;
  gap: .75rem;
}
.ext-glyph {
  flex: 0 0 auto;
  width: 2rem;
  height: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 9px;
  font-size: 1rem;
  background: var(--surface-control);
  border: 1px solid var(--outline-subtle);
}
.ext-main { flex: 1; min-width: 0 }
/* The card's right edge: the switch on top, the open button centred in the
   height left under it (extension_views.css). */
.ext-side {
  flex: 0 0 auto;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: .5rem;
}

.ext-title-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .25rem .5rem;
}
.ext-title {
  font-weight: 600;
  font-size: .95rem;
  color: var(--text);
  overflow-wrap: anywhere;
}
.ext-version {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .74rem;
  color: var(--muted);
  padding: .05rem .4rem;
  border-radius: 6px;
  background: var(--surface-control);
  border: 1px solid var(--outline-subtle);
}
.ext-kind {
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  font-weight: 600;
  color: var(--muted2);
}

/* Chip strip — signature, sandbox, health, update, stale, consent. */
.ext-chips {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem;
  margin-top: .4rem;
}
.ext-chip {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  padding: .12rem .5rem;
  border-radius: 999px;
  font-size: .7rem;
  font-weight: 500;
  line-height: 1.4;
  color: var(--muted);
  background: var(--surface-control);
  border: 1px solid var(--outline-subtle);
  white-space: nowrap;
}
.ext-chip[data-tone="ok"] {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.ext-chip[data-tone="info"] {
  color: var(--extension);
  border-color: color-mix(in srgb, var(--extension) 40%, transparent);
  background: color-mix(in srgb, var(--extension) 10%, transparent);
}
.ext-chip[data-tone="warn"] {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 45%, transparent);
  background: color-mix(in srgb, var(--warn) 10%, transparent);
}
.ext-chip[data-tone="err"] {
  color: var(--err);
  border-color: color-mix(in srgb, var(--err) 45%, transparent);
  background: color-mix(in srgb, var(--err) 10%, transparent);
}
.ext-chip[data-tone="update"] {
  color: var(--accent);
  font-weight: 600;
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.ext-chip .ext-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}
/* A health dot lights in its LED colour (base.css, "Status dots"). */
.ext-chip[data-tone="ok"] .ext-dot { background: var(--led-on) }
.ext-chip[data-tone="warn"] .ext-dot { background: var(--led-warn) }
.ext-chip[data-tone="err"] .ext-dot { background: var(--led-err) }

/* Meta line — one item per fact, a dot between neighbours. Every item
   carries its dot in its own left padding and is pulled back by the same
   amount, so the dot of an item that starts a line lands outside the box
   and is clipped: a wrapped line never begins with a separator. */
.ext-meta {
  display: flex;
  flex-wrap: wrap;
  overflow: hidden;
  margin-top: .35rem;
  font-size: .76rem;
  color: var(--muted2);
  line-height: 1.45;
}
.ext-meta-item {
  position: relative;
  min-width: 0;
  margin-left: -1rem;
  padding: 0 1rem;
  overflow-wrap: anywhere;
}
.ext-meta-item::before {
  content: "·";
  position: absolute;
  left: 0;
  width: 1rem;
  text-align: center;
  opacity: .6;
}
.ext-desc {
  margin-top: .35rem;
  font-size: .82rem;
  color: var(--text);
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.ext-error {
  margin-top: .4rem;
  font-size: .76rem;
  color: var(--err);
  line-height: 1.45;
  overflow-wrap: anywhere;
}

/* Detail drawer — the facts a careful owner wants: declared access,
   runtime containment, components, links, ids. Spans the whole row; opened,
   the facts sit in an inset panel so they read as one block. The selectors
   carry the element name to outrank the panel-wide
   `details:not(.panel-section)` rules in settings.css, whose top border
   would draw a second line right above the action bar's own. */
details.ext-details {
  margin-top: .6rem;
  padding-top: 0;
  border-top: 0;
}
details.ext-details > summary {
  list-style: none;
  cursor: pointer;
  font-size: .76rem;
  color: var(--muted);
  user-select: none;
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: 0;
}
details.ext-details[open] > summary { margin-bottom: 0 }

.ext-facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: .4rem 1.1rem;
  margin: .5rem 0 0;
  padding: .7rem .85rem;
  border-radius: 8px;
  background: var(--surface-control);
  border: 1px solid var(--outline-subtle);
  font-size: .78rem;
}
.ext-facts dt {
  font-size: .66rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  font-weight: 600;
  color: var(--muted);
  padding-top: .1rem;
}
.ext-facts dd {
  margin: 0;
  color: var(--text);
  line-height: 1.45;
  min-width: 0;
  overflow-wrap: anywhere;
}
.ext-facts dd ul {
  margin: 0;
  padding-left: 1rem;
}
.ext-facts dd li + li { margin-top: .1rem }
.ext-facts dd code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .74rem;
  padding: .02rem .3rem;
  border-radius: 4px;
  border: 1px solid var(--outline-subtle);
}
.ext-facts dd a { color: var(--accent); text-decoration: none }
.ext-facts dd a:hover { text-decoration: underline }
.ext-facts dd .ext-tone-warn { color: var(--warn) }
.ext-facts dd .ext-tone-err { color: var(--err) }
.ext-facts dd .ext-tone-ok { color: var(--accent) }

/* Action bar — the row's footer: policy picker on the left, the buttons as
   one right-aligned group. Short of room, the group moves to a line of its
   own as a whole; a single button never drops off the end. */
.ext-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .55rem .75rem;
  margin-top: .75rem;
  padding-top: .7rem;
  border-top: 1px solid var(--border2);
}
.ext-buttons {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: .45rem;
  margin-left: auto;
}

/* Update policy — a custom trigger + popover rather than a native <select>.
   Collapsed, a bare <select> barely reads as a control at all; opened, the
   OS paints its own option list that ignores every panel token. The Updates
   card's HH:00 picker avoids the same trap, so this borrows its vocabulary
   (chevron trigger, --bg2 popover, --accent selected row) at row scale. */
.ext-policy {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  font-size: .76rem;
  color: var(--muted);
}
/* Keeps the trigger and its popover one focus/outside-click unit, with the
   label outside so the list is measured against the button alone. */
.ext-policy-control {
  display: inline-block;
}
.ext-policy-btn {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  font: inherit;
  font-size: .78rem;
  line-height: 1.3;
  color: var(--text);
  background: var(--surface-control);
  border: 1px solid var(--outline-subtle);
  border-radius: 8px;
  padding: .28rem .45rem .28rem .6rem;
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
}
.ext-policy-btn:hover:not(:disabled),
.ext-policy-btn[aria-expanded="true"] {
  border-color: var(--accent);
  background: var(--bg);
}
.ext-policy-btn:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 14%, transparent);
}
.ext-policy-btn:disabled { opacity: .5; cursor: progress }
.ext-policy-value { font-weight: 500 }
.ext-policy-chevron {
  display: inline-flex;
  color: var(--muted);
  transition: transform .18s ease, color .15s ease;
}
.ext-policy-btn:hover:not(:disabled) .ext-policy-chevron,
.ext-policy-btn[aria-expanded="true"] .ext-policy-chevron { color: var(--accent) }
.ext-policy-btn[aria-expanded="true"] .ext-policy-chevron { transform: rotate(180deg) }

/* Anchored in the viewport by popover.js (top/left/min-width/max-height are
   set there), so the scrolling settings column cannot clip it and no card
   below can paint over it. z-index sits above the panel's own layers and the
   sticky settings sidebar, below the topbar (100) and modals (9999). */
.ext-policy-pop {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 90;
  /* Wide enough for the longest label ("Pin this version" and its
     translations) without letting one long word stretch the row. */
  max-width: min(15rem, 60vw);
  overflow-y: auto;
  padding: .3rem;
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, .35);
  animation: ext-policy-pop-in .14s ease-out;
}
@keyframes ext-policy-pop-in {
  from { opacity: 0; transform: translateY(-4px) }
  to   { opacity: 1; transform: translateY(0) }
}
.ext-policy-pop[hidden] { display: none }
.ext-policy-opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  width: 100%;
  padding: .35rem .6rem;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: .78rem;
  line-height: 1.4;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.ext-policy-opt:hover,
.ext-policy-opt:focus-visible {
  background: var(--bg);
  color: var(--accent);
  outline: none;
}
/* The current choice is marked with a tick, not a filled block: a highlight
   as wide as the popover reads as a second frame drawn inside the first. */
.ext-policy-opt[aria-selected="true"] {
  color: var(--accent);
  font-weight: 600;
}
.ext-policy-opt[aria-selected="true"]::after {
  content: "✓";
  font-size: .8rem;
  line-height: 1;
}
.ext-btn.primary:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent);
}
.ext-btn.danger:hover:not(:disabled) {
  border-color: var(--err);
  color: var(--err);
}

/* "Connect a client" modal (SPEC 4.2 endpoint access): per endpoint a
   heading, then label / value / Copy rows; the value is a code span or, for
   the client entry and the tunnel command, a wrapped <pre>. */
.ext-connect-endpoint { margin-top: .9rem }
.ext-connect-endpoint h4 { margin: 0 0 .35rem; font-size: .86rem }
.ext-connect-field {
  display: grid;
  grid-template-columns: 6.5rem minmax(0, 1fr) auto;
  gap: .4rem .6rem;
  align-items: start;
  margin-top: .4rem;
  font-size: .8rem;
}
.ext-connect-label { color: var(--muted); padding-top: .3rem }
.ext-connect-value {
  min-width: 0;
  margin: 0;
  padding: .3rem .5rem;
  border-radius: 6px;
  background: var(--surface-control);
  border: 1px solid var(--outline-subtle);
  font-size: .78rem;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

/* Purge checkbox inside the remove-confirmation modal. Scoped under
   .modal-body so it outranks the modal's uppercase form-label rule; the
   input opts out of base.css's full-width text-field styling, or it
   stretches across the row and pushes its text below. */
.modal-body .ext-purge {
  display: flex;
  align-items: flex-start;
  gap: .5rem;
  margin: .6rem 0 .2rem;
  font-size: .82rem;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--text);
  cursor: pointer;
}
.ext-purge input {
  flex: 0 0 auto;
  width: 1rem;
  height: 1rem;
  margin: .1rem 0 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  accent-color: var(--accent);
  cursor: pointer;
}
.ext-purge input:focus { box-shadow: none }
.ext-purge input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px }

/* ── An install under way ─────────────────────────────────────────
   Its row sits above the installed ones while the install runs: a light
   sweep across the row, a breathing glyph and an indeterminate bar along
   its foot. Every animation starts at --ext-since (a negative delay: how
   long the install has run), so a repaint mid-install never restarts it. */
.ext-install-row {
  overflow: hidden;
  animation: ext-install-in .32s ease-out both;
  animation-delay: var(--ext-since, 0s);
}
.ext-install-row[data-install="running"]::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 30%,
    color-mix(in srgb, var(--extension) 10%, transparent) 50%, transparent 70%);
  transform: translateX(-100%);
  animation: ext-install-sweep 2.6s ease-in-out infinite;
  animation-delay: var(--ext-since, 0s);
  pointer-events: none;
}
.ext-install-row[data-install="running"] .ext-glyph {
  animation: ext-install-breathe 1.8s ease-in-out infinite;
  animation-delay: var(--ext-since, 0s);
}
.ext-install-status {
  margin-top: .35rem;
  font-size: .82rem;
  line-height: 1.45;
  color: var(--extension);
}
.ext-install-hint {
  margin-top: .15rem;
  font-size: .76rem;
  line-height: 1.45;
  color: var(--muted2);
}
.ext-install-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  overflow: hidden;
  background: color-mix(in srgb, var(--extension) 14%, transparent);
}
.ext-install-bar::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 35%;
  background: linear-gradient(90deg, transparent, var(--extension), transparent);
  animation: ext-install-bar 1.6s ease-in-out infinite;
  animation-delay: var(--ext-since, 0s);
}

/* Failed: the whole error, in a box of its own, until the owner closes it. */
.ext-install-row[data-install="failed"] {
  border-left-color: var(--err);
  background: color-mix(in srgb, var(--err) 5%, var(--surface-flat));
}
.ext-install-row[data-install="failed"] .ext-install-status {
  color: var(--err);
  font-weight: 600;
}
.ext-install-error {
  margin-top: .4rem;
  padding: .55rem .7rem;
  border-radius: 8px;
  border: 1px solid color-mix(in srgb, var(--err) 30%, var(--outline-subtle));
  background: var(--surface-control);
  font-size: .8rem;
  line-height: 1.5;
  color: var(--text);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* Landed: the extension's own row glows once and says so for a moment. */
.ext-row[data-landed] {
  animation: ext-landed-glow 2.4s ease-out both;
  animation-delay: var(--ext-since, 0s);
}
.ext-landed-note {
  margin-top: .3rem;
  font-size: .78rem;
  font-weight: 500;
  color: var(--accent);
}
.ext-landed-note::before { content: "✓ " }

@keyframes ext-install-in {
  from { opacity: 0; transform: translateY(-6px) }
  to   { opacity: 1; transform: none }
}
@keyframes ext-install-sweep {
  from { transform: translateX(-100%) }
  to   { transform: translateX(100%) }
}
@keyframes ext-install-breathe {
  0%, 100% { transform: scale(1); border-color: var(--outline-subtle) }
  50%      { transform: scale(1.07); border-color: color-mix(in srgb, var(--extension) 55%, transparent) }
}
@keyframes ext-install-bar {
  from { transform: translateX(-100%) }
  to   { transform: translateX(300%) }
}
@keyframes ext-landed-glow {
  from { border-color: var(--accent); box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 45%, transparent) }
  to   { box-shadow: 0 0 0 10px transparent }
}

@media (prefers-reduced-motion: reduce) {
  .ext-install-row,
  .ext-install-row[data-install="running"] .ext-glyph,
  .ext-row[data-landed] { animation: none }
  .ext-install-row[data-install="running"]::before { display: none }
  /* A still bar: the row still says it is busy. */
  .ext-install-bar::before { animation: none; width: 100%; opacity: .5 }
}

@media (max-width: 640px) {
  /* The footer stacks: the policy line first, then the buttons sharing one
     full-width line in equal columns, so three of them never split 2 + 1. */
  .ext-policy { flex: 1 1 100%; justify-content: space-between }
  .ext-buttons {
    flex: 1 1 100%;
    margin-left: 0;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
  }
  .ext-buttons .ext-btn {
    justify-content: center;
    min-width: 0;
    padding-left: .4rem;
    padding-right: .4rem;
  }
  .ext-toolbar { flex-direction: column; align-items: stretch }
  .ext-install-form { flex-direction: column }
  .ext-check-btn { align-self: flex-start }
  .ext-facts { grid-template-columns: 1fr; gap: .15rem }
  .ext-facts dt { margin-top: .45rem }
  .ext-facts dt:first-child { margin-top: 0 }
}

@media (max-width: 380px) {
  /* Three labels no longer fit side by side: one button per line. */
  .ext-buttons:has(.ext-btn:nth-child(3)) { grid-auto-flow: row }
}
