@charset "UTF-8";

/*
   Design tokens live in tokens.css (loaded before this file).
   This stylesheet consumes them; it defines no tokens of its own.
*/

/* =========================================
   FONT FACES - Open Sans variable (wght 300-800)
   ========================================= */
@font-face {
    font-family: 'Open Sans';
    src: url("./open-sans/open-sans-variable.woff2") format('woff2-variations');
    font-weight: 300 800;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Open Sans';
    src: url("./open-sans/open-sans-italic-variable.woff2") format('woff2-variations');
    font-weight: 300 800;
    font-style: italic;
    font-display: swap;
}

/* Legacy font-family alias for backwards compatibility */
@font-face {
    font-family: 'System';
    src: url("./open-sans/open-sans-variable.woff2") format('woff2-variations');
    font-weight: 300 800;
    font-display: swap;
}

/* =========================================
   ANIMATIONS
   Server-patch constraint: an update re-inserts any node it moves, and a
   re-inserted node restarts its CSS animations. J2W.nodeChildrenApply keeps
   that to the nodes that genuinely moved, but a dialog is still re-opened
   whenever it loses the top layer, so an entry animation on one replays at
   arbitrary moments - the reason dialogs carry none. Transitions are the
   default idiom; keyframe ENTRY animations belong on short-lived overlay DOM
   that is discarded rather than patched (menu dropdown), or behind the
   .j2w-animate opt-in. Looping animations (spinner, indeterminate progress)
   are safe anywhere.

   Exit is the harder direction: a node hidden with setVisible(false) is left
   out of the payload, so it leaves the DOM before it could animate. The way
   out is Session.refreshAnimated - mark the node with .j2w-closing, publish
   that state, hold for the animation window, then drop it. Every -out
   keyframe below exists to be paired with that class.
   ========================================= */
@keyframes j2w-fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes j2w-fade-out {
    from { opacity: 1; }
    to { opacity: 0; }
}

