/* Styles for static/components/*.js. Tokens only. */

/* ---- header extras ------------------------------------------------------ */
.brand-mark { display: inline-flex; align-items: center; gap: var(--space-2); }

.account-btn {
  gap: var(--space-2);
  padding-inline: var(--space-2);
  max-width: 200px;
}
/* Neutral, not red. An avatar is an identity marker, not an action, and it sat
   permanently in the corner as a red dot competing with the launch button. */
.account-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: var(--radius-pill);
  background: var(--surface-inset);
  border: 1px solid var(--border-emphasis);
  color: var(--text);
  font-size: var(--text-xs);
  font-weight: 700;
  flex: 0 0 auto;
}
.account-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
}

@media (max-width: 900px) {
  .account-name { display: none; }
}

/* ---- signed-out ----------------------------------------------------------
   See static/components/signed_out.js. The panel variant takes the launch
   console's slot on Home, so it carries the same surface and chamfer the
   console does — the page should look composed, not like something failed to
   load. No clip-path: it holds a focusable button, and clip-path clips the
   focus ring (the same reason the hero cannot hold one). Corner brackets draw
   the chamfer instead. */
/* One compact invitation: copy and action balance a shared row on desktop,
   then stack naturally at phone widths. */
.signed-out-panel {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  padding: var(--panel-padding);
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  text-align: left;
  gap: var(--space-6);
}
.signed-out-panel::before,
.signed-out-panel::after {
  content: "";
  position: absolute;
  width: 10px;
  height: 10px;
  border-color: var(--graph);
  border-style: solid;
  pointer-events: none;
}
.signed-out-panel::before { top: -1px; left: -1px; border-width: 2px 0 0 2px; }
.signed-out-panel::after { bottom: -1px; right: -1px; border-width: 0 2px 2px 0; }

.signed-out-lede {
  margin: 0;
  max-width: 48ch;
  font-size: var(--text-md);
  line-height: var(--leading-snug);
  color: var(--text);
}

/* The one action on the page, so it is sized like one. .btn-lg alone gave a
   114px button; this sets a floor wide enough to read as the destination
   rather than an afterthought. min-width, not width, so a longer label in
   another language still fits. */
.signed-out-panel .btn {
  min-width: 200px;
  min-height: var(--control-height-lg);
  justify-content: center;
  font-size: var(--text-base);
  flex-shrink: 0;
}

/* The empty variant centres its button, which .empty-state does not do for
   inline-block children on its own. */
.signed-out-empty .btn { display: inline-flex; }

@media (max-width: 640px) {
  .signed-out-panel { flex-direction: column; align-items: stretch; gap: var(--space-5); }
  .signed-out-panel .btn { width: 100%; justify-content: center; }
}

/* ---- access card -------------------------------------------------------- */
.access-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--surface-inset);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.access-title {
  font-size: var(--text-base);
  font-weight: 600;
  font-family: var(--font);
  letter-spacing: 0;
  margin-bottom: var(--space-1);
}

.access-row {
  display: grid;
  grid-template-columns: minmax(72px, auto) 1fr auto;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
}

.access-label {
  font-size: var(--text-sm);
  color: var(--text-muted);
  white-space: nowrap;
}

.access-value {
  font-size: var(--text-sm);
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  padding: var(--space-1) var(--space-2);
}

.access-actions {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  /* Copy/reveal stay visible on touch and for keyboard users; only the
     pointer-hover affordance is progressive. */
  opacity: 0.75;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.access-card:hover .access-actions,
.access-card:focus-within .access-actions { opacity: 1; }

.access-quickstart { margin-top: var(--space-1); }
.access-quickstart summary {
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--text-muted);
  padding: var(--space-1) 0;
  list-style: none;
}
.access-quickstart summary::-webkit-details-marker { display: none; }
.access-quickstart summary::before {
  content: "▸";
  display: inline-block;
  margin-right: var(--space-2);
  transition: transform var(--dur-fast) var(--ease-out);
}
.access-quickstart[open] summary::before { transform: rotate(90deg); }
.access-quickstart summary:hover { color: var(--text); }

.access-quickstart-body {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  gap: var(--space-3);
  min-width: 0;
}
.access-quickstart-body .access-pre { margin-top: 0; }
.access-quickstart-body .access-actions { margin-top: var(--space-1); }

.access-pre {
  margin: var(--space-2) 0 0;
  padding: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  overflow-x: auto;
  white-space: pre;
}

