/* =====================================================================
   css/premium.css — PREMIUM PRESENTATION LAYER (redesign 2026-07).
   Purely visual: card surfaces, type scale, whitespace, hover/focus
   motion, and intentional desktop layouts for the Home (js/home.js,
   .uh-*) and Asset Workspace (.cc2) surfaces.

   RULES
   - Tokens only (var(--k-*) / public tokens). No raw hex. rgba() is
     used exclusively for neutral shadow/scrim alpha.
   - No behavior: nothing here changes markup, routing, data or copy.
   - Theme-aware: [data-theme="dark"] refines shadow/border strength.
   - Loaded LAST so equal-specificity rules override the inline app.html
     card baseline without touching it.
   ===================================================================== */

/* ---- motion + interaction primitives -------------------------------- */
.card,
.uh-tile,
.uh-asset-row,
.uh-act-row,
.row.clickable,
.fab-sheet-row {
  transition: transform 180ms cubic-bezier(.2, .6, .3, 1),
              box-shadow 180ms cubic-bezier(.2, .6, .3, 1),
              border-color 180ms ease,
              background-color 180ms ease;
}
@media (hover: hover) {
  .uh-clickable:hover,
  .card.clickable:hover,
  .uh-asset-row:hover,
  .uh-act-row:hover {
    transform: translateY(-2px);
    box-shadow: 0 2px 4px rgba(6, 12, 20, .05), 0 16px 32px -16px rgba(6, 12, 20, .18);
  }
}
.uh-clickable:active,
.card.clickable:active,
.uh-asset-row:active,
.uh-act-row:active {
  transform: translateY(0) scale(.99);
}
:where(.uh-clickable, .uh-asset-row, .uh-act-row, .card.clickable, button):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 12px;
}
@media (prefers-reduced-motion: reduce) {
  .card, .uh-tile, .uh-asset-row, .uh-act-row, .row.clickable, .fab-sheet-row {
    transition: none;
  }
  .uh-clickable:hover, .card.clickable:hover, .uh-asset-row:hover, .uh-act-row:hover {
    transform: none;
  }
}

/* ---- premium card surface (both surfaces) --------------------------- */
.uh-root .card,
.cc2 .card {
  border-radius: 20px;
  border: 1px solid var(--line);
  box-shadow: 0 1px 2px rgba(6, 12, 20, .04), 0 10px 28px -18px rgba(6, 12, 20, .14);
}
:root[data-theme="dark"] .uh-root .card,
:root[data-theme="dark"] .cc2 .card {
  box-shadow: 0 1px 0 rgba(255, 255, 255, .03) inset, 0 16px 40px -24px rgba(0, 0, 0, .55);
}

/* ---- Asset Workspace rhythm ----------------------------------------- */
/* `html .cc2` (0,1,1): must out-rank BOTH the app.html inline `.cc2`
   baseline (0,1,0) and comfort.polish's `html.comfort-on .cc2` gap rule
   (0,1,1 — beaten here by cascade order, premium.css loads last). */
html .cc2 { display: flex; flex-direction: column; gap: 18px; }

/* RESERVE THE ASYNC SUB-VIEW'S BOX (2026-08-12, asset surface render
   stability). `.cc2-loading` had NO rule anywhere in the codebase: it painted
   as a bare line of text ~20px tall, and when the ledger cache landed the
   sub-view grew to full content height. Measured on staging, cold boot of the
   asset surface: #tab-content h229->834 and h112->834, three shifts of
   identical value 0.05651, CLS 0.170 against <=0.005 on every other route.

   This is the same defect class as the un-reserved photo box, on the async
   sub-view instead of an image: the container does not hold the space its
   content will need. Reserving the remaining viewport makes the loading state
   occupy approximately the box the content lands in, so the transition stops
   moving the page.

   It is a FLOOR, not a fixed height -- content taller than the viewport still
   grows past it, which is correct; the goal is to stop the collapse-and-
   restore, not to clip anything. Centring the text keeps the loading state
   from looking like a mis-rendered empty page at that size. */
