html, body {
    height: 100%;
    margin: 0;
    padding: 0;
    overflow: hidden;
    font-family: var(--eb-font-family, system-ui, -apple-system, "Segoe UI", sans-serif);
    background: var(--eb-surface);
    color: var(--eb-text);
}

#app {
    height: 100%;
}

.app-loading {
    display: grid;
    place-items: center;
    height: 100%;
    opacity: 0.6;
}

#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1000;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    background: #ffe082;
    color: #111;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
}

#blazor-error-ui .dismiss {
    position: absolute;
    top: 0.5rem;
    right: 0.75rem;
    cursor: pointer;
}

/* EbEdgeTab slides its drawer with a transform, and keeps will-change: transform on it. Both make
   the drawer the containing block for position:fixed descendants — which is what EbPopover uses to
   float the colour picker out of EbCssColorField. Inside the drawer that put the picker roughly a
   drawer-width off to the side, and clipped it against the panel's own scrolling.

   Once the drawer is open it is at rest at its docked position, so neither is needed: dropping
   both restores a real viewport-fixed popover. The closed -> open slide still animates, because a
   transform transitions to "none" as the identity matrix. This has to be global CSS: the classes
   belong to the library's own markup, which carries no scope attribute of ours. */
.eb-edge-tab-open .eb-edge-tab-drawer {
    /* !important because the rule being overridden is the package's own CSS-isolation rule, which
       carries a generated scope attribute and three state classes - no app-level selector can
       outrank it, and the scope attribute is not ours to write. */
    transform: none !important;
    will-change: auto !important;
}

/* The drawer panel is a column: the tab strip sizes to its content and the active panel takes
   everything left, so a section fills the drawer's height instead of stopping partway down.
   Global rather than scoped because SettingsDrawer renders no element of its own to hang a
   ::deep selector on — its root is the EbEdgeTab component. */
.eb-edge-tab-panel.settings-panel {
    display: flex;
    flex-direction: column;
    padding: 0;
    overflow: hidden;
}

.settings-panel > .eb-tabs-card {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.settings-panel .eb-tabs-panel {
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
}

/* ---- Dialog sizing ---------------------------------------------------------------------------
   EbCard makes its body wrapper a one-row grid so a collapsible card can animate open and shut.
   In a dialog that row squeezes the body and pushes the footer out of the card, which is why a
   confirm dialog came out a line and a half tall with its Cancel/Confirm buttons scrolled out of
   sight. A dialog is never collapsible, so its wrapper does not need to be that grid and can just
   size to its content; the card's own max-height still caps a long one and the body still scrolls
   inside it.

   Global rather than scoped because the dialog is the package's own markup, and !important because
   the rule being overridden carries a generated scope attribute that no app-level selector can
   reliably outrank. */
.eb-dialog-card > .eb-card-body-wrapper {
    display: block !important;
}

/* ---- Drag repaint ----------------------------------------------------------------------------
   The grid's drag preview and the tile being dragged both move by transitioned `transform`, and
   neither asks for a layer of its own. Without one the browser repaints the region as they travel
   and can under-invalidate it, leaving the preview's 2px border smeared across the board as
   vertical streaks. Promoting them is the usual fix. */
.eb-tile-cell-highlight,
.eb-tile-tile.dragging {
    will-change: transform;
}

/* ---- Selector preview -------------------------------------------------------------------------
   What board.js puts on everything a stylesheet rule matches while that rule is pointed at in the
   drawer. Global because it lands on the components' own markup, and !important because a rule the
   user is editing may well be setting an outline of its own. */
.css-preview {
    outline: 2px dashed var(--eb-accent) !important;
    outline-offset: 2px !important;
    animation: css-preview-pulse 900ms ease-in-out infinite;
}

@keyframes css-preview-pulse {
    50% { outline-color: var(--eb-warning, #d97706) !important; }
}

/* Held steady rather than pulsing, for anyone who has asked for less movement. */
@media (prefers-reduced-motion: reduce) {
    .css-preview { animation: none; }
}