.access-note {
  margin: var(--space-1) 0 0;
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.access-cta { display: flex; gap: var(--space-2); margin-top: var(--space-2); flex-wrap: wrap; }

@media (max-width: 640px) {
  .access-row {
    grid-template-columns: 1fr;
    gap: var(--space-1);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--border);
  }
  .access-actions { opacity: 1; justify-content: flex-end; }
}

/* ---- modals (antd Modal) -------------------------------------------------
   Every dialog in the product goes through antd.Modal — launch progress, logs,
   redeem — and none of them were styled, so they wore stock antd: 8px radius
   and a sentence-case IBM Plex title. That was survivable while nothing else
   was styled either; once the destroy popconfirm got the display face and a
   squared surface, the modals became the odd ones out.

   Same treatment, same reasoning as the popconfirm below: squared, hairline,
   display-face title. Not the corner brackets, though — those mark a
   destructive act, and a logs viewer is not one. Restraint: the brackets stay
   rare enough to mean something. */
.ant-modal .ant-modal-content {
  border-radius: 0;
  border: 1px solid var(--border-emphasis);
  background: var(--surface-raised);
  box-shadow: var(--shadow-pop);
}

.ant-modal .ant-modal-header {
  background: transparent;
  border-bottom: 1px solid var(--border);
  margin: 0 0 var(--space-4);
  padding-bottom: var(--space-3);
}

.ant-modal .ant-modal-title {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text);
}

/* antd floats the footer buttons right with its own margins; ours space
   themselves with a flex gap, so the stray margin has to go or the first button
   sits 8px off the row. Same collision as the popconfirm's grid below. */
.ant-modal .ant-modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-5);
}
.ant-modal .ant-modal-footer .btn { margin-inline-start: 0; }

.ant-modal .ant-modal-body {
  max-height: calc(100dvh - 240px);
  overflow-y: auto;
  scrollbar-gutter: stable;
}
.ant-modal .ant-modal-footer { flex-wrap: wrap; }
@media (max-width: 640px) {
  .ant-modal .ant-modal-content { padding: var(--space-4); }
  .ant-modal .ant-modal-title { font-size: var(--text-md); }
  .ant-modal .ant-modal-footer .btn { flex: 1 1 auto; }
}

/* ---- popconfirm (antd Popover) ------------------------------------------
   The destroy confirmation. Same portal problem as the toasts below: antd
   renders popovers at document.body, OUTSIDE the ConfigProvider tree, so the
   token bridge in antd_theme.js never reaches them. Left alone it painted a
   stock antd card — #FDFCFC in DARK MODE, an amber ! roundel, sentence-case
   Bootstrap buttons — floating over a squared black HUD. The bridge could not
   have fixed the worst of it either: colorBgElevated is not in the bridge, and
   antd's `danger` button is its own red, not --danger.

   THIS IS THE MOST DESTRUCTIVE ACTION IN THE PRODUCT, and it looked like it
   came from a different application. Confidence in a confirm dialog is the
   whole point of having one.

   Restyled to the launch console's vocabulary: squared surface, hairline
   border, corner brackets, display-face uppercase title. The brackets are
   pseudo-elements rather than clip-path — clip-path would clip the focus ring
   on the two buttons inside, the same constraint documented on .spec-card and
   .hero--interactive. */
.ant-popover.ant-popconfirm { font-family: var(--font); }

/* Specificity mirrors antd's own `:where(.css-hash).ant-popover .ant-popover-inner`.
   The :where() is zero, so that is (0,2,0); a bare .ant-popover-inner is (0,1,0)
   and loses, leaving antd's white card under ours. */
.ant-popover.ant-popconfirm .ant-popover-inner {
  position: relative;
  /* A FIXED measure, not shrink-to-fit. antd sizes the popover to its content,
     so the panel changed width with the instance name — and the target block
     added below makes that swing much wider. A confirmation that resizes per
     machine reads as unstable. 320px holds the copy at ~34ch, its comfortable
     measure, in both languages. */
  width: 320px;
  max-width: calc(100vw - var(--space-6));
  background: var(--surface-raised);
  border: 1px solid var(--border-emphasis);
  border-radius: 0;
  box-shadow: var(--shadow-pop);
  padding: var(--space-5);
}

/* Targeting brackets, the same device .spec-card uses for selection. Two
   opposite corners read as a reticle without boxing the content in. --danger,
   not --graph: this is the one surface where the accent would be wrong, since
   the panel exists to say "this is destructive". */
