/* UI Skills Playbook — complete implementable set (47 lessons).
 * Source: http://localhost:4321/playbook
 * Linked from marketing, docs, and the main app. Appearance / a11y / motion
 * only — does not change product behaviour.
 *
 * Note: the ~400 “skills” on ui-skills are agent instruction docs (and React
 * library catalogues), not drop-in widgets. This file is every Playbook lesson
 * that can be applied to JMXpress’s existing HTML/CSS.
 */

:root{
  --pb-ease:cubic-bezier(.16,1,.3,1);
  --pb-exit:cubic-bezier(.4,0,1,1);
  --pb-press:.15s;
  --pb-fast:.2s;
  --pb-enter:.4s;
  --pb-exit-dur:.12s;
  --pb-scale:.96;
  --pb-tip-delay:0ms;
  --pb-touch:2.75rem;
  --pb-gap:.75rem; /* ~12px breathing room */
}

/* 1 reserve-space-with-aspect-ratio */
img,video,svg.shot,.frame img,.heroshot img,.docsbody img{
  height:auto;max-width:100%;
}
.frame img,.showcase .slide img,.featuremedia img{aspect-ratio:16/10;object-fit:cover}
.cardicon,.chipicon,.sidehelpicon{aspect-ratio:1;flex-shrink:0}

/* 2 use-text-balance */
h1,h2,h3,.heroline,.docstag,.pagehead h1,.cardtitle{text-wrap:balance}

/* 3 use-tabular-nums-for-data */
.mono,td.mono,th.mono,td.num,th.num,.tiprow b,.nowtime,.statnum,
.tablebar .count,.tabular,.plansummary .price,output{font-variant-numeric:tabular-nums}

/* 4 use-large-touch-targets */
.btn,.navtoggle,button.primary,button.ghost,a.btn,
.docssearch input,.docsnavwrap > summary,.totop,.hdrbtn,.hdrsearch{
  min-height:var(--pb-touch);
}
@media (max-width:62rem){
  .sitenav ul a{min-height:var(--pb-touch);display:flex;align-items:center}
}

/* 5 use-concentric-border-radius */
.card .cardicon,.topiccard .cardicon,.sheet input,.sheet select,.sheet textarea,
.metriccard .cardbtn,.formpanel .field input,.formpanel .field textarea{
  border-radius:max(4px, calc(var(--r-lg, 1rem) - .35rem));
}

/* 6 add-scale-on-press */
@media (prefers-reduced-motion:no-preference){
  .btn:active,button:not(:disabled):active,a.btn:active,
  .slidenav:active,.hdrbtn:active,.totop:active{
    transform:scale(var(--pb-scale));
  }
}

/* 7 anchor-popovers-to-triggers */
.cardmenulist,.railfly,.searchresults,.sitenav.open,.pickwrap .picklist,
.dropmenu,.menupop{transform-origin:var(--pb-origin, top center)}
.cardmenulist{--pb-origin:top right}
.railfly{--pb-origin:left center}
.searchresults{--pb-origin:top center}

/* 8 group-with-space-not-lines */
.tools,.sheetactions,.avatarbtns,.herocta,.navactions,.emptycta,
.tablebar,.pageheadrow,.ctapanel .herocta,.notfoundcta{gap:var(--pb-gap)}

/* 9 peek-the-next-scroll-item */
.scroll,.searchresults,.docsside,.docstoc,.pallist{
  scroll-padding-block:.75rem;
}