html .cc2-loading {
  min-height: calc(100vh - 4.5rem);
  display: flex;
  align-items: center;
  justify-content: center;
}
html .cc2 .card { padding: 22px; }
html .cc2 .cc2-eyebrow, html .cc2 h2 { letter-spacing: .01em; }

/* ---- page-level desktop container (BOTH surfaces) --------------------
   Correction 2026-07-20: desktop must be a real application layout, not
   a centered mobile column. `html .main:has(...)` (0,2,1) + loaded-last
   out-ranks the app.html .main caps AND comfort.polish's
   `html.comfort-on .main:has(.cc2)` 900/960 rules. */
@media (min-width: 1120px) {
  /* html.comfort-on matches comfort.polish's (0,3,1) specificity for
     .main:has(.cc2) so the LAST-loaded sheet (this one) wins; the plain
     variants cover any comfort-off state. */
  html.comfort-on .main:has(.cc2),
  html.comfort-on .main:has(.uh-root),
  html .main:has(.cc2),
  html .main:has(.uh-root),
  /* WIDTH MUST NOT DEPEND ON WHETHER THE CONTENT HAS LOADED (2026-08-12).
     These :has() rules key the page's desktop width on the PRESENCE of
     loaded content. While the sub-view is loading, and during every
     teardown/rebuild, .cc2 is absent, the rule stops matching, and .main
     snaps from 1240px to the narrower cap -- measured as x 100->20 and
     width 1080->1240, i.e. the whole page lurching 80px SIDEWAYS, twice per
     boot. That horizontal jump is the 0.05651-per-shift, CLS 0.170 defect on
     the asset surface; it was never a vertical/height problem, which is why
     two height-based fixes did not move it.
     The loading state and the route itself now satisfy the same regime, so
     the width is a property of WHERE YOU ARE, not of what has arrived. */
  html .main:has(.cc2-loading),
  html .main--myasset {
    max-width: 1240px;
    padding-left: 40px;
    padding-right: 40px;
  }
}
@media (min-width: 1500px) {
  html.comfort-on .main:has(.cc2),
  html.comfort-on .main:has(.uh-root),
  html .main:has(.cc2),
  html .main:has(.uh-root),
  html .main:has(.cc2-loading),
  html .main--myasset {
    max-width: 1320px;
  }
}

/* Workspace desktop: intentional two-column composition. Children opt
   in via aw-half (share a row); everything else spans. Grid, not a
   stretched mobile column. */
@media (min-width: 1120px) {
  html .cc2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 24px;
    align-items: start;
    justify-items: stretch;
  }
  html .cc2 > * { grid-column: 1 / -1; min-width: 0; width: auto; }
  html .cc2 > style { display: none; }
  html .cc2 > .aw-half { grid-column: auto / span 1; }
  /* Row-1 balance pair (co-owner + customer/vendor): shared minimum
     height so the two cards keep one visual rhythm even when one side
     is an empty state. Only the balance cards carry card+aw-half. */
  html .cc2 > .card.aw-half { min-height: 240px; }
  html .cc2 .card { padding: 28px; }
  /* Typography steps up with the wider canvas (visual only). */
  html .cc2 .aw-oi-value { font-size: 28px; }
  html .cc2 .aw-metric-value { font-size: 24px; }
  html .cc2 .aw-hero-photo { height: 300px; }
  html .cc2 .aw-bal-amt { font-size: 17px; }
}
@media (min-width: 1440px) {
  html .cc2 .aw-hero-name { font-size: 40px; }
  html .cc2 .aw-hero-photo { height: 320px; }
}

/* ---- shared link affordance ----------------------------------------- */
.uh-root .cmp-link, .cc2 .cmp-link { font-weight: 650; }

/* ---- SHARED premium asset card (2026-07-21) --------------------------
   One canonical card for Home "My Assets" AND the org asset selector
   (balenHome.assetCardHtml renders the markup on both surfaces).
   Moved here from js/home.js's scoped styles so the classes resolve
   anywhere. Tokens only; light+dark via tokens; hover/press/focus come
   from the interaction primitives above (.uh-clickable). */
