/* ============================================================
   RACK APPEARANCE — fill colors, stroke, ghost states
   All visual rack colors live here; JS constants in state.js
   (RACK_COLORS) must stay in sync for canvas/topology rendering.
   ============================================================ */

/* ── Color tokens ── */
:root {
    --rack-color-server:  #e0d9d7;
    --rack-color-network: #ccbeba;
    --rack-color-storage: #a79189;
    --rack-color-gpu:     #9c6b5f;
    --rack-color-odf: #ffffff;
    --rack-color-inrow: #ffffff;
    --rack-color-overhead: #f2f7fb;

    --rack-stroke:     #333333;
    --rack-stroke-odf: #999999;

    --panel-front-fill:   #3498db;
    --panel-front-stroke: var(--dc-c-info);
    --panel-rear-fill:    var(--dc-c-error);
    --panel-rear-stroke:  var(--dc-c-error-hover);

    --ghost-valid-fill:    #d5f5e3;
    --ghost-valid-stroke:  var(--dc-c-success);
    --ghost-invalid-fill:  #fde8e8;
    --ghost-invalid-stroke:var(--dc-c-error);
    --ghost-pending-stroke:var(--dc-c-dark);

    /* Dash pattern for outlines on the plan. Its period (6+4) must stay a divisor of
       TILE_PX and match RESERVED_DASH_PERIOD in js/racks.js — that is what lets two
       outlines lying on the same line overlap dash-on-dash instead of one filling the
       other's gaps and the seam turning solid. */
    --rack-dash: 6 4;
}

/* ── Rack body fills by type ── */
.rack-body { stroke: var(--rack-stroke); stroke-width: 2; }
.rack-body--server  { fill: var(--rack-color-server); }
.rack-body--network { fill: var(--rack-color-network); }
.rack-body--storage { fill: var(--rack-color-storage); }
.rack-body--gpu     { fill: var(--rack-color-gpu); }
.rack-body--odf {
    fill: var(--rack-color-odf);
    stroke: var(--rack-stroke-odf);
}
.rack-body--inrow {
    fill: var(--rack-color-inrow);
    stroke: var(--rack-stroke);
}
/* Suspended panel: body is drawn 500 mm inside its 600 mm footprint, so the
   footprint rectangle itself is never painted — only this inner square.
   Neutral outline on purpose: blue belongs to the panel bar alone, which darkens
   once a link lands on it. A blue body drowned out that signal. */
.rack-body--overhead {
    fill: var(--rack-color-overhead);
    stroke: var(--rack-stroke);
}

/* Reserved: a placeholder for a cabinet that will be installed later. Dashed
   outline, no panels, no service zones — see drawRack(). */
/* The suspended panel's body is only 500 mm across and the bar takes a slice of it,
   so the name runs at a smaller size than a cabinet's to stay inside. */
.rack-label--overhead {
    font-size: 8px;
}

/* The body rect of a Reserved position draws nothing at all: it exists only as the
   click area. Its outline is four separate dashed lines (.rack-reserved-edge), because
   a dashed rect runs one pattern around the whole perimeter and two cabinets side by
   side then merge their shared edge into a solid line — see _reservedOutlineEls(). */
.rack-body--reserved {
    fill: none;
    stroke: none;
    /* Without this an unfilled SVG rect is not clickable at all, and the cabinet could
       be marked Reserved but never selected to turn it back. */
    pointer-events: all;
}
.rack-reserved-edge {
    stroke: var(--rack-stroke);
    stroke-width: 2;
    stroke-dasharray: var(--rack-dash);
    /* Phase comes from stroke-dashoffset, set per edge in _reservedOutlineEls() */
    pointer-events: none;
}
.rack-reserved-label {
    font-size: 8px;
    font-weight: 700;
    letter-spacing: 0.5px;
    fill: var(--rack-stroke);
    opacity: 0.65;
    text-anchor: middle;
    dominant-baseline: middle;
    pointer-events: none;
}

.odf-service-zone {
    opacity: 0.55;
}

/* ── Ghost states ── */
.rack-ghost {
    stroke-width: 2.5;
    stroke-dasharray: 6,3;
}
.rack-ghost--valid   { fill: var(--ghost-valid-fill);   stroke: var(--ghost-valid-stroke); }
.rack-ghost--invalid { fill: var(--ghost-invalid-fill); stroke: var(--ghost-invalid-stroke); }
.rack-ghost--pending { stroke: var(--ghost-pending-stroke); }

/* ── Ghost rack label color ── */
.rack-ghost-label--valid   { fill: var(--ghost-valid-stroke); }
.rack-ghost-label--invalid { fill: var(--ghost-invalid-stroke); }
.rack-ghost-label--pending { fill: var(--ghost-pending-stroke); }

