/* =============================================================================
   bitecloud portal — infrastructure console
   Craft bar: AWS Cloudscape / Azure Portal. Executed straight, not ironically.
   Hand-written CSS served from Go embed (ADR-0003). No build step.

   The rules this register imposes, from PRODUCT.md "Brand Commitments":
     - Familiar affordances and ordinary names outrank expression.
     - Density over whitespace: these users scan resources.
     - Colour is functional only - status, links, the primary action.
     - Brand lives in the details: the neutral ramp, the accent, icon weight,
       table headers, focus rings. Never in a metaphor.
   Light is the default: the use scene is a desk, in office light, for long
   sessions, reading dense tables. Dark is a full second theme.
   ============================================================================= */

@font-face{
  font-family:'Archivo';
  src:url('/static/fonts/archivo.woff2') format('woff2-variations'),
      url('/static/fonts/archivo.woff2') format('woff2');
  font-weight:100 900;font-stretch:62% 125%;font-style:normal;font-display:swap;
}
@font-face{
  font-family:'Hanken Grotesk';
  src:url('/static/fonts/hanken-grotesk.woff2') format('woff2-variations'),
      url('/static/fonts/hanken-grotesk.woff2') format('woff2');
  font-weight:100 900;font-style:normal;font-display:swap;
}

:root{
  --font-display:'Archivo','Hanken Grotesk',system-ui,sans-serif;
  --font:'Hanken Grotesk',system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
  --font-mono:ui-monospace,'SF Mono',Menlo,Consolas,'Liberation Mono',monospace;

  /* A warm paper ground, not the default cool grey. The neutrals carry a trace
     of the accent's hue so the whole surface reads as one material. */
  --canvas:#F4F3EF; --surface:#FFFFFF; --surface-2:#FAF9F6; --surface-3:#EFEEE8;
  --ink:#14201D; --ink-2:#4F5D58; --ink-3:#5C6863;   /* AA on every ground */
  --line:#E3E2DA; --line-strong:#C9C8BE;
  /* Must clear 3:1 on BOTH grounds: these borders define controls on surface
     and pills that sit straight on canvas. #8E8E82 cleared surface at 3.31 and
     missed canvas at 2.98. */
  --line-control:#8B8B7F;   /* 3.44:1 on surface, 3.10:1 on canvas */

  /* Nordic sea-green. Every hyperscaler console is blue; this one is not. */
  --accent:#0A6B5C; --accent-hover:#075247; --accent-soft:#E2F0EC;
  --on-accent:#FFFFFF;      /* ink on an accent fill — 6.42:1 here */
  --ok:#0A6B5C; --ok-soft:#DCEDE8;      /* running: the brand hue, earned */
  --warn:#8A5A12; --warn-soft:#F8EEDC;
  --danger:#A32A1E; --danger-soft:#F8E7E4;
  --neutral:#4F5D58; --neutral-soft:#ECEBE4;

  --shadow-sm:0 1px 1px rgba(20,32,29,.04),0 1px 3px rgba(20,32,29,.05);
  --shadow:0 6px 18px -4px rgba(20,32,29,.12),0 1px 2px rgba(20,32,29,.06);

  /* Type roles, not values. Seven steps, each far enough from its neighbour to
     carry a different job. rem so the user's own font size still governs. */
  --fs-display:1.875rem;   /* 30px  page title            */
  --fs-title:1.1875rem;    /* 19px  featured card, figures */
  --fs-head:1rem;          /* 16px  section and panel head */
  --fs-body:0.875rem;      /* 14px  default                */
  --fs-secondary:0.8125rem;/* 13px  taglines, step copy    */
  --fs-meta:0.75rem;       /* 12px  notes, captions        */
  --fs-micro:0.6875rem;    /* 11px  uppercase labels, pills*/
  --lh-tight:1.2; --lh:1.5; --lh-prose:1.55;

  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:20px;
  --sp-6:24px; --sp-7:32px; --sp-8:40px; --sp-9:56px; --sp-10:72px;
  --r-xs:2px; --r:4px; --r-lg:6px; --r-pill:999px;
  --sidebar:248px; --topbar:52px; --content-max:1400px;
  --ease:cubic-bezier(.2,.6,.3,1); --t-fast:100ms; --t:140ms;

  color-scheme:light;
}
:root[data-theme="dark"]{
  /* Dark is ink, not slate: a deep green-black that belongs to the same world. */
  --canvas:#0B120F; --surface:#121A17; --surface-2:#18211D; --surface-3:#1F2A25;
  --ink:#ECEFEA; --ink-2:#A2B0AA; --ink-3:#8A9891;   /* AA on every ground */
  --line:#212C27; --line-strong:#334138;
  --line-control:#5E6F65;   /* 3.32:1 */
  --accent:#4FC2A6; --accent-hover:#77D6BF; --accent-soft:#0F241E;
  --on-accent:#08110E;      /* white on this mint is 2.19:1; this ink is 8.75:1 */
  --ok:#4FC2A6; --ok-soft:#10281F;      /* running */
  --warn:#E0B268; --warn-soft:#241C10;
  --danger:#EC8878; --danger-soft:#2A1613;
  --neutral:#A2B0AA; --neutral-soft:#18211D;
  --shadow-sm:0 1px 2px rgba(0,0,0,.4);
  --shadow:0 8px 22px -6px rgba(0,0,0,.55),0 1px 2px rgba(0,0,0,.4);
  color-scheme:dark;
}
:root[data-theme="dark"] body{letter-spacing:.004em}
:root[data-theme="dark"] .muted,:root[data-theme="dark"] .tiny{letter-spacing:.008em}

/* ---- Base + browser surfaces --------------------------------------------- */
*,*::before,*::after{box-sizing:border-box}
button,input,select,textarea{font:inherit;color:inherit}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--canvas);color:var(--ink);
  font-family:var(--font);font-size:var(--fs-body);line-height:1.5;
  font-variant-numeric:tabular-nums;-webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4{margin:0;font-family:var(--font-display);font-weight:600;letter-spacing:-.015em}