.uh-asset-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; }
@media (min-width: 1024px) {
  .uh-asset-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
  .uh-asset-grid--single { grid-template-columns: minmax(0, 1fr); }
}
.uh-asset-row {
  display: flex; align-items: center; gap: 16px; padding: 14px 16px;
  min-height: 104px; background: var(--surface); border: 1px solid var(--line);
  border-radius: 18px; box-shadow: var(--shadow-card); cursor: pointer;
}
@media (min-width: 1024px) { .uh-asset-row { min-height: 128px; padding: 18px 20px; gap: 18px; } }
.uh-thumb { width: 76px; height: 76px; border-radius: 16px; overflow: hidden; flex-shrink: 0; background: var(--surface-2); }
@media (min-width: 1024px) { .uh-thumb { width: 84px; height: 84px; } }
.uh-thumb-glyph {
  display: flex; align-items: center; justify-content: center; color: var(--accent);
  background: linear-gradient(135deg, var(--accent-tint) 0%, var(--surface-2) 82%);
}
.uh-thumb-glyph svg { width: 30px; height: 30px; }
.uh-live-chip {
  display: inline-block; vertical-align: 2px; margin-left: 8px; padding: 3px 9px;
  border-radius: 999px; background: var(--accent-tint); color: var(--accent);
  font-size: 11.5px; font-weight: 700; letter-spacing: .02em; white-space: nowrap;
}
.uh-asset-main { flex: 1; min-width: 0; }
.uh-asset-name { font-family: var(--font-sans); font-weight: 650; font-size: 18px; letter-spacing: -.01em; color: var(--text-primary); }
@media (min-width: 1024px) { .uh-asset-name { font-size: 19px; } }
.uh-asset-sub { font-family: var(--font-sans); font-size: 15px; color: var(--text-secondary); margin-top: 3px; }
.uh-asset-next { font-family: var(--font-sans); font-size: 14px; color: var(--text-tertiary); margin-top: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.uh-asset-next-quiet { color: var(--text-tertiary); }
.uh-asset-right { text-align: right; flex-shrink: 0; display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; align-self: stretch; padding: 2px 0; }
.uh-asset-right .uh-chev { margin-top: auto; }
.uh-asset-meter { display: flex; flex-direction: column; align-items: flex-end; }
.uh-asset-meter > div:first-child { font-size: 18px !important; font-weight: 650; }
@media (min-width: 1024px) { .uh-asset-meter > div:first-child { font-size: 20px !important; } }
.uh-chev { color: var(--text-tertiary); font-size: 18px; }
/* Exchange-access variant metadata (same card family). */
.uh-x-chip { background: var(--surface-2); color: var(--text-secondary); border: 1px solid var(--line); }
.uh-x-label {
  font-family: var(--font-sans); font-size: 12px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-tertiary);
  margin: 20px 2px 12px;
}
.uh-orgx:empty { display: none; }

/* ---- minimal workspace nav bar (2026-07-21) -------------------------- */
/* Apple-style: back · small title · commands. Page-colored (no teal
   presentation block) so the content hero below is the ONE introduction
   and visually grows out of the navigation. */
/* Skin-proof (2026-07-21): the Midnight/Air appearances re-gradient
   `.hero` at (0,2,1)+!important (css/balen.skin.app.css §5.5), which
   out-ranks a (0,1,1) override and painted the nav bar with the full
   gradient again -- title and back arrow lost contrast. The skin-scoped
   selectors below sit at (0,2,2) so the bar stays PAGE-COLORED chrome
   with token foregrounds (AA in every skin/theme) no matter the skin.
   Skins are themes: they may recolor tokens, never repaint chrome. */
