/* px_ui / slide_panel_html — the form's stylesheet, an asset because the
   server strips a template's style text.
   Head-linked from anvil.yaml native_deps.head_html, so it also styles the
   designer canvas.

   THE PRIVATE SCOPE CLASS, and why it is not optional. As a head link these
   rules load on EVERY page of all four consuming apps, and
   px-management-app declares the SAME class names in its own theme.css for its
   own hand-rolled panels (owings_html, portal_engagement_html). Unscoped, this
   dependency would truncate management's panel titles, gap its
   headers and outlining its buttons. The form root therefore carries a second
   class, px-ui-slide-panel, and every rule below is scoped to it.

   The scope has TWO arms, because the overlay IS the form root — a
   selector naming it can never be given the root as an
   ANCESTOR. A root-subject selector takes the scope class in the SAME compound
   (.px-slide-panel-overlay.px-ui-slide-panel); every other selector takes it as
   a descendant prefix. Written the other way round, `.px-ui-slide-panel
   .px-slide-panel-overlay` matches nothing at all — the overlay is not a
   descendant of itself. There are THREE root-subject selectors here and the
   third is inside the reduced-motion block.

   :where() is deliberately NOT used. This file holds one form's rules, so a
   uniform +1 class changes no outcome inside it, and the added weight is
   exactly what beats a host sheet declaring the same class names at (0,1,0),
   since a head link does not win on document order.

   THE DESIGNER GATE (guide step 4). The closed state below hides three of the
   four families the guide names — visibility, opacity and an entrance
   transform — and the designer runs no Python and no ES module, so an ungated
   closed state opens this form on the canvas as a blank page and staff cannot
   edit what they cannot see. Every hiding declaration therefore sits in its own
   rule one class heavier, gated on px-ui-slide-panel-live, which initPanel adds
   to this form's own root. At runtime each reveal rule (.active, .open)
   matches the gated hide's specificity and wins on document order. The
   class cannot reach a host screen — it is
   written to this form's own root and dies with it — and the leak direction is
   the safe one, because the live class carries the HIDING declarations: a
   missing class paints the OPEN state on the canvas, never an open overlay over
   somebody else's page. */

/* =====================================================================
   px_ui / slide_panel_html — the shared slide-panel / side-sheet chrome
   (6C-48). The MOTION and STATE of a panel already live in the shared
   toolkit (px_theme/_px_shared_js.py: pxShowSlidePanel,
   pxHideSlidePanel, pxCancelSlidePanelCallback). This Form adds only
   what the toolkit deliberately does not own: the markup, the ARIA
   contract, the keyboard and history wiring, and the CSS.

   COLOUR POLICY (3C-03b / OD-02 / OD-26, hard):
     * ZERO colour literals live in this file. Every surface, text,
       border and shadow value is a --px-* token.
     * NO var() fallbacks — an unresolved token must fail visibly
       rather than silently paint a stand-in.

   SCOPING: every rule below is prefixed .px-slide-panel-overlay or
   .px-slide-panel, so mounting this Form paints nothing else on the
   host page. Those two class names are NOT free choices — the shared
   toolkit queries overlay.querySelector('.px-slide-panel') and toggles
   .active / .open on exactly these elements.

   TIMING: the open/close transition uses --px-transition-slow (350ms),
   which is the same 350ms the toolkit waits before it tears the panel
   down and fires the stored onClose. The duration is read from the
   token, never re-typed here.
   ===================================================================== */

.px-slide-panel-overlay.px-ui-slide-panel {
  position: fixed;
  inset: 0;
  z-index: var(--px-z-overlay);
  background: var(--px-backdrop);
  transition: opacity var(--px-transition-slow),
              visibility var(--px-transition-slow);
}

/* The CLOSED state, gated for the designer. (0,3,0), which .active ties and
   then wins on document order. */
.px-slide-panel-overlay.px-ui-slide-panel.px-ui-slide-panel-live {
  visibility: hidden;
  opacity: 0;
}