p{margin:0}
a{color:var(--accent);text-underline-offset:2px}
a:hover{color:var(--accent-hover)}
::selection{background:var(--accent-soft);color:var(--ink)}
:root{caret-color:var(--accent);accent-color:var(--accent);scrollbar-color:var(--line-strong) transparent;scrollbar-width:thin}
::-webkit-scrollbar{width:12px;height:12px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--line-strong);border:3px solid var(--canvas);border-radius:var(--r-pill)}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:var(--r-xs);box-shadow:0 0 0 4px var(--accent-soft)}
.skip-link{position:absolute;left:-9999px;top:0;z-index:99;background:var(--accent);color:var(--on-accent);padding:var(--sp-3) var(--sp-5)}
.skip-link:focus{left:0}
.sprite{position:absolute;width:0;height:0;overflow:hidden}
.ic{width:16px;height:16px;flex:none;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.ic-lg{width:20px;height:20px}
/* Brand marks are filled glyphs, not stroked line icons: .ic sets
   fill:none;stroke:currentColor, which would render them invisible. */
.ic-brand{fill:currentColor;stroke:none}

/* Choice cards — the provider picker. Built on the radio-card idiom already
   used for a text choice, scaled up because this decision is the page's
   first and largest, and given a mark so the two options are told apart by
   shape before they are read. */
.choice-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:var(--sp-3)}
.choice{display:grid;grid-template-columns:auto auto minmax(0,1fr);align-items:center;
  gap:var(--sp-3);padding:var(--sp-4);border:1px solid var(--line-strong);border-radius:var(--r-md);
  cursor:pointer;transition:border-color var(--t-fast) var(--ease),background var(--t-fast) var(--ease)}
.choice:hover{border-color:var(--accent)}
.choice input{margin:0}
.choice .ic-brand{width:28px;height:28px;color:var(--ink-2)}
.choice-body{display:flex;flex-direction:column;gap:2px;min-width:0}
.choice-name{font-weight:600;color:var(--ink)}
.choice-note{font-size:var(--fs-meta);color:var(--ink-3)}
.choice:has(input:checked){border-color:var(--accent);background:var(--accent-soft)}
.choice:has(input:checked) .ic-brand{color:var(--accent)}
.choice:has(input:disabled){opacity:.6;cursor:not-allowed}

/* Two fields on one row where they are a pair and the screen has room.
   These users scan resources and favour information per screen over air, so a
   single full-width control per row wastes most of a desktop viewport on a
   form that is seven controls long. Collapses to one column before the fields
   get too narrow to read their own hint text. */
.field-pair{display:grid;grid-template-columns:minmax(0,1fr);gap:0 var(--sp-4)}
@media (min-width:900px){.field-pair{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}}

/* Classes the templates used and the system never defined — the modules rows.
   A fieldset keeps a UA 2px groove bevel and a legend ignores the field-label
   rule, both of which stood out badly in a form otherwise built on 1px
   hairlines. */
fieldset.field{border:0;padding:0;margin-inline:0;min-inline-size:0}
.field > legend{font-size:var(--fs-meta);font-weight:600;color:var(--ink);padding:0;margin-bottom:6px}
.check{display:grid;grid-template-columns:auto 1fr;align-items:baseline;gap:2px var(--sp-3);
  padding:var(--sp-2) 0;cursor:pointer}
.check input{grid-row:1/3;align-self:center;margin:0}
.check-name{font-weight:600;color:var(--ink)}
.check-note{grid-column:2;font-size:var(--fs-meta);color:var(--ink-3)}
.muted{color:var(--ink-2)}
.tiny{font-size:var(--fs-meta)}
/* small is .tiny under the name signup.html.tmpl asked for — same scale,
   a second word because the two were written by different tasks. */
.small{font-size:var(--fs-meta)}
.grow,.spacer{flex:1}
/* The UA's [hidden] rule loses to any class that sets display, so a hidden
   .rows stayed on screen. Anything marked hidden is hidden. */
[hidden]{display:none!important}
.eyebrow{display:block;font-size:var(--fs-micro);font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3);margin-bottom:var(--sp-2)}
.section-label{display:block;font-size:var(--fs-micro);font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3);padding:var(--sp-4) var(--sp-5) var(--sp-2)}

/* ---- Shell ---------------------------------------------------------------- */
.app{display:grid;grid-template-columns:var(--sidebar) 1fr;min-height:100vh}
.sidebar{display:flex;flex-direction:column;background:var(--surface);border-right:1px solid var(--line);position:sticky;top:0;height:100vh}
.brand{display:flex;align-items:center;gap:var(--sp-3);padding:0 var(--sp-5);text-decoration:none;border-bottom:1px solid var(--line);height:var(--topbar);flex:none}
.brand .mark{width:22px;height:22px;display:grid;place-items:center;color:var(--accent)}
.brand .mark svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.6}
.brand .word{font-family:var(--font-display);font-size:var(--fs-head);font-weight:700;letter-spacing:-.02em;color:var(--ink)}
.nav-section{padding-bottom:var(--sp-3)}
.nav{display:flex;flex-direction:column}
/* Rows run edge to edge and the current one is marked on the sidebar's own
   boundary. What this replaces — a rounded accent-filled pill floating inside
   an inset gutter — is the shape every dashboard template ships with. A console
   marks the current resource on the frame, and lets the label carry the weight. */
.nav-item{
  display:flex;align-items:center;gap:var(--sp-3);
  padding:6px var(--sp-5);border:0;border-left:2px solid transparent;
  color:var(--ink-2);text-decoration:none;font-size:var(--fs-secondary);
  background:none;font-family:inherit;cursor:pointer;text-align:left;
  transition:background var(--t-fast) var(--ease),color var(--t-fast) var(--ease);
}
/* The icon is a locator, not a peer of the label. Uniform-weight icons beside
   uniform-weight text is what made this column read as filler. */
