/* Shared chrome: reset, header, rails, buttons, type scale.
   Colours, spacing, and font sizes come only from tokens.css. */

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

html {
  scrollbar-gutter: stable;
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: calc(var(--header-height) + var(--space-5));
}

/* Every owned overflow region inherits one operable scrollbar treatment. */
* { scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track); scrollbar-width: thin; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--scrollbar-track); }
::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border: 2px solid var(--scrollbar-track); }
::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-hover); }
::-webkit-scrollbar-thumb:active { background: var(--scrollbar-active); }
@media (forced-colors: active) {
  * { scrollbar-color: auto; }
}

:where(button, input, select, textarea, a, [tabindex]) {
  scroll-margin-block: calc(var(--header-height) + var(--space-4)) var(--space-5);
}

body {
  --site-help-footer-rail: var(--page-rail);
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

#root { min-height: 100vh; }

/* The layout has to inherit the full-height floor, not just sit inside it.
   #root is min-height:100vh but display:block, so an .ant-layout child is only
   as tall as its content — on a short page (admin's Users tab at 375px) that
   left the layout 31px shy of the viewport and its background stopped early,
   showing a band of bare --bg under the console.

   Both rules are needed: min-height gives the floor, and #root becoming a flex
   column is what lets the layout actually grow into it. */
#root { display: flex; flex-direction: column; }
.ant-layout {
  background: var(--bg);
  flex: 1 0 auto;
  min-height: 100vh;
}

a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--border-emphasis);
  text-underline-offset: 2px;
  transition: text-decoration-color var(--dur-fast) var(--ease-out);
}
a:hover { text-decoration-color: var(--text); }

/* ---- type --------------------------------------------------------------
   Every heading is Chakra Petch and every heading is UPPERCASE. Uppercase is
   doing real work here rather than shouting: this face was drawn for HUD
   labels, its caps are the same width as its lowercase, and setting them upper
   makes headings read as machine annotations on a panel — which is what they
   are — instead of as prose. Body copy stays sentence case so nothing that must
   actually be READ is set in caps. */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: var(--tracking-display);
  line-height: var(--leading-tight);
  text-transform: uppercase;
  margin: 0;
}

.display-1 { font-size: var(--text-4xl); font-weight: 700; }
.display-2 { font-size: var(--text-2xl); }

/* Panel chamfer. A single cut on the bottom-right, always --notch, applied to
   static containers only — clip-path CLIPS THE FOCUS RING, so it must never go
   on a focusable control. Interactive elements get corner brackets instead. */
.chamfer {
  clip-path: polygon(
    0 0, 100% 0,
    100% calc(100% - var(--notch)), calc(100% - var(--notch)) 100%,
    0 100%
  );
}

/* One eyebrow definition, aliased for the pages that used the old names.
   The previous build defined this same recipe four times under four names
   (.page-eyebrow, .gallery-eyebrow, .status-eyebrow, .preview-eyebrow),
   which is exactly how they drifted apart. */
.eyebrow,
.page-eyebrow,
.gallery-eyebrow,
.status-eyebrow,
.preview-eyebrow {
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
  font-family: var(--font-display);
}

.lede {
  color: var(--text-muted);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  max-width: 60ch;
  margin: 0;
}

.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ---- header -------------------------------------------------------------
   Two tiers, after developer.amd.com:

     .header-identity   black in BOTH themes — brand, account, global toggles
     .header-nav-bar    themed — where you go

   The identity bar overrides the text/border tokens locally rather than
   hardcoding colours, so everything inside it (icon buttons, the instance pill,
   the account chip) inherits correct-on-black values without needing its own
   dark-tier variant. The AMD lockup is drawn for a black field; keeping that
   field constant is why it does not need a light-mode redraw.

   .app-header below is the SINGLE-TIER header still used by admin, status,
   github-landing, and notebook-preview. Those pages are not React and do not
   render this component. Both must keep working. */
.site-header { position: sticky; top: 0; z-index: 20; }