/* 10 outline-images-neutrally */
img:not([class*="logo"]):not(.brandlogo):not(.brandword):not(.brandmini),
.frame img,.docsbody img,.featuremedia img{
  border:1px solid var(--line, #DCE4F0);
}

/* 11 clamp-overflowing-titles */
.cardtitle,.actionlabel,.personnames b,.rtitle,.clamped,.plansummary{
  overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;
  -webkit-line-clamp:2;
}

/* 12 keep-secondary-text-readable */
.sub,.lede,.muted,.fieldnote,.emptynote,.small,.rsummary,footer .footbrand p{
  color:var(--muted, #53627C);
}
@media (min-width:40rem){
  .lede,.sub{color:color-mix(in srgb,var(--muted,#53627C) 88%,var(--ink,#0F172A))}
}

/* 13 avoid-entering-from-scale-zero — entrances never use scale(0) */
@keyframes pb-enter{
  from{opacity:0;translate:0 10px;transform:scale(.98)}
  to{opacity:1;translate:0 0;transform:none}
}
@keyframes pb-menu{
  from{opacity:0;transform:translateY(-4px) scale(.98)}
  to{opacity:1;transform:none}
}

/* 14 use-structural-skeletons */
@keyframes pb-skeleton{
  0%{background-position:100% 0}
  100%{background-position:-100% 0}
}
.skeleton,.skeleton-line{
  display:block;border-radius:var(--r-md,.625rem);color:transparent!important;
  background:linear-gradient(90deg,
    var(--sunken,#EDF2FA) 25%,
    var(--hover,#F5F8FD) 37%,
    var(--sunken,#EDF2FA) 63%);
  background-size:200% 100%;
}
.skeleton-line{height:.85rem;margin:.35rem 0}
@media (prefers-reduced-motion:no-preference){
  .skeleton,.skeleton-line{animation:pb-skeleton 1.25s var(--pb-ease) infinite}
}

/* 15 limit-accent-color-usage — secondary stays neutral */
button.ghost,a.btn.ghost,.btn.ghost{color:var(--ink,#0F172A)}
.card p,.note p{color:var(--muted,#53627C)}

/* 16 cap-line-length */
.lede,.sub,.docsbody p,.docsbody li,.midhead .lede,.emptywords span{max-width:68ch}
.docsbody{max-width:72rem}

/* 17 use-shadow-for-elevation / borders for structure */
.card,.formpanel,.sheet,.palette,.searchresults,.railfly{
  box-shadow:var(--shadow-md,0 4px 12px -2px rgba(12,27,51,.10));
  border:1px solid var(--line,#DCE4F0);
}

/* 18 show-visible-focus-rings */
:focus{outline:none}
:focus-visible{
  outline:2px solid var(--focus,var(--accent,#2563EB));
  outline-offset:2px;
}

/* 19 show-errors-beside-fields */
.fielderr,.fielderror{
  display:block;margin-top:.5rem;font-size:.88rem;
  color:var(--danger,#C0392B);font-weight:550;line-height:1.35;
}
.fielderr[hidden],.fielderror[hidden]{display:none}
[aria-invalid=true]{
  border-color:var(--danger,#C0392B)!important;
}

/* 20 label-every-form-field — labels stay visible (never sr-only by default) */
.field > label,.sheet label,label.fieldlabel{
  display:block;margin-bottom:.35rem;font-weight:600;
}
label .visually-hidden:only-child{/* keep intentional sr-only */}

/* 21 give-empty-states-one-action */
.empty,.searchnone,.bellempty{
  display:flex;flex-direction:column;align-items:center;gap:var(--pb-gap);
  text-align:center;
}
.emptycta{display:flex;flex-wrap:wrap;gap:var(--pb-gap);justify-content:center}

/* 22 use-sentence-case-labels */
.cardmenuhead,.sorthead,.palgroup,.detailsection,.footlinks h2{
  text-transform:none;letter-spacing:.02em;font-weight:650;
}
/* Intentional short marks may stay uppercase */
.eyebrow,.tag,.navsection,.rsection{text-transform:uppercase}

/* 23 pair-status-with-labels */
.state,.connstate,.monitorstate,.capturestate,.billstate,.recstatus,
.st-ok,.st-err{
  font-weight:600;
}

/* 24–26 motion easing + icon craft */
@media (prefers-reduced-motion:no-preference){
  .reveal,.formsaid:not([hidden]),
  main > .pagehead,.docsbody,.herotext > *{
    animation:pb-enter var(--pb-enter) var(--pb-ease) both;
  }
}
.btn svg,.cardicon svg,.hdricon,.btnicon,.subicon,.navtop svg,.topiccard svg{
  stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;
}
.btnicon,.hdricon,.btn svg{transform:translateY(.04em)}

/* 27 use-outline-icons-by-default — prefer currentColor stroke */
.btn svg,.cardicon svg,.hdricon,.btnicon{fill:none;stroke:currentColor}

/* 28 avoid-glow-primary-actions */
.btn.solid,button.primary,a.btn.primary{
  box-shadow:var(--shadow-sm,0 1px 2px rgba(12,27,51,.08));
  filter:none;
}
.btn.solid:hover,button.primary:hover,a.btn.primary:hover{
  box-shadow:var(--shadow-md,0 4px 12px -2px rgba(12,27,51,.12));
}

/* 29 animate-icon-state-changes */
button svg,a.btn svg,.hdrbtn svg,.cardbtn svg,.navchevron svg{
  transition:opacity var(--pb-fast) var(--pb-ease),
              transform var(--pb-fast) var(--pb-ease),
              color var(--pb-fast) ease;
}
button[aria-expanded=true] .navchevron svg{transform:rotate(90deg)}

/* 30 give-targets-breathing-room — see gap rules above */

/* 31 inset-primary-actions */
.sheet .sheetactions,.formpanel .btn,.ctapanel .btn,.section .shell > .btn{
  max-width:100%;
  margin-inline:0;
  box-sizing:border-box;
}

/* 32 tighten-heading-line-height */
h1{line-height:1.1}
h2{line-height:1.15}
h3{line-height:1.2}

/* 33 tune-tracking-by-size */
h1{letter-spacing:-.03em}
h2{letter-spacing:-.02em}
h3{letter-spacing:-.015em}
.heroline{letter-spacing:-.035em}

/* 34 confirm-destructive-actions — dialogs already; style danger clearly */
button.danger,.ghost.danger,.cardmenuitem.danger{
  color:var(--danger,#C0392B);
}

/* 35 fade-scroll-edges */
.scroll,.searchresults{
  -webkit-mask-image:linear-gradient(180deg,#000 0,#000 calc(100% - 1.1rem),transparent);
  mask-image:linear-gradient(180deg,#000 0,#000 calc(100% - 1.1rem),transparent);
}

/* 36 stagger-infrequent-entrances */
@media (prefers-reduced-motion:no-preference){
  .herotext > *:nth-child(1){animation-delay:40ms}
  .herotext > *:nth-child(2){animation-delay:120ms}
  .herotext > *:nth-child(3){animation-delay:200ms}
  .herotext > *:nth-child(4){animation-delay:280ms}
  .herotext > *:nth-child(5){animation-delay:360ms}
  main > .grid > .actiontile:nth-child(n),
  main > .grid > .tile:nth-child(n){animation:pb-enter .4s var(--pb-ease) both}
  main > .grid > .actiontile:nth-child(1),main > .grid > .tile:nth-child(1){animation-delay:40ms}
  main > .grid > .actiontile:nth-child(2),main > .grid > .tile:nth-child(2){animation-delay:100ms}
  main > .grid > .actiontile:nth-child(3),main > .grid > .tile:nth-child(3){animation-delay:160ms}
  main > .grid > .actiontile:nth-child(4),main > .grid > .tile:nth-child(4){animation-delay:220ms}
  main > .grid > .actiontile:nth-child(5),main > .grid > .tile:nth-child(5){animation-delay:280ms}
  main > .grid > .actiontile:nth-child(6),main > .grid > .tile:nth-child(6){animation-delay:340ms}
}

/* 37 keep-exits-subtle + 38 warm-toolbar-tooltips + 39 interruptible + 41 fade-menus-out + 45 same-path */
.cardmenulist{
  opacity:0;visibility:hidden;pointer-events:none;
  transform:translateY(-4px) scale(.98);
  transition:opacity var(--pb-exit-dur) var(--pb-exit),
              transform var(--pb-exit-dur) var(--pb-exit),
              visibility var(--pb-exit-dur);
}
.cardmenu.open .cardmenulist{
  opacity:1;visibility:visible;pointer-events:auto;transform:none;
  transition-timing-function:var(--pb-ease);
}
.charttip,.railfly.tiponly{
  transition:opacity .06s var(--pb-ease),transform .12s var(--pb-ease);
  transition-delay:var(--pb-tip-delay,0ms);
}
html[data-tip-warm]{--pb-tip-delay:0ms}
html:not([data-tip-warm]) .charttip{transition-delay:120ms}

/* 40 restrain-high-frequency-motion — no infinite invite pulses in CSS here;
   site-specific loops are slowed in marketing/docs sheets. */

/* 42 reserve-brand-color-for-links
   Skip chrome that paints its own selected state. .reptool was matching this
   rule with higher specificity than .reptool.on, so the active Reports tab
   kept accent-coloured text on an accent fill and the label vanished. */
a:not(.btn):not(.navtop):not(.navsub a):not(.reptool):not([class*="ghost"]){
  color:var(--accent,#2563EB);
}
.section.navy a:not(.btn){color:#fff}

/* 43 use-solid-modal-scrims */
.sheet::backdrop,.palette::backdrop{
  background:color-mix(in srgb,var(--deep,#0B1730) 50%,transparent);
  backdrop-filter:none;-webkit-backdrop-filter:none;
}

/* 44 fade-truncated-text */
td.path,.personnames b,.personnames small,.barlabel span,.treepick b,
.treepick small,.framebar span,.soft-truncate{
  -webkit-mask-image:linear-gradient(90deg,#000 70%,transparent);
  mask-image:linear-gradient(90deg,#000 70%,transparent);
  text-overflow:clip;overflow:hidden;white-space:nowrap;
}

/* 46 blur-imperfect-label-morphs */
.recstatus,.agentbar .agentmsg,.nowtime,.rtitle,.rsummary{
  transition:opacity .14s var(--pb-ease),filter .14s var(--pb-ease);
}

@media (prefers-reduced-motion:reduce){
  .skeleton,.skeleton-line,
  .reveal,.formsaid,
  main > .pagehead,.docsbody,.herotext > *,
  main > .grid > .actiontile,main > .grid > .tile{animation:none!important}
  .cardmenulist{transition:none}
  .cardmenu:not(.open) .cardmenulist{display:none}
  .scroll,.searchresults{mask-image:none;-webkit-mask-image:none}
  td.path,.personnames b,.personnames small,.barlabel span,.treepick b,
  .treepick small,.framebar span,.soft-truncate{
    mask-image:none;-webkit-mask-image:none;text-overflow:ellipsis}
  .btn:active,button:active,a.btn:active{transform:none}
}