.nav-item .ic{color:var(--ink-3);transition:color var(--t-fast) var(--ease)}
.nav-item:hover{background:var(--surface-2);color:var(--ink)}
.nav-item:hover .ic{color:var(--ink-2)}
.nav-item.is-active{background:var(--surface-3);color:var(--ink);font-weight:600;border-left-color:var(--accent)}
.nav-item.is-active .ic{color:var(--accent)}
.nav-item.is-soon,.nav-item.is-soon .ic{color:var(--ink-3);cursor:default}
.nav-item.is-soon:hover{background:none;color:var(--ink-3)}
.nav-item.is-soon:hover .ic{color:var(--ink-3)}
.nav-tag{margin-left:auto;font-size:var(--fs-micro);font-weight:600;letter-spacing:.02em;color:var(--ink-3);border:1px solid var(--line-control);border-radius:var(--r-xs);padding:0 4px}

/* Which organisation you are operating inside scopes every route beneath it,
   so it reads as a strip under the brand rather than a card at the foot. */
.org-scope{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3) var(--sp-5);border-bottom:1px solid var(--line);text-decoration:none;min-width:0}
.org-scope .ic{color:var(--ink-3)}
.org-scope .id{font-family:var(--font-mono);font-size:var(--fs-secondary);color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.org-scope:hover{background:var(--surface-2)}
.org-scope:hover .id{color:var(--accent)}

/* The foot used to repeat the top bar back at the user: a theme toggle and a
   circular monogram of the person already named two inches above it. Help is
   the one thing that earns the position — reachable from anywhere, last. */
.sidebar-foot{margin-top:auto;padding:var(--sp-2) 0 var(--sp-3);border-top:1px solid var(--line)}
.avatar{width:28px;height:28px;flex:none;display:grid;place-items:center;border-radius:var(--r-pill);background:var(--accent);color:var(--on-accent);font-family:var(--font-display);font-size:var(--fs-meta);font-weight:700}
.avatar.sm{width:24px;height:24px;font-size:var(--fs-micro)}

.main{display:flex;flex-direction:column;min-width:0}
.topbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:var(--sp-4);height:var(--topbar);padding:0 var(--sp-6);background:var(--surface);border-bottom:1px solid var(--line)}
/* One line, always. The topbar is a fixed-height strip, so a title that wraps
   pushes itself out of its own bar — "Create a cluster" broke onto three lines
   on a phone and collided with the icon row. */
.page-title{font-size:var(--fs-secondary);font-weight:600;color:var(--ink);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.actions{display:flex;align-items:center;gap:var(--sp-2)}
.iconbtn{width:30px;height:30px;display:grid;place-items:center;position:relative;background:none;font-family:inherit;font-size:var(--fs-body);border:1px solid transparent;border-radius:var(--r);color:var(--ink-2);cursor:pointer;transition:background var(--t-fast) var(--ease)}
.iconbtn:hover{background:var(--surface-3);color:var(--ink)}
.iconbtn .dot{position:absolute;top:5px;right:5px;width:6px;height:6px;border-radius:var(--r-pill);background:var(--danger)}
.search-trigger{display:flex;align-items:center;gap:var(--sp-3);height:30px;padding:0 var(--sp-3);min-width:200px;background:var(--surface-2);border:1px solid var(--line-control);border-radius:var(--r);color:var(--ink-3);font-family:inherit;font-size:var(--fs-secondary);cursor:pointer}
.search-trigger:hover{border-color:var(--accent)}
.search-trigger .label{flex:1;text-align:left}
.search-trigger kbd{font-family:var(--font-mono);font-size:var(--fs-micro);color:var(--ink-3);background:var(--surface);border:1px solid var(--line-strong);border-radius:var(--r-xs);padding:1px 5px}
.menu-btn{display:none}
.content{flex:1}
.content-inner{max-width:var(--content-max);padding:var(--sp-6) var(--sp-6) var(--sp-10)}

/* ---- Page header ----------------------------------------------------------
   Every page opens the way the overview does: what this is on the left, the
   account's own figures on the right, closed by a hairline. The readout is
   optional — with one child the grid is simply one column — but the rule is
   not, because it is what tells the eye where the chrome stops and the page
   starts. This used to be .overview-head and applied to exactly one page. */
.page-intro{
  display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:end;
  gap:var(--sp-8);padding-bottom:var(--sp-6);margin-bottom:var(--sp-7);
  border-bottom:1px solid var(--line);
}
.page-head-main{min-width:0}
.title-row{display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap}
.page-intro h1{font-size:var(--fs-display);font-weight:700;letter-spacing:-.03em;font-stretch:96%}
.page-intro .sub{margin-top:var(--sp-2);color:var(--ink-2);max-width:70ch;line-height:var(--lh-prose);font-size:var(--fs-secondary)}
/* The live-status notice is in the DOM from load — an assistive technology has
   to be watching a region before it changes — but takes no space until the
   poller writes into it. It appears; nothing animates, so there is nothing for
   prefers-reduced-motion to reduce. */
[data-reload-notice]:empty{display:none}
/* A breadcrumb in the eyebrow is a path, not a call to action. */
.page-intro .eyebrow a{color:inherit;text-decoration:none}
.page-intro .eyebrow a:hover{color:var(--accent);text-decoration:underline}

/* A section titles itself on the canvas and only its content sits in a
   container. The overview already worked this way; the product pages wrapped
   every heading in a panel-head instead, so three empty states cost three
   header bands and the page became mostly chrome. */
.section-head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-4);margin-bottom:var(--sp-3)}
.section-head:has(.button){align-items:center}
.section-head h2{font-size:var(--fs-head);font-weight:650;letter-spacing:-.02em}
/* The right-hand slot explains the left-hand heading: what state it is in, or
   why the control beside it is off. */
.section-note{display:flex;align-items:center;gap:var(--sp-3);font-size:var(--fs-meta);color:var(--ink-3)}

/* ---- Panels: the console's primary container ------------------------------ */
.stack{display:flex;flex-direction:column;gap:var(--sp-5)}
/* Rhythm follows grouping: sections inside one family sit close, families are
   separated generously. Repeating a single interval would make four peers of
   two families. */
section + section{margin-top:var(--sp-5)}
section + section.family{margin-top:var(--sp-9)}
/* .stack already spaces its children; without this the two rules stack and
   every product page ran at double the overview's interval. */