/* --header-rail, not --page-rail: the chrome frames the content column and sits
   wider than it. Both tiers use this one rule, so the brand and the nav links
   below it stay on the same vertical line — change the rail here and they move
   together. See --header-rail in tokens.css for why it is capped. */
.header-inner {
  width: 100%;
  max-width: var(--header-rail);
  margin: 0 auto;
  padding: 0 var(--page-gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  min-width: 0;
}

.header-identity {
  --text: #F5F5F6;
  --text-muted: #A1A1A8;
  --text-subtle: #7E7E86;
  --border: #2A2A2E;
  --border-emphasis: #3D3D42;
  --surface-inset: #1C1C1F;
  /* --control and --border-control belong to this list too. This bar is black
     in BOTH themes, so it re-declares the neutrals it sits on; miss one and a
     control inherits the PAGE's value against a black backdrop. That is exactly
     what happened to the signed-out Sign in button — .btn-secondary took a
     white --control with the near-white --text above, and the label vanished. */
  --control: #1C1C1F;
  --border-control: #5A5A62;
  background: #000;
  color: var(--text);
  height: var(--header-identity-height);
  display: flex;
  align-items: center;
}

.header-nav-bar {
  height: var(--header-nav-height);
  min-height: var(--header-nav-height);
  display: flex;
  align-items: center;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--border);
}
/* Tabs are a left-aligned cluster, not a full-width space-between row.
   Brand and account stay on .header-identity; this inner must not stretch
   Home / Gallery apart to fill the rail. */
.header-nav-bar .header-inner {
  justify-content: flex-start;
  height: 100%;
}

/* ---- single-tier header (non-React pages) ------------------------------- */
.app-header {
  min-height: var(--header-identity-height);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  padding: 0 max(var(--page-gutter), calc((100% - var(--header-rail)) / 2 + var(--page-gutter)));
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 20;
}
.ant-layout-header.app-header {
  height: var(--header-identity-height);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  line-height: unset;
  padding-inline: max(var(--page-gutter), calc((100% - var(--header-rail)) / 2 + var(--page-gutter)));
}

/* Standalone pages use a single identity tier, with the same rail as content. */
body:has(.app-header):not(:has(.site-header)) { --header-height: var(--header-identity-height); }
body:has(.content.ma-wide), body:has(.admin-identity) { --page-rail: var(--rail-wide); }
.app-header .app-nav { height: var(--header-identity-height); }

.left-header { display: flex; align-items: center; gap: var(--space-6); min-width: 0; }
.header-actions { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }

/* ---- brand ------------------------------------------------------------- */
.brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text);
  text-decoration: none;
  min-width: 0;
  white-space: nowrap;
}
.brand:hover { text-decoration: none; }
/* THE LOCKUP IS SIZED BY ONE NUMBER: --brand-amd-h. Everything else is a
   calc() off it, so resizing the brand is a single-value edit.

   The two wordmarks are matched by PAINTED GLYPH height, not by box height.
   Setting both SVGs to the same height renders AMD 12.5% larger, because the
   AMD art fills its viewBox (glyph 190.8 of 191 units) while the Radeon art
   carries 12 units of top padding (glyph 173.1 of 195). Measured in-browser
   with getBBox().

   Hence the 1.126516 factor: 195/173.1. It makes the Radeon glyph PAINT at
   --brand-amd-h even though its BOX is taller.

   These used to be three hardcoded pixel values (17 / 19.15 / 24) duplicated
   again in admin.html's mobile block, which meant changing the brand size
   silently desynchronised the two. Do not reintroduce literals here. */
.brand { --brand-amd-h: var(--header-brand-height); }
.brand-amd, .brand-radeon { display: inline-flex; }
.brand-amd { height: var(--brand-amd-h); }
.brand-radeon { height: calc(var(--brand-amd-h) * 1.126516); }
.brand-amd svg { height: var(--brand-amd-h); width: auto; display: block; }
.brand-radeon svg { height: calc(var(--brand-amd-h) * 1.126516); width: auto; display: block; }
.brand-rule { width: 1px; height: calc(var(--brand-amd-h) * 0.82); background: var(--border); flex: 0 0 auto; }