.px-slide-panel-overlay.px-ui-slide-panel.active {
  visibility: visible;
  opacity: 1;
}

.px-ui-slide-panel .px-slide-panel {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 680px;
  max-width: 100vw;
  display: flex;
  flex-direction: column;
  font-family: var(--px-font);
  color: var(--px-text-primary);
  background: var(--px-bg-card);
  box-shadow: var(--px-shadow-lg);
  transition: transform var(--px-transition-slow);
  z-index: calc(var(--px-z-overlay) + 1);
}

/* The CLOSED state, gated. This and the .open reveal below are both (0,4,0),
   so .open wins on document order. The reveal is written from the root
   compound rather than as a plain descendant to reach that tie: at (0,3,0)
   it would lose to the gate. */
.px-slide-panel-overlay.px-ui-slide-panel.px-ui-slide-panel-live .px-slide-panel {
  transform: translateX(100%);
}

.px-slide-panel-overlay.px-ui-slide-panel .px-slide-panel.open {
  transform: translateX(0);
}

.px-ui-slide-panel .px-slide-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--px-space-md);
  padding: var(--px-space-md) var(--px-space-lg);
  border-bottom: 1px solid var(--px-border);
  flex-shrink: 0;
}

.px-ui-slide-panel .px-slide-panel-title {
  font-family: var(--px-font);
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--px-text-primary);
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.px-ui-slide-panel .px-slide-panel-close {
  flex-shrink: 0;
  background: none;
  border: none;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--px-text-muted);
  cursor: pointer;
  padding: var(--px-space-xs) var(--px-space-sm);
  border-radius: var(--px-radius-sm);
  transition: background-color var(--px-transition-fast),
              color var(--px-transition-fast);
}

.px-ui-slide-panel .px-slide-panel-close:hover {
  background: var(--px-bg-elevated);
  color: var(--px-text-primary);
}

.px-ui-slide-panel .px-slide-panel-close:focus-visible,
.px-ui-slide-panel .px-slide-panel :focus-visible {
  outline: 2px solid var(--px-accent);
  outline-offset: 2px;
}

.px-ui-slide-panel .px-slide-panel-subheader {
  flex-shrink: 0;
  border-bottom: 1px solid var(--px-border-subtle);
}

/* The sub-header collapses when the host slots nothing into it. Before
   conversion the div was literally :empty; it now holds the slot plumbing the
   runtime renders, so :empty alone would never match again and every panel
   would carry a stray 1px divider under the header.

   The test is deliberately NOT "is there a dropzone with a child". The
   plumbing's tag name and nesting are not verifiable from any source in this
   estate and the estate's HTML forms
   read them two different ways, so a rule that names one shape swallows a
   host's slotted sub-header with no error whenever the other shape is what
   ships. This rule asks the question that survives every reading: is there
   anything in here that is not slot plumbing. A slotted form's root matches
   neither name at any nesting, so a filled sub-header shows; an empty div, a
   bare dropzone and a dropzone inside a slot all collapse; and a third wrapper
   tag nobody predicted fails the VISIBLE way, as the stray divider this rule
   removes, never as missing host content. No comma inside :not(), because the
   scope test splits selector lists on commas. */
.px-ui-slide-panel .px-slide-panel-subheader:not(:has(:not(anvil-slot):not(anvil-dropzone))) {
  display: none;
}

.px-ui-slide-panel .px-slide-panel-body {
  flex: 1;
  overflow-y: auto;
  padding: var(--px-space-lg);
}

@media (max-width: 768px) {
  .px-ui-slide-panel .px-slide-panel {
    width: 100vw;
  }
}

/* A panel that slides is decoration; the panel arriving is the point. */
@media (prefers-reduced-motion: reduce) {
  .px-slide-panel-overlay.px-ui-slide-panel,
  .px-ui-slide-panel .px-slide-panel {
    transition: none;
  }
}