.stack > section + section{margin-top:0}
.panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-sm);overflow:hidden}
.panel-head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--sp-5);padding:var(--sp-5);border-bottom:1px solid var(--line);background:var(--surface-2)}
.panel-title{display:flex;gap:var(--sp-3);align-items:flex-start}
.panel-icon{width:28px;height:28px;flex:none;display:grid;place-items:center;border-radius:var(--r);background:var(--accent-soft);color:var(--accent);font-size:var(--fs-meta);font-weight:700}
/* A panel heading takes the body scale whatever its level — the level tracks
   document structure, not size. */
.panel-head h2,.panel h3{font-size:var(--fs-body);font-weight:600}
.panel-head .muted{font-size:var(--fs-secondary);margin-top:2px;max-width:70ch}
.panel > .empty,.panel > .rows,.panel > .checklist,.panel > .field-group,.panel > .panel-grid,.panel > .tiles,.panel > .steps{padding:var(--sp-5)}
/* A table runs flush: its header band is the panel's own top edge. */
.panel > .table-wrap{padding:0}
.panel > .table-wrap .table tr:last-child td{border-bottom:0}
.panel > .form-foot{padding:0 var(--sp-5) var(--sp-5)}
.panel > .checklist + .form-foot{margin-top:0}
/* A note explains the panel it belongs to, whether it sits inside the frame or
   under it. */
.panel-note{color:var(--ink-3);font-size:var(--fs-meta)}
.panel > .panel-note{padding:0 var(--sp-5) var(--sp-5)}
.panel + .panel-note{margin-top:var(--sp-2)}
.panel-grid{display:grid;gap:var(--sp-6);grid-template-columns:minmax(0,2fr) minmax(220px,1fr)}

/* ---- Resource rows -------------------------------------------------------- */
.rows{display:flex;flex-direction:column}
.row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:var(--sp-4);padding:var(--sp-4) 0;border-top:1px solid var(--line);text-decoration:none;color:inherit}
.rows > :first-child{border-top:0;padding-top:0}
a.row:hover strong{color:var(--accent)}
/* A row holding a single line does not need the height of one holding two, so
   key/value lists stay tight while resource rows keep their room. */
.rows .row:not(:has(.row-main)){padding:var(--sp-3) 0}
.row strong{font-size:var(--fs-secondary);font-weight:600;color:var(--ink)}
.row .tiny{color:var(--ink-3);margin-top:2px}

/* ---- Service catalog tiles ------------------------------------------------ */
.tiles{display:grid;gap:var(--sp-4);grid-template-columns:repeat(auto-fill,minmax(268px,1fr))}
/* Two options weighed against each other, not two entries in a catalogue: they
   split the row so the comparison is column against column. */
.tiles.pair{grid-template-columns:repeat(2,minmax(0,1fr))}
.tile{display:flex;flex-direction:column;gap:var(--sp-2);padding:var(--sp-4);background:var(--surface);border:1px solid var(--line);border-radius:var(--r);text-decoration:none;color:inherit;transition:border-color var(--t) var(--ease),box-shadow var(--t) var(--ease)}
a.tile{position:relative}
a.tile::after{content:"";position:absolute;left:-1px;top:-1px;bottom:-1px;width:2px;background:var(--accent);border-radius:var(--r) 0 0 var(--r);transform:scaleY(0);transform-origin:center;transition:transform 220ms var(--ease)}
a.tile:hover{border-color:var(--line-strong);box-shadow:var(--shadow)}
a.tile:hover::after{transform:scaleY(1)}
.tile-head{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3)}
.tile-icon{width:28px;height:28px;flex:none;display:grid;place-items:center;border-radius:var(--r);background:var(--surface-3);color:var(--ink-2)}
.tile h3,.tile h4{font-size:var(--fs-body);font-weight:600;color:var(--ink)}
.tile .tagline,.tile p{font-size:var(--fs-meta);color:var(--ink-2)}
.tile-foot{display:inline-flex;align-items:center;gap:var(--sp-2);margin-top:auto;padding-top:var(--sp-2);font-size:var(--fs-meta);font-weight:600;color:var(--accent)}
/* Inside a tile the whole card is the target, so the call to action is a link
   in the card's own voice — not a filled button competing with it. */
.tile-foot a{display:inline-flex;align-items:center;gap:var(--sp-2);color:inherit;text-decoration:none}
.tile-foot a:hover{color:var(--accent-hover);text-decoration:underline}
.tile.soon .tile-foot{color:var(--ink-3);font-weight:400}
.tile .checklist{margin-top:var(--sp-2)}

/* ---- Status: a dot, a word, a shape. Never colour alone. ------------------ */
.pill{display:inline-flex;align-items:center;gap:6px;justify-self:start;align-self:center;padding:2px 8px 2px 6px;border-radius:var(--r-pill);white-space:nowrap;font-size:var(--fs-micro);font-weight:600;background:var(--neutral-soft);color:var(--neutral);border:1px solid transparent}
.pill::before{content:"";width:6px;height:6px;border-radius:var(--r-pill);background:currentColor;flex:none}
.pill.active{background:var(--ok-soft);color:var(--ok);border-color:color-mix(in srgb,var(--ok) 26%,transparent)}
/* Not a status: nothing is happening, it is simply available to turn on. */
.pill.available{background:transparent;color:var(--ink-2);border-color:var(--line-control)}
.pill.available::before{background:var(--ink-3)}
.pill.soon{background:var(--neutral-soft);color:var(--ink-3)}
.pill.warn{border-color:color-mix(in srgb,var(--warn) 30%,transparent)}
.pill.danger{border-color:color-mix(in srgb,var(--danger) 32%,transparent)}
.pill.warn{background:var(--warn-soft);color:var(--warn)}
.pill.danger{background:var(--danger-soft);color:var(--danger)}

/* alert-ok reads a success banner — the response to minting an invitation,
   here. .alert itself carries no box styling yet (signup.html.tmpl and
   members.html.tmpl both use it unstyled, for a bare line of error text), so
   this modifier supplies its own presentation rather than tint one that
   isn't there. */