/* CLOUD is set to read as PART OF the Radeon lockup, not as a caption beside
   it. Three things do that: the display face (whose flat terminals and squared
   counters match the wordmark's construction), full --text weight rather than
   muted, and a cap height matched to the 17px SVG rather than a small-label
   size. It sits on the wordmark's baseline, so the three marks scan as one. */
.brand-cloud {
  font-family: var(--font-display);
  /* Matched by CAP HEIGHT to the wordmark glyphs, not by font-size. Chakra
     Petch's caps are ~0.70em, so dividing by 0.70 gives capitals that stand
     exactly as tall as the letters in RADEON beside it. */
  font-size: calc(var(--brand-amd-h) / 0.7);
  font-weight: 600;
  line-height: var(--brand-amd-h);
  letter-spacing: 0.06em;
  color: var(--text);
  text-transform: uppercase;
  /* Extra air before CLOUD. The lockup reads AMD | RADEON CLOUD: the rule
     already separates AMD, so this gap is what stops RADEON and CLOUD from
     fusing into one word. */
  margin-left: var(--space-2);
}

/* ---- nav: compact display labels, quiet neutral active marker ----------- */
.app-nav {
  display: flex;
  align-items: stretch;
  justify-content: flex-start;
  gap: var(--header-nav-gap);
  height: var(--header-nav-height);
  min-width: 0;
  width: max-content;
  max-width: 100%;
  flex: 0 1 auto;
}

.nav-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding: 0 var(--header-nav-item-pad);
  font-family: var(--font-display);
  font-size: var(--header-nav-size);
  font-weight: 600;
  letter-spacing: var(--header-nav-tracking);
  line-height: var(--leading-snug);
  text-transform: uppercase;
  color: var(--text-muted);
  text-decoration: none;
  white-space: nowrap;
  background: none;
  border: 0;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}
.nav-link:hover { color: var(--text); text-decoration: none; }
.nav-link[aria-current="page"] { color: var(--text); font-weight: 600; }

/* The active-page marker is NEUTRAL. It was AMD red, which put a second red
   element on a page whose only red should be the launch button — the eye read
   the underline as an action before it found the actual one.
   Width 80% / centered matches developer.amd.com's .nav-link:after. */
.nav-link::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 0;
  height: 2px;
  background: var(--text);
  transform: translateX(-50%);
  transition: width var(--dur-base) var(--ease-out);
}
.nav-link:hover::after { width: 80%; background: var(--border-emphasis); }
.nav-link[aria-current="page"]::after { width: 80%; background: var(--text); }

.nav-beta {
  margin-left: var(--space-1);
  padding: 1px 5px;
  border-radius: var(--radius-control);
  border: 1px solid var(--border);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--text-subtle);
  vertical-align: 2px;
}

/* ---- buttons ------------------------------------------------------------
   Three ranks, and the distinction between them is load-bearing:

     .btn-primary   filled accent blue — the one thing to do on this surface
     .btn-secondary neutral outline    — everything else
     .btn-destroy   neutral outline, RED only on hover/focus

   .btn-destroy deliberately does NOT get a fill. A filled destructive button
   sitting beside a filled primary button is how people destroy work they meant
   to keep. It always pairs with a confirm step. Now that primary is blue and
   destroy is red, the two differ in hue as well as weight. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--control-height);
  padding: 0 var(--space-4);
  border-radius: var(--radius-control);
  border: 1px solid transparent;
  /* Buttons are labels on a machine: display face, uppercase, tracked. */
  font-family: var(--font-display);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; transform: none; }

.btn-lg { min-height: var(--control-height-lg); padding: var(--space-2) var(--space-5); font-size: var(--text-base); }
.btn-sm { min-height: var(--control-height-sm); padding: var(--space-1) var(--space-3); font-size: var(--text-sm); }
.btn-block { width: 100%; }