@keyframes j2w-pop-in {
    from {
        opacity: 0;
        transform: scale(0.97) translateY(-4px);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

/* Entry for a panel that replaces another in place: a fade alone is too easy to miss. */
@keyframes j2w-enter {
    from {
        opacity: 0;
        transform: translateY(6px);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes j2w-pop-out {
    from {
        opacity: 1;
        transform: none;
    }
    to {
        opacity: 0;
        transform: scale(0.97) translateY(-4px);
    }
}

@keyframes j2w-scale-in {
    from {
        opacity: 0;
        transform: scale(0.96) translateY(8px);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes j2w-spin {
    to { transform: rotate(360deg); }
}

@keyframes j2w-indeterminate {
    from { translate: -110% 0; }
    to { translate: 360% 0; }
}

/* Legacy keyframe names kept for downstream stylesheets */
@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes fadeOut {
    from { opacity: 1; }
    to { opacity: 0; }
}

/* =========================================
   BASE STYLES
   ========================================= */
*,
*::before,
*::after {
    box-sizing: border-box;
}

:root {
    scrollbar-width: thin;
    scrollbar-color: var(--j2w-border-strong) transparent;
}

html {
    display: block;
    height: 100%;
    width: 100%;
}

/* Single body rule for both libraries (fx2web.css defines no body rule):
   a clipped stretch grid so the page panel fills the viewport. */
body {
    height: 100%;
    width: 100%;
    margin: 0;
    display: grid;
    justify-content: stretch;
    align-content: stretch;
    grid-template-rows: minmax(0, 1fr);
    grid-template-columns: minmax(0, 1fr);
    overflow: hidden;
    background: var(--j2w-bg);
    color: var(--j2w-text);
    font: 400 var(--j2w-text-md) / var(--j2w-leading) var(--j2w-font-sans);
    /* Inherited framework-wide: the DOM is generated from Java text nodes
       that rely on preserved whitespace. */
    white-space: pre-wrap;
}

button,
input,
select,
textarea {
    font: inherit;
    color: inherit;
}

pre {
    margin: unset;
    font-family: unset;
}

/* Keyboard-driven focus: a real ring. Mouse focus stays quiet.
   The ring is drawn inward: the layout containers clip with overflow: hidden,
   so an outward ring loses the sides that touch a container edge. */
:focus-visible {
    outline: 2px solid var(--j2w-focus-ring);
    outline-offset: -2px;
}

::selection {
    background: var(--j2w-selection);
}

/* =========================================
   MODAL & DIALOG SYSTEM
   ========================================= */
modals {
    position: fixed;
    top: 0;
    left: 0;
    width: 0;
    height: 0;
}

/* Closed dialogs render nothing. A dialog is dropped from the tree in the same
   update that closes it, so there is no intermediate state to keep visible. */
dialog:not([open]) {
    display: none;
}

dialog {
    display: grid;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
    margin: 0;
    border: none;
    background: var(--j2w-backdrop);
    backdrop-filter: var(--j2w-backdrop-blur);
    -webkit-backdrop-filter: var(--j2w-backdrop-blur);
}

.j2w-animate {
    animation: j2w-fade-in var(--j2w-duration-overlay) var(--j2w-ease-out);
}

dialog > div {
    display: grid;
    overflow: hidden;
    border-radius: var(--j2w-radius-xl);
    border: 1px solid var(--j2w-border);
    background: var(--j2w-surface-overlay);
    box-shadow: var(--j2w-shadow-5);
}

/* The preferred size a caller set with Modal.setPrefSize. It is read from custom
   properties on the dialog rather than written on the card, because a drag
   records the user's size in the card's own style attribute and the server
   leaves an attribute alone until its value changes - so the dragged size
   survives every refresh. With no preferred size the variable is unset, the
   declaration is invalid at computed-value time and the card keeps sizing to
   its content: every dialog that never asks for a size renders as before. */
dialog > div {
    width: min(var(--j2w-dialog-pref-width), var(--j2w-dialog-max-width));
    height: min(var(--j2w-dialog-pref-height), var(--j2w-dialog-max-height));
}

/* Dialog.init builds the three frame sections as Panels, so each arrives with
   the .j2w-panel vertical margin. Inside the card that margin is not spacing
   between siblings but a strip of card background above the header and below
   the buttons, so the tinted header no longer meets the top edge. Only the
   frame sections are reset; panels an application puts in the body keep their
   own rhythm. */
dialog > div > grid > .j2w-panel {
    margin: 0;
}

/* The body is the frame, not a pane inside it: whatever a caller hands to Dialog/Window
   should meet the card's edges, so a content node that fills the dialog (an editor, a
   split, a table) is not framed by a strip of dialog background it cannot use. The
   .j2w-panel padding this cancels is meant for panels stacked in a page, not for the one
   section that IS the window. Content that wants breathing room asks for it - add
   .j2w-padding to the node handed in, as a short text or a small form should. */
dialog [body] {
    padding: 0;
}

dialog [header] {
    display: grid;
    background: var(--j2w-surface-sunken);
    border-bottom: 1px solid var(--j2w-border);
    padding: var(--j2w-space-2) var(--j2w-space-4);
    font-weight: 600;
}

dialog [buttons] {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: max-content;
    gap: var(--j2w-space-2);
    justify-content: end;
    align-items: center;
    border-top: 1px solid var(--j2w-border);
    padding: var(--j2w-space-2) var(--j2w-space-3);
}

/* Modal.setResizable. The ceiling sits on the card, not on the grid track it is
   centred in: a card with a definite width sets its track's minimum, so the
   fit-content(90%) track alone could not stop a drag past the viewport. */
dialog.j2w-dialog-resizable > div {
    resize: both;
    min-width: min(var(--j2w-dialog-min-width), var(--j2w-dialog-max-width));
    min-height: min(var(--j2w-dialog-min-height), var(--j2w-dialog-max-height));
    max-width: var(--j2w-dialog-max-width);
    max-height: var(--j2w-dialog-max-height);
}

/* A resized card hands its size down to the content, as a JavaFX window does
   to its scene, and content that no longer fits scrolls rather than being cut
   off by the card's rounded clip. */
dialog.j2w-dialog-resizable > div > grid > [body] {
    display: grid;
    grid-template-rows: minmax(0, 1fr);
    grid-template-columns: minmax(0, 1fr);
    overflow: auto;
}

/* The content the body stretches is held to the body's size, so what it does not
   fit overflows the content itself rather than the body - and the layout boxes a
   dialog is usually handed (a vbox, an FX2Web pane) clip their overflow. Without
   this a list of choices taller than its dialog was cut off with no way to reach
   the rest. */
dialog.j2w-dialog-resizable > div > grid > [body] > * {
    overflow: auto;
}

/* Keeps the last button clear of the resize grip in the card's corner. */
dialog.j2w-dialog-resizable > div > grid > [buttons] {
    padding-inline-end: var(--j2w-space-6);
}

/* =========================================
   LAYOUT COMPONENTS
   ========================================= */
/* overflow: hidden keeps minmax(0, 1fr) grid tracks from blowing out
   (previously applied by the universal reset). */
grid {
    display: grid;
    overflow: hidden;
}

/* Declared by basic/ScrollPane.java; previously styled only when
   fx2web.css was loaded. */
scrollpane {
    display: grid;
    justify-content: stretch;
    align-content: stretch;
    grid-template-rows: minmax(0, 1fr);
    grid-template-columns: minmax(0, 1fr);
    overflow: hidden;
}

scrollpane>content {
    display: block;
    overflow-x: auto;
    overflow-y: auto;
}

line {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: max-content;
    grid-auto-rows: 1fr;
    align-content: start;
    justify-content: start;
    align-items: center;
    justify-items: start;
    overflow: hidden;
}

bar {
    display: grid;
    grid-auto-flow: row;
    grid-auto-columns: 1fr;
    grid-auto-rows: max-content;
    align-content: start;
    justify-content: start;
    align-items: start;
    justify-items: start;
    overflow: hidden;
}

hbox {
    display: grid;
    grid-auto-rows: auto;
    grid-auto-columns: auto;
    align-content: stretch;
    justify-content: start;
    grid-auto-flow: column;
    overflow: hidden;
}

vbox {
    display: grid;
    grid-auto-rows: auto;
    grid-auto-columns: auto;
    align-content: start;
    justify-content: stretch;
    grid-auto-flow: row;
    overflow: hidden;
}

/* =========================================
   INTERACTIVE ELEMENTS
   ========================================= */
button {
    display: grid;
    grid-auto-flow: column;
    justify-content: center;
    align-content: center;
    justify-items: center;
    align-items: center;
    grid-auto-columns: max-content;
    grid-auto-rows: max-content;
    overflow: hidden;
    min-height: var(--j2w-control-height);
    line-height: var(--j2w-leading-control);
    padding: var(--j2w-space-1) var(--j2w-space-3);
    gap: var(--j2w-space-1);
    border: 1px solid color-mix(in srgb, var(--j2w-accent) 25%, transparent);
    border-radius: var(--j2w-radius-md);
    background-color: var(--j2w-accent-subtle);
    color: var(--j2w-accent);
    font-weight: 500;
    cursor: pointer;
    transition:
        background-color var(--j2w-duration-base) var(--j2w-ease),
        border-color var(--j2w-duration-base) var(--j2w-ease),
        box-shadow var(--j2w-duration-base) var(--j2w-ease),
        transform var(--j2w-duration-fast) var(--j2w-ease);
}

button:hover {
    background-color: color-mix(in srgb, var(--j2w-accent) 18%, transparent);
}

button:active {
    background-color: color-mix(in srgb, var(--j2w-accent) 24%, transparent);
    transform: scale(0.98);
}

button[disable],
button[disable]:hover,
button:disabled {
    opacity: 0.5;
    cursor: default;
    pointer-events: none;
}

/* Solid accent variant for the main action of a view */
button.j2w-primary {
    background-color: var(--j2w-accent);
    border-color: var(--j2w-accent);
    color: var(--j2w-text-on-accent);
    box-shadow: var(--j2w-shadow-1);
}

button.j2w-primary:hover {
    background-color: var(--j2w-accent-hover);
    border-color: var(--j2w-accent-hover);
}

button.j2w-primary:active {
    background-color: var(--j2w-accent-active);
    border-color: var(--j2w-accent-active);
}

/* Low-emphasis variant for secondary actions */
button.j2w-quiet {
    background-color: transparent;
    border-color: transparent;
    color: var(--j2w-text-secondary);
    font-weight: 400;
}

button.j2w-quiet:hover {
    background-color: var(--j2w-hover-tint);
    color: var(--j2w-text);
}

button.j2w-quiet:active {
    background-color: var(--j2w-active-tint);
}

.j2w-clickable,
.j2w-hover {
    transition: background-color var(--j2w-duration-base) var(--j2w-ease);
}

.j2w-clickable:hover,
.j2w-hover:hover {
    background-color: var(--j2w-hover-tint);
    cursor: pointer;
}

/* =========================================
   FORM ELEMENTS
   ========================================= */
textarea {
    resize: none;
    overflow-y: auto;
}

img {
    object-fit: scale-down;
}

input,
select,
textarea {
    cursor: text;
}

/* A select animates as far as a select can: the field's border, glow and fill
   transition here, but the option list it opens is browser chrome rather than
   DOM, so no rule reaches it. Animating that list would mean replacing the
   native control with a scripted listbox - and in a round-trip framework the
   latency of opening one would cost more than the motion buys. */
input,
select,
textarea,
.j2w-input {
    background-color: var(--j2w-surface);
    margin: 0;
    padding: var(--j2w-space-1) var(--j2w-space-2);
    border: 1px solid var(--j2w-border);
    border-radius: var(--j2w-radius-md);
    transition:
        border-color var(--j2w-duration-base) var(--j2w-ease),
        box-shadow var(--j2w-duration-base) var(--j2w-ease),
        background-color var(--j2w-duration-base) var(--j2w-ease);
}

/* Field-shaped inputs join the shared control height so they line up with a
   button beside them - .j2w-filter drops to text-sm and would otherwise sit a
   pixel or two short. The widget-shaped types size themselves and opt out, and
   so does textarea, which is sized by its rows. */
input:not([type="checkbox"], [type="radio"], [type="range"], [type="color"]),
select,
.j2w-input {
    min-height: var(--j2w-control-height);
    line-height: var(--j2w-leading-control);
}

input::placeholder,
textarea::placeholder {
    color: var(--j2w-text-muted);
}

input:hover,
select:hover,
textarea:hover,
.j2w-input:hover {
    border-color: var(--j2w-border-strong);
}

input:focus,
select:focus,
textarea:focus,
.j2w-input:focus {
    border-color: var(--j2w-accent);
}

/* Field-shaped controls draw the glow into their own padding so it survives
   the container clip. The widget-shaped types have no padding to draw into
   and keep the outward ring below. */
input:not([type="checkbox"], [type="radio"], [type="range"], [type="color"]):focus,
select:focus,
textarea:focus,
.j2w-input:focus {
    outline: none;
    box-shadow: var(--j2w-focus-glow);
}

select,
input[type="button"],
input[type="checkbox"],
input[type="color"],
input[type="radio"] {
    cursor: pointer;
}

input[type="number"] {
    text-align: right;
}

input[type="date"]::-webkit-calendar-picker-indicator {
    opacity: 0.6;
    cursor: pointer;
}

input[type="date"]::-webkit-calendar-picker-indicator:hover {
    opacity: 1;
}

.j2w-input[readonly],
input[readonly] {
    background-color: var(--j2w-surface-sunken);
}

/* =========================================
   CHECKBOX & RADIO - native controls on the accent color
   ========================================= */
input[type="checkbox"],
input[type="radio"] {
    accent-color: var(--j2w-accent);
    width: 15px;
    height: 15px;
    padding: 0;
    border: none;
    outline-offset: 2px;
}

/* Too small to hold an inset ring - these keep the outward glow, and the
   surrounding cell is taller than the 15px box, so it has room to show. */
input[type="checkbox"]:focus,
input[type="radio"]:focus,
input[type="range"]:focus,
input[type="color"]:focus {
    outline: 3px solid color-mix(in srgb, var(--j2w-accent) 20%, transparent);
}

input[type="range"]:focus,
input[type="color"]:focus {
    outline-offset: 0;
}

/* =========================================
   SLIDER - input[type=range]
   Rendered by fx2web nodes/Slider.java as a bare range input.

   A capsule track, filled up to the thumb, with a thumb noticeably larger than
   the bar.

   The track is painted on the engines' own track pseudo-elements rather than on
   the input's background. Both would show through, but only this one is the
   actual track, and it is what keeps the bar the same thickness whatever box the
   slider is drawn in.

   '--j2w-slider-bar' and '--j2w-slider-thumb' are the sizes a caller that draws
   its own slider writes onto the element - one dragged out large should be a
   large slider, not a large box with the same hairline in it. The values here
   are what a slider that says nothing falls back to. The thumb has to stand off
   the bar to be grabbable, so the two are declared together: the thumb's
   overhang is read back from both.

   '--j2w-slider-fill' is how far along the track the thumb is. A caller that
   holds the value server-side writes it on and the filled part of the track
   stops there with no client script in it; one that does not leaves the track
   empty under WebKit and lets Firefox paint its own progress. Custom properties
   inherit into pseudo-elements, which is what lets the rules below read all
   three.

   There is no second set of rules for a vertical slider: it is this control
   rotated a quarter turn by whoever lays it out, so there is only one to style.
   ========================================= */
input[type="range"] {
    --j2w-slider-bar: 8px;
    --j2w-slider-thumb: 18px;
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    border: none;
    padding: 0;
    cursor: pointer;
}

/* Written out per engine: a vendor pseudo-element one browser does not know
   takes the whole rule down with it, so they cannot share a selector list.

   The capsule's outline is painted inside the track rather than declared as a
   border. WebKit lays the thumb out from the track's content box, so a border
   shrinks that box and drops the thumb half a border off the bar's centre - and
   on a slider turned a quarter turn that half pixel is sideways, which reads as
   the bar's outline missing down the side the thumb has moved onto. An inset
   shadow paints the same hairline in the same place without taking any room
   from the box the thumb is placed in. */
input[type="range"]::-webkit-slider-runnable-track {
    height: var(--j2w-slider-bar);
    border-radius: var(--j2w-radius-full);
    box-shadow: inset 0 0 0 1px var(--j2w-border);
    background-image: linear-gradient(
        to right,
        var(--j2w-accent) 0 var(--j2w-slider-fill, 0%),
        var(--j2w-surface-sunken) var(--j2w-slider-fill, 0%) 100%);
}

input[type="range"]::-moz-range-track {
    height: var(--j2w-slider-bar);
    border-radius: var(--j2w-radius-full);
    box-shadow: inset 0 0 0 1px var(--j2w-border);
    background-image: linear-gradient(
        to right,
        var(--j2w-accent) 0 var(--j2w-slider-fill, 0%),
        var(--j2w-surface-sunken) var(--j2w-slider-fill, 0%) 100%);
}

/* Firefox fills the track up to the thumb on its own, so a slider whose value
   nobody writes on still reads as filled there. */
input[type="range"]::-moz-range-progress {
    height: var(--j2w-slider-bar);
    border-radius: var(--j2w-radius-full);
    background: var(--j2w-accent);
}

/*
 * WebKit lays the thumb on the top edge of the track rather than across it, so
 * half its overhang is taken back as a negative margin. Firefox centres it for
 * us, and giving it the same margin would push it off.
 */
input[type="range"]::-webkit-slider-thumb {
    appearance: none;
    -webkit-appearance: none;
    box-sizing: border-box;
    width: var(--j2w-slider-thumb);
    height: var(--j2w-slider-thumb);
    margin-top: calc((var(--j2w-slider-bar) - var(--j2w-slider-thumb)) / 2);
    border: 2px solid var(--j2w-surface-raised);
    border-radius: var(--j2w-radius-full);
    background: var(--j2w-accent);
    box-shadow: var(--j2w-shadow-1);
    transition: transform var(--j2w-duration-fast) var(--j2w-ease);
}

input[type="range"]::-moz-range-thumb {
    box-sizing: border-box;
    width: var(--j2w-slider-thumb);
    height: var(--j2w-slider-thumb);
    border: 2px solid var(--j2w-surface-raised);
    border-radius: var(--j2w-radius-full);
    background: var(--j2w-accent);
    box-shadow: var(--j2w-shadow-1);
    transition: transform var(--j2w-duration-fast) var(--j2w-ease);
}

/* Hovered on the control, not on the thumb: the thumb is what grows, but a
   pointer on its way to a bar this thin should not have to land on it first. */
input[type="range"]:hover::-webkit-slider-thumb {
    transform: scale(1.1);
}

input[type="range"]:hover::-moz-range-thumb {
    transform: scale(1.1);
}

/* The blanket rule at the end of this file is written against '*', which no
   vendor pseudo-element matches, so these two say it again for themselves. */
@media (prefers-reduced-motion: reduce) {
    input[type="range"]::-webkit-slider-thumb {
        transition: none;
    }

    input[type="range"]::-moz-range-thumb {
        transition: none;
    }
}

input[type="range"]:disabled {
    opacity: 0.5;
    cursor: default;
}

/* Discrete controls only ring for keyboard focus; the soft glow above is
   for text-editing contexts. */
input[type="checkbox"]:focus:not(:focus-visible),
input[type="radio"]:focus:not(:focus-visible),
input[type="range"]:focus:not(:focus-visible),
input[type="color"]:focus:not(:focus-visible) {
    outline: none;
}

/* =========================================
   SVG ELEMENTS
   ========================================= */
svg text {
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    pointer-events: none;
}

/* =========================================
   UTILITY CLASSES - Colors
   ========================================= */
.j2w-darker,
.j2w-toolbar {
    background-color: var(--j2w-surface-sunken);
}

.j2w-lighter {
    background-color: color-mix(in srgb, var(--j2w-surface) 20%, transparent);
}

.j2w-white {
    background: var(--j2w-surface);
}

/* =========================================
   UTILITY CLASSES - Layout
   ========================================= */
.j2w-fill {
    justify-self: stretch;
    align-self: stretch;
}

.j2w-hfill {
    justify-self: stretch;
}

.j2w-vfill {
    align-self: stretch;
}

.j2w-nofill {
    width: fit-content;
    height: fit-content;
}

.j2w-vnofill {
    height: fit-content;
}

.j2w-hnofill {
    width: fit-content;
}

/* =========================================
   UTILITY CLASSES - Scroll
   ========================================= */
.j2w-vscroll {
    overflow-y: auto;
}

.j2w-hscroll {
    overflow-x: auto;
}

.j2w-scroll {
    overflow: auto;
}

/* =========================================
   UTILITY CLASSES - Spacing
   ========================================= */
.j2w-hpadding {
    padding-left: var(--j2w-space-1);
    padding-right: var(--j2w-space-1);
}

.j2w-padding {
    padding: var(--j2w-space-1);
}

.j2w-vpadding {
    padding-top: var(--j2w-space-1);
    padding-bottom: var(--j2w-space-1);
}

.j2w-hmargin {
    margin-left: var(--j2w-space-1);
    margin-right: var(--j2w-space-1);
}

.j2w-margin {
    margin: var(--j2w-space-1);
}

.j2w-vmargin {
    margin-top: var(--j2w-space-1);
    margin-bottom: var(--j2w-space-1);
}

/* =========================================
   UTILITY CLASSES - Borders
   ========================================= */
.j2w-rounded {
    border-radius: var(--j2w-radius-lg);
}

.j2w-border {
    border: 1px solid var(--j2w-border);
}

.j2w-resize {
    resize: both;
}

.j2w-resize-v {
    resize: vertical;
}

.j2w-resize-h {
    resize: horizontal;
}

/* =========================================
   UTILITY CLASSES - Grid
   ========================================= */
.j2w-grid-padding {
    padding: var(--j2w-space-1);
    gap: var(--j2w-space-1);
}

.j2w-grid-gap {
    gap: var(--j2w-space-1);
}

.j2w-grid-center {
    justify-items: center;
    align-items: center;
}

.j2w-grid-vstart {
    align-items: start;
}

.j2w-grid-vcenter {
    align-items: center;
}

.j2w-grid-vend {
    align-items: end;
}

.j2w-grid-hstart {
    justify-items: start;
}

.j2w-grid-hcenter {
    justify-items: center;
}

.j2w-grid-hend {
    justify-items: end;
}

/* =========================================
   UTILITY CLASSES - Grid Items
   ========================================= */
.j2w-gitem-center {
    justify-self: center;
    align-self: center;
}

.j2w-gitem-vstart {
    align-self: start;
}

.j2w-gitem-vcenter {
    align-self: center;
}

.j2w-gitem-vend {
    align-self: end;
}

.j2w-gitem-hstart {
    justify-self: start;
}

.j2w-gitem-hcenter {
    justify-self: center;
}

.j2w-gitem-hend {
    justify-self: end;
}

/* =========================================
   UTILITY CLASSES - Text Alignment
   ========================================= */
.j2w-align-right {
    text-align: right;
}

.j2w-align-hcenter,
.j2w-align-center {
    text-align: center;
}

.j2w-align-left {
    text-align: left;
}

.j2w-align-top {
    vertical-align: top;
}

.j2w-align-vcenter {
    vertical-align: middle;
}

.j2w-align-bottom {
    vertical-align: bottom;
}

.j2w-nowrap {
    white-space: nowrap;
}

/* =========================================
   TABLE COMPONENTS
   ========================================= */
.j2w-line {
    border: none;
}

.j2w-layout tr,
.j2w-layout td {
    vertical-align: top;
}

.j2w-table tr {
    display: table-row;
    width: 100%;
    vertical-align: middle;
}

.j2w-table tr[row] td {
    display: table-cell;
    text-align: left;
    vertical-align: middle;
}

.j2w-table tr[row] th {
    display: table-cell;
    text-align: left;
    font-weight: bold;
    vertical-align: top;
}

.j2w-table tr[hdr] td,
.j2w-table tr[hdr] th {
    display: table-cell;
    text-align: left;
    font-weight: bold;
    vertical-align: middle;
}

.j2w-table-hover tr[row]:hover,
.j2w-table-hover-light tr[row]:hover {
    color: var(--j2w-accent);
}

/* =========================================
   PANELS
   Emitted by panels/*.java and windows/Window.java, Process.java.
   ========================================= */
.j2w-container {
    padding: var(--j2w-space-2) var(--j2w-space-4);
}

.j2w-panel {
    padding: var(--j2w-space-2) var(--j2w-space-4);
    margin: var(--j2w-space-2) 0;
}

.j2w-card {
    background-color: var(--j2w-surface);
    border: 1px solid var(--j2w-border);
    border-radius: var(--j2w-radius-lg);
    box-shadow: var(--j2w-shadow-2);
    padding: var(--j2w-space-4);
    margin: var(--j2w-space-2) 0;
}

.j2w-bar {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: max-content;
    align-items: center;
    width: 100%;
    gap: var(--j2w-space-1);
}

.j2w-bar-item {
    padding: var(--j2w-space-2) var(--j2w-space-4);
}

.j2w-right {
    margin-left: auto;
    justify-self: end;
}

.j2w-row {
    display: flex;
    flex-wrap: wrap;
}

.j2w-col {
    flex: 1 1 0;
}

.j2w-block {
    display: block;
    width: 100%;
}

.j2w-label {
    color: var(--j2w-text-secondary);
    font-size: var(--j2w-text-sm);
}

.j2w-accent-bg {
    background-color: var(--j2w-accent);
    color: var(--j2w-text-on-accent);
}

/* =========================================
   ICONS
   Monochrome SVG applied as a mask painted with currentColor, so icons
   follow the text color in both themes. The concrete glyph is set per
   element via --icon (see basic/Icon.java).
   ========================================= */
.j2w-icon {
    display: inline-block;
    width: 16px;
    height: 16px;
    flex: none;
    background-color: currentColor;
    -webkit-mask: var(--icon) center / contain no-repeat;
    mask: var(--icon) center / contain no-repeat;
}

/* =========================================
   SPECIAL COMPONENTS
   ========================================= */
/* A filter is a field, not a fixed-size widget. An input with no width of its
   own carries the browser's default one - twenty characters, near 180px - and a
   grid item's automatic minimum is that same content width, so the pill refused
   to compress and put a floor under whatever held it: a side menu with a filter
   in its toolbar could not be dragged narrower than the field inside it. Losing
   the floor lets the field follow the room it is given instead of setting it. */
.j2w-filter,
.j2w-search {
    min-width: 0;
    width: 100%;
    color: var(--j2w-text-secondary);
    font-size: var(--j2w-text-sm);
    padding: var(--j2w-space-1) var(--j2w-space-2) var(--j2w-space-1) var(--j2w-space-3);
    border-radius: var(--j2w-radius-full);
    border: 1px solid var(--j2w-border);
    background-color: var(--j2w-surface);
    background-image: url(icons/search-muted.svg);
    background-position: right;
    background-repeat: no-repeat;
    background-size: 14px;
    background-origin: content-box;
}

.j2w-filter:focus,
.j2w-search:focus {
    color: inherit;
}

.j2w-input-file {
    border: 4px dashed var(--j2w-accent);
    border-radius: var(--j2w-radius-lg);
    background: var(--j2w-accent-subtle);
}

.j2w-input-file:hover {
    box-shadow: inset 0 0 0 999px var(--j2w-hover-tint);
}

.j2w-bottom-bar {
    position: fixed;
    bottom: 0;
    width: 100%;
}

.j2w-modal {
    margin: auto;
}

/* =========================================
   UTILITY CLASSES - Animation Control
   ========================================= */
.j2w-noanimate * {
    animation: none;
}

/* =========================================
   SCROLLBAR STYLES
   scrollbar-width/color are inherited from :root (see BASE STYLES);
   the ::-webkit-* block below is the WebKit fallback.
   ========================================= */
::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: var(--j2w-border-strong);
    border-radius: var(--j2w-radius-sm);
}

::-webkit-scrollbar-thumb:hover {
    background: var(--j2w-text-muted);
}

/* =========================================
   REDUCED MOTION
   ========================================= */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}