.alert.alert-ok{background:var(--ok-soft);border:1px solid color-mix(in srgb,var(--ok) 26%,transparent);border-radius:var(--r);color:var(--ink);padding:var(--sp-4);margin-bottom:var(--sp-4)}
.alert.alert-ok p{margin:0 0 var(--sp-2)}
.alert.alert-ok p:last-child{margin-bottom:0}

/* ---- Controls ------------------------------------------------------------- */
.button{display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);height:32px;padding:0 var(--sp-4);border:1px solid var(--accent);border-radius:var(--r);background:var(--accent);color:var(--on-accent);font-family:inherit;font-size:var(--fs-secondary);font-weight:600;text-decoration:none;cursor:pointer;transition:background var(--t-fast) var(--ease)}
.button:hover{background:var(--accent-hover);border-color:var(--accent-hover);color:var(--on-accent)}
/* An action that is not built yet stays visible — the gap is named, not hidden
   — but it must not carry the weight of one that works. */
/* Dropping the fill left --line-strong as the only thing defining the control,
   at 1.51:1. --line-control is the ramp step that clears 3:1 on both grounds. */
.button:disabled,.button[disabled]{background:none;border-color:var(--line-control);color:var(--ink-3);cursor:not-allowed}
.button.secondary{background:var(--surface);color:var(--accent)}
.button.secondary:hover{background:var(--accent-soft);color:var(--accent-hover)}
.copy-btn{background:var(--surface);border:1px solid var(--line-control);border-radius:var(--r);color:var(--ink-2);font-family:var(--font-mono);font-size:var(--fs-micro);padding:3px 8px;cursor:pointer}
.copy-btn:hover{border-color:var(--accent);color:var(--accent)}

/* .field started as a wrapper — a label with its control inside — and every
   rule below still reaches that control as a descendant. But the templates
   have always put class="field" on the <input> itself (signup, members,
   invitations), where none of those rules match and the control falls back to
   the browser default: no border of ours, wrong height, no padding. Rather
   than rewrite every form, the control rules accept both shapes, and the
   container rule steps aside when .field lands on a control. */
.field:not(input):not(select):not(textarea){display:flex;flex-direction:column;gap:6px;margin-bottom:var(--sp-4)}
.field > span:first-child{font-size:var(--fs-meta);font-weight:600;color:var(--ink)}
/* The border is the only thing that says "this is a field", so it is a
   non-text contrast target (1.4.11). --line-strong measured 1.65:1. */
input.field,select.field,textarea.field,.field input[type=text],.field input[type=email],.field select,.field textarea{width:100%;height:34px;padding:0 var(--sp-3);background:var(--surface);border:1px solid var(--line-control);border-radius:var(--r);color:var(--ink);font-family:inherit;font-size:var(--fs-secondary)}
.field textarea{height:auto;padding:var(--sp-2) var(--sp-3)}
input.field::placeholder,.field input::placeholder{color:var(--ink-3)}
input.field:focus,select.field:focus,textarea.field:focus,.field input:focus,.field select:focus,.field textarea:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}
.field .tiny{color:var(--ink-3)}
.field-group{display:flex;flex-direction:column}
label.field:has(input[type=radio]){flex-direction:row;align-items:flex-start;gap:var(--sp-3);border:1px solid var(--line-strong);border-radius:var(--r);padding:var(--sp-3) var(--sp-4);cursor:pointer;margin-bottom:var(--sp-3)}
label.field:has(input[type=radio]:checked){border-color:var(--accent);background:var(--accent-soft)}
/* field-static shows a value the visitor cannot edit — the address an
   invitation already committed to, in signup.html.tmpl — in the same box
   shape a real input takes, so a read-only value doesn't read as a broken
   one. Compound with .field so it wins over that class's own column layout
   at equal specificity. */
.field.field-static{display:block;height:34px;line-height:32px;margin:0;padding:0 var(--sp-3);background:var(--surface-2);border:1px solid var(--line-control);border-radius:var(--r);color:var(--ink-2);font-size:var(--fs-secondary)}
.form-foot{margin-top:var(--sp-5);display:flex;gap:var(--sp-3);align-items:center}
/* The minted invitation URL, in the one moment it is ever on screen. Two
   things it must do that an <input> was not doing: show the whole string, so
   the reader can check what they are about to send someone, and hand it over
   in one gesture. It wraps rather than scrolling out of sight, and
   user-select:all means a single click takes all of it — no drag, no
   keyboard, nothing to get half-right. The previous attempt put class="field"
   on a bare <input>, which is a wrapper class landing on the element its own
   rules only ever match as a descendant: unstyled, and unselectable. */
.mint-url{display:flex;flex-wrap:wrap;align-items:flex-start;gap:var(--sp-2);margin:var(--sp-3) 0}
.mint-url code{flex:1 1 20rem;padding:var(--sp-2) var(--sp-3);background:var(--surface);border:1px solid var(--line-control);border-radius:var(--r);color:var(--ink);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:var(--fs-secondary);line-height:1.5;word-break:break-all;-webkit-user-select:all;user-select:all}
.mint-url .copy-btn{flex:0 0 auto;align-self:flex-start}

/* ---- Lists, steps, data --------------------------------------------------- */
.checklist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--sp-2)}
.checklist li{display:flex;gap:var(--sp-2);align-items:flex-start;color:var(--ink-2);font-size:var(--fs-meta)}
.checklist .ic{color:var(--ok);margin-top:2px;width:14px;height:14px}
.steps{display:flex;flex-direction:column;gap:var(--sp-4);list-style:none;margin:0;padding:0}
/* A finished step recedes: the mark becomes a check, the copy steps back, and
   the row stops competing with the work that is still outstanding. */