.btn-primary { background: var(--action); color: var(--on-action); }
.btn-primary:hover:not(:disabled) { background: var(--action-hover); }
.btn-primary:active:not(:disabled) { background: var(--action-active); }

.btn-secondary {
  background: var(--control);
  color: var(--text);
  border-color: var(--border-control);
}
.btn-secondary:hover:not(:disabled) { border-color: var(--text-muted); background: var(--surface-inset); }

.btn-ghost { background: transparent; color: var(--text-muted); }
.btn-ghost:hover:not(:disabled) { background: var(--surface-inset); color: var(--text); }

.btn-destroy {
  background: transparent;
  color: var(--text-muted);
  border-color: var(--border-control);
}
/* --danger, NOT --action-text. Both were red when this was written; --action-text
   is now blue, so reusing it here would have turned the destroy button into a
   second primary-coloured control on hover — the precise confusion this class
   exists to prevent. */
.btn-destroy:hover:not(:disabled),
.btn-destroy:focus-visible:not(:disabled) {
  color: var(--danger);
  border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 12%, transparent);
}

/* ---- icon buttons (theme, language) ------------------------------------ */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  height: 34px;
  min-width: 34px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-control);
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-muted);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.icon-btn:hover { background: var(--surface-inset); color: var(--text); }

/* RADEON-INTERNAL-RECONCILED
   The classic-UI escape hatch. Shares .icon-btn's geometry with its neighbours so
   the row stays one row, and carries a hairline border they do not: the theme and
   language toggles report state, this one leaves the page, and the outline is what
   says so without resorting to the accent colour. The accent means "this is the
   thing to press", and an exit is not that.

   Tracked and uppercased to match the nav rather than the account name, because it
   is chrome, not content. Hidden below 900px, where .account-name is already gone
   and the row has no space left; Settings keeps the same control in prose. */
.icon-btn.ui-switch {
  border-color: var(--border);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
}
.icon-btn.ui-switch:hover {
  border-color: var(--border-emphasis);
  text-decoration: none;
}
@media (max-width: 900px) {
  .icon-btn.ui-switch { display: none; }
}

/* ---- instance pill ------------------------------------------------------ */
.instance-slot {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  color: var(--text-muted);
  text-decoration: none;
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.instance-slot:hover { text-decoration: none; border-color: var(--border-emphasis); color: var(--text); }
.instance-slot-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--text-subtle);
  flex: 0 0 auto;
}
.instance-slot.is-running .instance-slot-dot {
  background: var(--success);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--success) 22%, transparent);
}

/* ---- layout ------------------------------------------------------------- */
.content {
  width: 100%;
  max-width: var(--page-rail);
  min-width: 0;
  margin: 0 auto;
  padding: var(--page-top) var(--page-gutter) var(--space-7);
  box-sizing: border-box;
}
.content.ma-wide { max-width: var(--rail-wide); }

/* Page titles establish hierarchy without pushing the workspace down. */
.page-heading { margin-bottom: var(--space-6); }
.page-heading .eyebrow { margin-bottom: var(--space-3); }
.page-heading h1 { font-size: var(--text-2xl); margin-bottom: var(--space-2); overflow-wrap: anywhere; }
.page-heading h1:last-child { margin-bottom: 0; }
.page-heading .lede { max-width: 65ch; font-size: var(--text-base); }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-5);
}

.footer-brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
  color: var(--text-muted);
  font-size: var(--text-sm);
}
/* The footer carries the AMD mark alone (no RADEON, no CLOUD), so it sets the
   one variable rather than overriding heights directly. */
.footer-brand { --brand-amd-h: 12px; }
.footer-brand .brand-amd { height: var(--brand-amd-h); color: var(--text-muted); }
.footer-brand .brand-amd svg { height: var(--brand-amd-h); }