.ant-popover.ant-popconfirm .ant-popover-inner::before,
.ant-popover.ant-popconfirm .ant-popover-inner::after {
  content: "";
  position: absolute;
  width: 9px;
  height: 9px;
  border-color: var(--danger);
  border-style: solid;
  pointer-events: none;
}
.ant-popover.ant-popconfirm .ant-popover-inner::before {
  top: -1px; left: -1px;
  border-width: 2px 0 0 2px;
}
.ant-popover.ant-popconfirm .ant-popover-inner::after {
  bottom: -1px; right: -1px;
  border-width: 0 2px 2px 0;
}

/* The arrow is a separate element with its own background; without this it
   stays antd's near-white and points at the card like a stray chip. */
.ant-popover.ant-popconfirm .ant-popover-arrow::before {
  background: var(--surface-raised);
}

/* THREE TYPE ROLES, not one. Everything in this panel used to be 14px — title,
   body and buttons — so nothing led and the eye had no entry point. Measured, it
   was 14px/700 against 14px/400: a weight difference doing a size difference's
   job. The three roles now differ in SIZE, FACE and COLOUR, so the order they
   are read in is not a matter of luck:

     operation  11px display, tracked, --danger  what is about to happen
     target     14px MONO,             --text    which machine it happens to
     cost       14px body,             --muted   what it costs

   The operation line is the SMALLEST of the three, deliberately. It is the least
   informative — you already clicked a button that said Destroy — and making it a
   14px bold headline gave the most weight to the thing carrying the least news. */
.ant-popover.ant-popconfirm .ant-popconfirm-title {
  font-family: var(--font-display);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--danger);
}

.ant-popover.ant-popconfirm .ant-popconfirm-description {
  margin-top: var(--space-3);
  margin-bottom: var(--space-5);
  max-width: 34ch;
}

/* ---- the target block ---------------------------------------------------
   The panel names the machine it is about to destroy. It has to: measured, the
   popover covers .instance-title, .instance-meta and both action buttons, so it
   was asking "destroy this instance?" while occluding which instance and how
   long it had been running.

   MONO, matching .access-value in the SSH card above it. Machine facts are set
   in the mono face throughout this product; an instance name is a machine fact.
   It also makes the name impossible to mistake for prose, which matters when the
   sentence below it is prose. */
.destroy-target {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding-bottom: var(--space-3);
  /* Hairline splits WHAT from WHAT IT COSTS. One rule, doing structural work —
     it is not decoration, it is the seam between identity and consequence. */
  border-bottom: 1px solid var(--border);
}

.destroy-target-name {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-base);
  line-height: var(--leading-snug);
  color: var(--text);
  overflow-wrap: anywhere;
}

/* --text-muted, NOT --text-subtle. Subtle measures 4.22:1 against
   --surface-raised in dark mode — fine on --bg, which is where it is normally
   used, but this panel sits on the raised surface and 11px text has no size
   exemption. Muted clears it at 6.6:1. Caught by the audit, not by eye. */
.destroy-target-spec {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}

.destroy-detail {
  margin: var(--space-3) 0 0;
  font-size: var(--text-base);
  line-height: var(--leading-snug);
  color: var(--text-muted);
}

/* antd's amber ! roundel is dropped: a filled warning glyph imports a palette
   we do not use. Nothing replaces it. The leading rule that lived here briefly
   was a third red mark competing with the corner brackets and the button — the
   brackets already carry the "destructive" signal, and repeating it three times
   on one small panel weakened it rather than reinforcing it. */
.ant-popover.ant-popconfirm .ant-popconfirm-message > .ant-popconfirm-message-icon {
  display: none;
}
.ant-popover.ant-popconfirm .ant-popconfirm-message {
  align-items: stretch;
}

/* Buttons: our .btn ranks, not antd's. The confirm is a REAL destroy button —
   outline, red only on hover — for the same reason .btn-destroy is: a filled
   red button next to a filled neutral one is how people confirm by reflex.
   The dialog is already the deliberate step; the button does not need to shout.

   Specificity mirrors antd's `:where().ant-btn` plus the ancestors, and every
   property antd sets on .ant-btn has to be restated — height and padding are
   set on the base class, so omitting them leaves 32px antd buttons wearing our
   type. */
/* EQUAL COLUMNS, full width — not floated right at their natural widths. That
   was the "weird alignment": Cancel measured 93px and Destroy 103px, right-
   aligned, so neither edge lined up with the text block above and the ragged
   left edge left a 90px gap under the copy. Two equal columns give the panel a
   flat base and make the two outcomes visibly equal in weight, which is the
   correct reading — the dialog exists to make the choice deliberate, not to
   steer it. */