.step.is-done .num{background:var(--ok-soft);color:var(--ok)}
.step.is-done .num .ic{width:13px;height:13px;stroke-width:2.2}
.step.is-done .step-title{color:var(--ink-2)}
.step.is-done p{color:var(--ink-3)}
.step-done-label{
  font-size:var(--fs-micro);font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--ok);margin-left:var(--sp-2);vertical-align:1px;
}
.step{display:grid;grid-template-columns:auto minmax(0,1fr);gap:var(--sp-3);align-items:start}
.step .num{width:22px;height:22px;display:grid;place-items:center;border-radius:var(--r-pill);background:var(--accent-soft);color:var(--accent);font-size:var(--fs-micro);font-weight:700}
/* The checklist heading is a step, not a section: it takes the body scale, not
   the browser's default h3. It became an h3 to fix a heading-level skip and
   nothing re-stated the size. */
.step .step-title{font-size:var(--fs-secondary);margin-bottom:2px}
.step .step-title a{text-decoration:underline;text-underline-offset:3px}
.step p{font-size:var(--fs-secondary);color:var(--ink-2);max-width:68ch;line-height:var(--lh-prose)}
.stats{display:flex;flex-direction:column;gap:var(--sp-4)}
.stat{display:flex;flex-direction:column;gap:2px}
.stat .figure,.stat .value{font-family:var(--font-display);font-size:var(--fs-title);font-weight:700;letter-spacing:-.02em;color:var(--ink);line-height:1.15}
.stat .figure.mono{font-family:var(--font-mono);font-size:var(--fs-secondary);font-weight:500;word-break:break-all}
.stat .label{font-size:var(--fs-micro);color:var(--ink-3)}
.table-wrap{overflow-x:auto}
table{width:100%;border-collapse:collapse;font-size:var(--fs-secondary)}
th{font-size:var(--fs-micro);font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--ink-3);text-align:left;padding:var(--sp-3) var(--sp-4);background:var(--surface-2);border-bottom:1px solid var(--line)}
td{padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--line);font-variant-numeric:tabular-nums}
tr:last-child td{border-bottom:0}
.token{font-family:var(--font-mono);font-size:var(--fs-meta);background:var(--surface-3);border:1px solid var(--line-strong);border-radius:var(--r-xs);padding:1px 5px;color:var(--ink)}
/* An identifier and the control that copies it are two things, side by side.
   Settings had the button nested inside the token chip, which stretched a
   5px-padded inline code chip around a 22px control. */
.value-copy{display:inline-flex;align-items:center;gap:var(--sp-2);min-width:0}
.value-copy .token{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ---- Lifecycle timeline ---------------------------------------------------
   The states read left to right, because the order is the information: what is
   behind this request, where it is now, and what is still ahead. Rendered as a
   vertical stack of pills it said the same words and lost the direction. */
.timeline{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-2);list-style:none;margin:0;padding:var(--sp-5)}
.timeline li{display:flex;align-items:center;gap:var(--sp-2);font-size:var(--fs-secondary);color:var(--ink-3);white-space:nowrap}
.timeline li::after{content:"";width:18px;height:1px;background:var(--line-strong);flex:none;margin-left:var(--sp-2)}
.timeline li:last-child::after{display:none}
.timeline .mark{width:18px;height:18px;flex:none;display:grid;place-items:center;border-radius:var(--r-pill);border:1px solid var(--line-control)}
.timeline .mark .ic{width:11px;height:11px;stroke-width:2.4}
.timeline .is-done{color:var(--ink-2)}
.timeline .is-done .mark{background:var(--ok-soft);border-color:transparent;color:var(--ok)}
/* Where it is now is the one thing the customer came to read. */
.timeline .is-current{color:var(--ink);font-weight:600}
.timeline .is-current .mark{border-color:var(--accent);border-width:2px;box-shadow:0 0 0 3px var(--accent-soft)}
/* A node the engine reported as failed reads as the danger role, not merely
   "current" — the customer needs to see where it broke, not just where it is. */
.timeline .is-failed .mark{border-color:var(--danger);border-width:2px;background:var(--danger-soft);color:var(--danger)}
.timeline .is-failed{color:var(--ink);font-weight:600}

/* Nothing here is still a row of information, so it is laid out as one. Stacked
   vertically at sp-8 it cost ~150px to say "none yet", and a page with three
   sub-resources spent most of its height saying it three times. */
.empty{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:var(--sp-4);padding:var(--sp-5)}
.empty-icon{display:grid;place-items:center;width:32px;height:32px;border-radius:var(--r);background:var(--surface-3);color:var(--ink-3)}
.empty-body{display:flex;flex-direction:column;gap:2px;min-width:0}
.empty h3,.empty h4{font-size:var(--fs-body)}
.empty p{font-size:var(--fs-meta);color:var(--ink-2);max-width:70ch}

/* ---- Command palette, menus ---------------------------------------------- */
.overlay{display:none;position:fixed;inset:0;background:rgba(16,24,40,.44);z-index:60}
.overlay[data-open]{display:block}
.cmdk-wrap{display:none;position:fixed;inset:0;z-index:61;place-items:start center;padding-top:12vh}
.cmdk-wrap[data-open]{display:grid}
.cmdk{width:min(600px,92vw);background:var(--surface);border:1px solid var(--line-strong);border-radius:var(--r-lg);box-shadow:var(--shadow);overflow:hidden}
.cmdk-input-row{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--line)}
.cmdk-input{flex:1;background:none;border:0;color:var(--ink);font-family:inherit;font-size:var(--fs-body)}
.cmdk-input:focus{outline:none}
.cmdk-list{list-style:none;margin:0;padding:var(--sp-2);max-height:52vh;overflow:auto}
.cmdk-item{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-2) var(--sp-3);border-radius:var(--r);color:var(--ink-2);cursor:pointer;font-size:var(--fs-secondary)}
.cmdk-item[aria-selected=true],.cmdk-item:hover{background:var(--accent-soft);color:var(--accent)}
.cmdk-empty,.cmdk-foot{padding:var(--sp-3) var(--sp-4);color:var(--ink-3);font-size:var(--fs-micro)}
.cmdk-foot{border-top:1px solid var(--line);display:flex;gap:var(--sp-4);flex-wrap:wrap}
.dropdown{position:relative}
.account-btn{display:flex;align-items:center;gap:var(--sp-2);background:none;border:0;color:var(--ink-2);cursor:pointer;padding:var(--sp-1);border-radius:var(--r)}
.account-btn:hover{background:var(--surface-3)}
.menu{position:absolute;right:0;top:calc(100% + 6px);min-width:230px;z-index:40;background:var(--surface);border:1px solid var(--line-strong);border-radius:var(--r);box-shadow:var(--shadow);display:none;flex-direction:column;padding:var(--sp-2)}
.dropdown[data-open] .menu{display:flex}
.menu-head{display:flex;gap:var(--sp-3);align-items:center;padding:var(--sp-2) var(--sp-3)}
.menu-head .name{font-size:var(--fs-secondary);font-weight:600;color:var(--ink)}
.menu-head .mail{font-size:var(--fs-micro);color:var(--ink-3)}
.menu .divider{height:1px;background:var(--line);margin:var(--sp-2) 0}
.menu-item{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-2) var(--sp-3);border-radius:var(--r);color:var(--ink-2);text-decoration:none;font-size:var(--fs-secondary);background:none;border:0;font-family:inherit;cursor:pointer;text-align:left;width:100%}
.menu-item:hover{background:var(--surface-3);color:var(--ink)}