/* ---- responsive --------------------------------------------------------- */
@media (max-width: 900px) {
  .left-header { gap: var(--space-4); }
  /* 641-900px is the band where the FULL lockup (364px at 20px) still shows
     alongside the instance pill and the account button. Measured at 641px the
     20px lockup overlapped .header-actions by 9px and clipped the pill's label.
     Stepping the one variable back to 17px shrinks the whole lockup in
     proportion — RADEON and CLOUD follow by calc(). Below 640px the AMD mark
     and rule drop out entirely, which frees the width again. */
  .brand { --brand-amd-h: 20px; }
  /* The nav bar SURVIVES on mobile — it is only three links now, where the old
     five-item nav had to be hidden behind nothing at all, stranding phone users
     with no way to reach Gallery or Models. Compact height, 16px floor, scroll
     rather than wrapping so the cluster stays one row. */
  .header-nav-bar,
  .app-nav {
    height: var(--header-nav-height);
    min-height: var(--header-nav-height);
  }
  .nav-link { font-size: var(--header-nav-size); padding: 0 var(--space-4); }
  .app-nav { gap: var(--space-1); overflow-x: auto; }
}
@media (max-width: 640px) {
  .admin-identity .ant-tag { display: none; }
  /* The brand lockup is a fixed-width SVG pair and will not shrink, so without
     these the header actions slide underneath it on a narrow phone. */
  .brand { flex: 0 1 auto; overflow: hidden; }
  .header-actions { flex: 0 0 auto; gap: var(--space-1); }
  .instance-slot { display: none; }
  /* CLOUD stays. It was hidden here when it was a muted caption; it is now part
     of the wordmark, and dropping it renamed the product to "AMD Radeon" on
     every phone. The AMD mark yields instead — it is the one piece a narrow
     header can spare, since Radeon Cloud is the product being named. */
  .brand-amd { display: none; }
  .brand-rule { display: none; }
  /* Even without the AMD mark, RADEON CLOUD at 17px is 226px and the last
     letters fell off a 375px phone — .brand's overflow:hidden above CLIPS that
     rather than reporting it, so it will not show up as a collision or a
     scrollbar. Only a screenshot catches it. 15px keeps the word whole. */
  .brand { --brand-amd-h: 16px; }
}
/* Larger identity controls share the taller bar; compact geometry keeps the
   complete product name visible on phones. */
.header-identity .icon-btn, .header-identity .theme-toggle { min-width: 40px; height: 40px; font-size: var(--text-base); }
/* MUST outrank the line above, and does so by being MORE SPECIFIC rather than by
   sitting below it. `.icon-btn.ui-switch` (where the 12px is declared, ~line 450)
   and `.header-identity .icon-btn` are both (0,2,0) — a tie, broken by source
   order, which the 40px/16px rule won purely by being further down the file. The
   label therefore rendered at 16px in a 40px box: the size of the account name
   beside it, on a control its own comment describes as chrome. Scoping this to
   (0,3,0) makes the intended size apply and survives any future reordering.

   Smaller than its neighbours ON PURPOSE. Theme and language report the state of
   the page you are on; this one leaves it. Matching their weight advertised the
   exit as an equal of the controls you actually use. */
.header-identity .icon-btn.ui-switch {
  height: 28px;
  min-width: 0;
  font-size: var(--text-xs);
  padding-inline: var(--space-2);
}
.header-identity .instance-slot { font-size: var(--text-base); padding-block: var(--space-2); }
.header-identity .account-avatar { width: 28px; height: 28px; font-size: var(--text-sm); }
@media (max-width: 1100px) {
  .header-identity .account-name, .header-identity .ui-switch { display: none; }
}
@media (max-width: 760px) {
  .header-identity .instance-slot { display: none; }
}
@media (max-width: 640px) {
  .header-inner { gap: var(--space-2); }
  .header-identity .icon-btn, .header-identity .theme-toggle { min-width: 36px; height: 40px; padding-inline: var(--space-1); }
}
@media (max-width: 380px) {
  .brand { --brand-amd-h: 12px; }
  .brand-cloud { margin-left: var(--space-1); }
  .header-inner { gap: var(--space-1); }
  .nav-link { padding-inline: var(--space-3); }
  .header-identity .icon-btn, .header-identity .theme-toggle { min-width: 32px; }
}