.ant-popover.ant-popconfirm .ant-popconfirm-buttons {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
}
/* antd puts margin-inline-start:8px on each button to space its own flex row.
   Under grid the gap already does that, and the leftover margin inset the first
   column by 8px — so the button edge missed the text edge above it by exactly
   that. Measured 94 against 86 before this line; the grid gap owns the spacing
   now. */
.ant-popover.ant-popconfirm .ant-popconfirm-buttons .ant-btn { margin-inline-start: 0; }
.ant-popover.ant-popconfirm .ant-popconfirm-buttons .ant-btn {
  height: 36px;
  padding: 0 var(--space-4);
  border-radius: var(--radius-control);
  border: 1px solid var(--border-control);
  background: transparent;
  color: var(--text);
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  box-shadow: none;
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.ant-popover.ant-popconfirm .ant-popconfirm-buttons .ant-btn:hover {
  border-color: var(--border-emphasis);
  color: var(--text);
  background: var(--surface-inset);
}
/* The destructive one. Matches .btn-destroy exactly, including that it carries
   no fill at rest. */
.ant-popover.ant-popconfirm .ant-popconfirm-buttons .ant-btn-dangerous {
  color: var(--text-muted);
  border-color: var(--border-control);
  background: transparent;
}
.ant-popover.ant-popconfirm .ant-popconfirm-buttons .ant-btn-dangerous:hover,
.ant-popover.ant-popconfirm .ant-popconfirm-buttons .ant-btn-dangerous:focus-visible {
  color: var(--danger);
  border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 12%, transparent);
}
/* antd sets `outline: none` on .ant-btn, so the ring has to be restated. No
   !important needed: our two ancestor classes out-specify antd's
   `:where(.css-hash).ant-btn`, whose :where() counts as zero.

   Do not try to verify this with element.focus() — :focus-visible does not match
   programmatic focus, so the computed outline reads `none` and looks like the
   rule lost a specificity fight it never entered. Tab to it, or apply the same
   declaration without the pseudo-class to confirm it paints. */
.ant-popover.ant-popconfirm .ant-popconfirm-buttons .ant-btn:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* ---- toasts (antd message) ----------------------------------------------
   antd renders message notices into a portal at document.body, OUTSIDE the
   ConfigProvider tree, so none of the theme tokens in antd_theme.js reach them.
   Left alone they render as stock antd: white card, 8px radius, -apple-system,
   and antd's own green tick — a Bootstrap-looking chip floating over a squared
   HUD. Restyled here rather than in JS so every caller is covered at once,
   including launch_flow.js's success/error/warning paths.

   Positioned top-centre by antd; kept there deliberately, since the launch modal
   owns the centre of the screen and a toast lower down would collide with it. */
.ant-message { font-family: var(--font); }

/* Specificity has to MATCH antd's, which emits:
     :where(.css-hash).ant-message .ant-message-notice-wrapper .ant-message-notice-content
   The :where() counts as zero, so that is (0,2,1) — .ant-message plus
   .ant-message-notice-wrapper plus the content class. A bare class, or even
   `:root .ant-message-notice-content`, loses and the wrapper keeps its white
   8px card under our dark one, rendering a dark chip inside a white pill.
   Mirroring the two ancestor classes ties, and later-in-cascade wins. */
.ant-message .ant-message-notice-wrapper .ant-message-notice-content {
  padding: 0;
  background: transparent;
  box-shadow: none;
  border-radius: 0;
}

.ant-message-custom-content {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-raised);
  color: var(--text);
  border: 1px solid var(--border-emphasis);
  /* The same chamfer the hero panel uses, so a toast reads as a piece of this
     product rather than a browser notification. */
  clip-path: polygon(
    0 0, 100% 0,
    100% calc(100% - var(--notch)), calc(100% - var(--notch)) 100%,
    0 100%
  );
  box-shadow: var(--shadow-pop);
  font-size: var(--text-base);
  text-align: left;
}

/* A 2px rule on the leading edge carries the status colour, replacing antd's
   coloured glyph. Same device as the hero's registration mark. */
.ant-message-custom-content::before {
  content: "";
  align-self: stretch;
  width: 2px;
  flex: 0 0 auto;
  margin-right: var(--space-1);
  background: var(--text-muted);
}
.ant-message-success::before { background: var(--graph); }
.ant-message-error::before   { background: var(--danger); }
.ant-message-warning::before { background: var(--attention); }
.ant-message-info::before    { background: var(--info); }

/* antd's own icon glyph is redundant next to the rule and imports its palette. */
.ant-message-custom-content > .anticon { display: none; }