/* ---- Public surfaces ------------------------------------------------------ */
.public{min-height:100vh;display:flex;flex-direction:column;background:var(--canvas)}
.public-bar{display:flex;align-items:center;gap:var(--sp-5);padding:var(--sp-4) var(--sp-6);background:var(--surface);border-bottom:1px solid var(--line)}
.public-nav{margin-left:auto;display:flex;gap:var(--sp-5);align-items:center;font-size:var(--fs-secondary)}
.public-nav a{color:var(--ink-2);text-decoration:none}
.public-nav a:hover{color:var(--accent)}
.public-main{flex:1;display:grid;place-items:center;padding:var(--sp-9) var(--sp-6)}
.public-footer{padding:var(--sp-5) var(--sp-6);border-top:1px solid var(--line);color:var(--ink-3);font-size:var(--fs-meta);background:var(--surface)}
.hero{width:100%;max-width:760px}
.hero h1{font-size:clamp(28px,4vw,42px);font-weight:700;letter-spacing:-.025em}
.hero-meta{display:flex;gap:var(--sp-3);align-items:center;margin-top:var(--sp-6);flex-wrap:wrap}
.card{width:min(440px,94vw);background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-sm);padding:var(--sp-7)}
.card h1{font-size:var(--fs-title);margin-bottom:var(--sp-2)}
.card-icon{display:grid;place-items:center;width:36px;height:36px;border-radius:var(--r);background:var(--accent-soft);color:var(--accent);margin-bottom:var(--sp-4)}
.card-grid{display:grid;gap:var(--sp-4);grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
.doc-card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:var(--sp-5);text-decoration:none;color:inherit;display:block}
.doc-card:hover{border-color:var(--accent)}

/* ---- Theme control -------------------------------------------------------- */
.ic-moon{display:none}
:root[data-theme="dark"] .ic-sun{display:none}
:root[data-theme="dark"] .ic-moon{display:block}

/* ---- Motion -------------------------------------------------------------
   Feedback and continuity only. No page-load choreography: a console is read,
   not watched. Timings follow distance — 120ms acknowledges, 180ms explains an
   overlay, nothing runs longer than that. */
@keyframes overlay-in{from{opacity:0}to{opacity:1}}
@keyframes panel-in{from{opacity:0;transform:translateY(-6px) scale(.985)}to{opacity:1;transform:none}}
@keyframes menu-in{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
@keyframes sheet-in{from{transform:translateX(-101%)}to{transform:none}}

.overlay[data-open]{animation:overlay-in 140ms var(--ease) both}
.cmdk-wrap[data-open] .cmdk{animation:panel-in 180ms var(--ease) both}
.dropdown[data-open] .menu{animation:menu-in 120ms var(--ease) both;transform-origin:top right}

/* Press feedback: the smallest change that makes cause and result unmistakable. */
.button:active{transform:translateY(1px)}
.iconbtn:active,.nav-item:active,.copy-btn:active{transform:translateY(.5px)}
.button,.iconbtn,.copy-btn{transition:background var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease),transform 90ms var(--ease)}
.nav-item{transition:background var(--t-fast) var(--ease),color var(--t-fast) var(--ease),transform 90ms var(--ease)}
:focus-visible{transition:box-shadow 120ms var(--ease)}

@media (prefers-reduced-motion:reduce){
  /* Remove spatial movement; keep the feedback that confirms an action. */
  .cmdk-wrap[data-open] .cmdk{animation-name:overlay-in}
  .dropdown[data-open] .menu{animation-name:overlay-in}
  .button:active,.iconbtn:active,.nav-item:active,.copy-btn:active{transform:none}
  a.tile::after{transition:opacity 120ms var(--ease);transform:scaleY(1);opacity:0}
  a.tile:hover::after{opacity:1}
  .sidebar{transition:none}
  .app[data-sidebar="open"] .sidebar{animation:none}
}

/* ---- Responsive: the product is desktop; this keeps small screens usable --- */
@media (max-width:1000px){
  .app{grid-template-columns:1fr}
  .sidebar{position:fixed;inset:0 auto 0 0;width:min(280px,86vw);z-index:70;transform:translateX(-101%);transition:transform var(--t) var(--ease)}
  .app[data-sidebar="open"] .sidebar{transform:none;animation:sheet-in 220ms var(--ease) both}
  .menu-btn{display:grid}
  .content-inner{padding:var(--sp-5) var(--sp-4) var(--sp-9)}
  .topbar{padding:0 var(--sp-4)}
  .search-trigger{min-width:0}
  .search-trigger .label,.search-trigger kbd{display:none}
  .panel-grid{grid-template-columns:minmax(0,1fr)}
}
@media (max-width:640px){
  .panel-head{flex-direction:column;gap:var(--sp-3)}
  .tiles,.tiles.pair{grid-template-columns:minmax(0,1fr)}
  .section-head{flex-direction:column;align-items:flex-start;gap:var(--sp-2)}
}