html .hero--nav,
html.skin-midnight .hero--nav,
html.skin-air .hero--nav,
html.comfort-on .hero--nav {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 16px;
  padding-top: calc(10px + env(safe-area-inset-top, 0px));
  /* Kill the teal presentation surface completely: no gradient, no
     inset shadows -- the bar is page-colored chrome. !important is
     required: the app.html .hero base pins its gradient/color/shadow
     with !important (anti-banding block). */
  background: var(--page) !important;
  background-image: none !important;
  box-shadow: none !important;
  border-bottom: 0; min-height: 0;
  padding-bottom: 10px !important;
  color: var(--text-primary) !important;
}
html .hero--nav .hero-nav-title {
  font-family: var(--font-sans); font-size: 15px; font-weight: 650;
  letter-spacing: -.01em; color: var(--text-primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
/* Buttons on the page-colored bar: token surfaces, NOT the translucent
   white the gradient hero pins with !important (app.html:1980) -- white
   ghosts on a light page were invisible under the Air skin. */
html .hero--nav .hero-icon-btn,
html.skin-midnight .hero--nav .hero-icon-btn,
html.skin-air .hero--nav .hero-icon-btn {
  color: var(--text-primary) !important;
  border: 1px solid var(--line) !important;
  background: var(--surface) !important;
}
html .hero--nav .hero-icon-btn:hover,
html.skin-midnight .hero--nav .hero-icon-btn:hover,
html.skin-air .hero--nav .hero-icon-btn:hover {
  background: var(--surface-2) !important;
}
/* The workspace content starts tight under the bar (hero attaches). */
html .hero--nav + * .main, /* defensive: structure variants */
html .hero--nav ~ .main { padding-top: 6px; }

/* ---- header command bar (2026-07-21): one engineered control group --- */
.pm-cmdbar { display: flex; align-items: center; gap: 10px; }
.pm-cmdbar .hero-icon-btn,
.uh-cmdbar .uh-cmd {
  width: 40px; height: 40px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; position: relative; cursor: pointer;
  transition: background-color 140ms ease, transform 140ms ease;
}
.uh-cmdbar { display: flex; align-items: center; gap: 10px; }
.uh-cmdbar .uh-cmd {
  border: 1px solid var(--line); background: var(--surface); color: var(--text-primary);
}
@media (hover: hover) {
  .pm-cmdbar .hero-icon-btn:hover, .uh-cmdbar .uh-cmd:hover { background: var(--surface-2); }
}
.pm-cmdbar .hero-icon-btn:active, .uh-cmdbar .uh-cmd:active { transform: scale(.94); }
/* Honest badge: only rendered when a genuine actionable count exists. */
.pm-badge {
  position: absolute; top: -4px; right: -4px;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;
  background: var(--warn); color: var(--warn-on, var(--surface));
  font-family: var(--font-sans); font-size: 10.5px; font-weight: 700;
  line-height: 18px; text-align: center; letter-spacing: 0;
}

/* ---- entrance motion (Phase 2): one subtle rise, alive not flashy ----
   INC-2026-08-06b phase 5: "one subtle rise" was the intent, but this
   replayed on EVERY render. renderApp() assigns app.innerHTML, so the
   .uh-root / .cc2 nodes are recreated each time and the animation
   restarts from `from { opacity: 0 }` with fill-mode `both` -- the whole
   Home surface drops to invisible and fades back in. That is the single
   visible flash reported after the render-loop, permission and
   first-paint-theme fixes had all landed, and it affects every
   authenticated user and every role because .uh-root IS the Home root.

   It also evaded every screenshot-based instrument in this incident:
   Playwright's animations:'disabled' forces animations to their END
   state, so the blink was suppressed inside the very measurement meant
   to catch it.

   Fix: the entrance plays ONCE per app session. js/app.js stamps
   html.pm-entered once the first render's motion has finished; from then
   on these surfaces render at their final state with no fade. The
   intended first-load motion is preserved exactly. Reduced-motion users
   were never in scope and still are not. */
@media (prefers-reduced-motion: no-preference) {
  .uh-root, .cc2 {
    animation: pmRise 380ms cubic-bezier(.2, .6, .3, 1) both;
  }
  html.pm-entered .uh-root,
  html.pm-entered .cc2 {
    animation: none;
  }
}
@keyframes pmRise {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