/* ── External Cabinet (square shape) ── */
:root {
    --rack-color-external: #e8f4fd;
    --rack-stroke-external: #333333;
}
.external-rack-body {
    fill: var(--rack-color-external);
    stroke: var(--rack-stroke-external);
    stroke-width: 1;
    cursor: pointer;
}
.external-rack:hover .external-rack-body {
    fill: #d6eaf8;
}
.external-rack-body--active {
    stroke: var(--dc-c-accent);
    stroke-width: 3.75;
}
.external-rack-ghost {
    fill: rgba(68, 168, 219, 0.2);
    stroke: #2e86c1;
    stroke-width: 1.5;
    stroke-dasharray: 5, 3;
    pointer-events: none;
}

/* ── Rack type icons on floor plan ── */
.rack-icon              { opacity: 0.35; }
.rack-icon--storage,
.rack-icon--gpu         { opacity: 0.5; filter: brightness(5); }

/* ── External cabinet link section header in link list ── */
.link-list-section-header {
    font-size: var(--dc-text-xs);
    font-weight: 700;
    color: var(--dc-c-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 8px 4px 4px;
    margin-top: 8px;
    border-top: 1px solid var(--dc-n-150);
}

/* ── External endpoint card in link modal ── */
.lp-ep-card--external {
    background: #eaf4fc;
    border: 1px dashed #5dade2;
}

/* ── Tray draw preview (line / rect modes) ── */
.tray-preview-cell         { fill: rgba(101,184,165,0.45); stroke: var(--dc-c-accent); stroke-width: 1; pointer-events: none; }
.tray-preview-cell--remove { fill: rgba(231, 76, 60, 0.35); stroke: var(--dc-c-error); stroke-width: 1; pointer-events: none; }

/* ── Ghost panel pathway indicators ── */
.ghost-panel-bar--front { fill: var(--panel-front-fill); stroke: var(--panel-front-fill); stroke-width: 1; }
.ghost-panel-bar--rear  { fill: var(--panel-rear-fill);  stroke: var(--panel-rear-fill);  stroke-width: 1; }
.ghost-panel-bar--off-pathway { fill: #ffffff; stroke-width: 2; }

/* ── Floating rack action toolbar ── */
.rack-toolbar {
    position: fixed;
    z-index: 2000;
    display: flex;
    align-items: center;
    gap: 2px;
    background: rgba(255,255,255,0.95);
    border: 1px solid var(--dc-n-200);
    border-radius: 7px;
    padding: 4px 6px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.15);
    transform: translate(-50%, calc(-100% - 10px));
    pointer-events: all;
    user-select: none;
}
.rack-toolbar::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    border: 6px solid transparent;
    border-top-color: var(--dc-n-200);
}
.rack-toolbar button {
    width: 26px;
    padding: 0;
    height: 26px;
    background: transparent;
    border: none;
    color: var(--dc-n-900);
    cursor: pointer;
    border-radius: 4px;
    font-size: var(--dc-text-lg);
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s;
}
/* Icons through a mask on ::before — the same way icon buttons already work in
   css/ui-primitives.css (.icon-btn--delete and friends), and reusing its very delete
   artwork. Not <img>: the mask takes its colour from the button, so one file serves all
   three states this toolbar has — the resting grey, the darker hover, and the red the
   danger button turns. An <img> is stuck with the colour it was exported in, and the
   destructive action would lose the one signal saying it is destructive.
   flex: 0 0 auto matters: a sized box inside a flex-centred button is otherwise free to be
   shrunk away, which is exactly what happened to the inline <svg> tried here first.
   Worth knowing before debugging a blank toolbar: a mask loaded from a separate file is
   blocked under file://, where every file counts as its own origin. Open the app through a
   server — Live Server in development, nginx in production — and it paints. */
.rack-toolbar button::before {
    content: '';
    width: 17px;
    height: 17px;
    flex: 0 0 auto;
    background-color: currentColor;
    -webkit-mask: center / contain no-repeat;
            mask: center / contain no-repeat;
}
.rack-toolbar .rt-rotate::before {
    -webkit-mask-image: url('../images/icon_rotate.svg');
            mask-image: url('../images/icon_rotate.svg');
}
.rack-toolbar .rt-row::before {
    -webkit-mask-image: url('../images/icon_row.svg');
            mask-image: url('../images/icon_row.svg');
}
/* The bin is drawn edge to edge in its 20×20 box, where the other two sit inside a margin.
   Matched by eye rather than by number so the three read at one weight. */
.rack-toolbar .rt-delete::before {
    width: 15px;
    height: 15px;
    -webkit-mask-image: url('../images/icon_delete.svg');
            mask-image: url('../images/icon_delete.svg');
}
.rack-toolbar button:hover {
    background: var(--dc-n-50);
    color: var(--dc-n-1000);
}
.rack-toolbar button.rack-toolbar-btn--danger:hover {
    background: #fde8e8;
    color: var(--dc-c-error-hover);
}
.rack-toolbar-sep {
    width: 1px;
    height: 18px;
    background: var(--dc-n-200);
    margin: 0 3px;
}