/* ---- Header readout -------------------------------------------------------
   The console opens with state, not a greeting. The figures are the account's
   own — a count the page can actually observe — never a metric invented to
   fill the slot. Layout only; no colour, face or primitive the system does not
   already own. */
.stat-strip{display:flex;gap:var(--sp-8);margin:0;align-items:flex-end}
.stat-strip .stat{gap:var(--sp-1)}
.stat-strip dt.label{order:2;margin:0}
.stat-strip dd.figure{order:1;margin:0;display:flex;align-items:baseline;gap:6px}
.stat-strip .of{font-family:var(--font);font-size:var(--fs-meta);font-weight:500;letter-spacing:0;color:var(--ink-3)}
.stat-strip .figure.is-mono{font-family:var(--font-mono);font-size:var(--fs-secondary);font-weight:500;letter-spacing:0}

/* The running product is the page's peak: wider, taller, more air inside. */
.tile.is-featured{
  grid-column:1/-1;display:grid;grid-template-columns:auto minmax(0,1fr) auto;
  align-items:center;gap:var(--sp-5);padding:var(--sp-5) var(--sp-6);
  background:linear-gradient(180deg,var(--surface-2),var(--surface));
  border-color:var(--line-strong);
}
.tile.is-featured .tile-icon{width:36px;height:36px;background:var(--accent-soft);color:var(--accent)}
.tile.is-featured h3{font-size:var(--fs-title);letter-spacing:-.02em}
.tile.is-featured .tile-body{display:flex;flex-direction:column;gap:2px;min-width:0}
.tile.is-featured .tile-head{justify-content:flex-start;gap:var(--sp-3)}
.tile.is-featured .tagline{font-size:var(--fs-secondary);max-width:70ch}
.tile.is-featured .tile-foot{font-size:var(--fs-secondary);margin:0;padding:0;white-space:nowrap}

/* What is not built yet states itself once and gets out of the way. */
.soon-list{list-style:none;margin:0;padding:0;border-top:1px solid var(--line)}
.soon-list li{
  display:flex;align-items:center;gap:var(--sp-3);
  padding:var(--sp-3) var(--sp-1);border-bottom:1px solid var(--line);
  font-size:var(--fs-secondary);color:var(--ink-3);
}
.soon-list strong{font-weight:600;color:var(--ink-2)}
.soon-list .muted{flex:1;min-width:0;font-size:var(--fs-meta)}
.soon-list .ic{color:var(--ink-3)}

@media (max-width:900px){
  .page-intro{grid-template-columns:minmax(0,1fr);gap:var(--sp-5)}
  .stat-strip{gap:var(--sp-6);flex-wrap:wrap}
  .tile.is-featured{grid-column:auto}
  .soon-list .muted{display:none}
}

/* ---- Classes the templates used and the system never defined ---------------
   Settings and Members were rendering .row-label / .row-value / .row-main /
   .cell-user / .table with no rules at all: label and value computed
   identically, and the member avatar stacked on top of the name. */
.row-main{display:flex;flex-direction:column;gap:2px;min-width:0}
.row-label{font-size:var(--fs-meta);font-weight:600;color:var(--ink-3)}
.row-value{font-size:var(--fs-body);color:var(--ink)}
.cell-user{display:inline-flex;align-items:center;gap:var(--sp-3);min-width:0}
.cell-user .name{font-weight:600;color:var(--ink)}
.table{width:100%;border-collapse:collapse;font-size:var(--fs-secondary)}
/* Identifiers a customer copies into a terminal or a support thread are set in
   the mono face. Settings, Members and Profile all asked for this class and
   rendered in the body face because nothing defined it. */
.mono{font-family:var(--font-mono);font-size:.95em;letter-spacing:0}

/* A neutral outline button, for the second action beside a primary one.
   .secondary is the accent's own outline; .ghost carries no colour at all —
   easycvr was rendering "View API docs" as a second primary fill.
   list, sits in a table row and must not outweigh the page's one primary
   action either. Same rule rather than a second one, so the two names never
   drift apart in how they look. */
.button.ghost{background:var(--surface);border-color:var(--line-control);color:var(--ink)}
.button.ghost:hover{background:var(--surface-3);border-color:var(--line-strong);color:var(--ink)}
/* .button.ghost is declared after .button:disabled at equal specificity, so its
   full-strength ink was winning on disabled controls. This restates the
   disabled treatment without re-softening the border to --line. */
.button.ghost:disabled,.button.ghost[disabled]{background:none;border-color:var(--line-control);color:var(--ink-3)}

/* Docs cards: the guide index asked for .doc-icon and .doc-foot and got
   unstyled spans, so every card was a title, a paragraph, and a bare arrow. */
.doc-icon{display:grid;place-items:center;width:28px;height:28px;border-radius:var(--r);background:var(--accent-soft);color:var(--accent);margin-bottom:var(--sp-3)}
.doc-card h3{font-size:var(--fs-body);font-weight:600;margin-bottom:var(--sp-2)}
.doc-card p{font-size:var(--fs-meta);color:var(--ink-2);line-height:var(--lh-prose)}
.doc-foot{display:inline-flex;align-items:center;gap:var(--sp-2);margin-top:var(--sp-3);font-size:var(--fs-meta);font-weight:600;color:var(--accent)}

/* A destructive action must never inherit the primary action's colour. */
.panel.danger{border-color:color-mix(in srgb,var(--danger) 45%,var(--line))}
.panel.danger .panel-icon{background:var(--danger-soft);color:var(--danger)}
.button.danger{background:var(--danger);border-color:var(--danger);color:#fff}
.button.danger:hover{filter:brightness(1.08);background:var(--danger);border-color:var(--danger)}
.button.danger:disabled,.button.danger[disabled]{background:none;border-color:var(--line-control);color:var(--ink-3);filter:none}

/* An available pill sits on canvas as often as on surface; the border is the
   only thing defining it, so it needs 3:1 against the darker of the two. */
.pill.available{border-color:var(--line-control)}

/* Visually hidden, still announced — for disambiguating repeated link names. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
