/* ==========================================================================
   PUDL floating windows. Load after pudl.css, and only on pages that use
   them. pudl-windows.js adds dragging, resizing and snapping; without it the
   windows the server rendered still show where the URL put them.

   A window's placement is four fractions of the layer, --win-x, --win-y,
   --win-w and --win-h, which the server writes into a style attribute and
   the script updates. data-win-mode selects maximised or a snapped half, and
   the fractions are then the floating geometry to restore to. A minimised
   window carries the hidden attribute.

   The window's frame is a band of the raised tint between two hairlines, and
   the whole band is the resize border.
   ========================================================================== */

:root {
  --win-band: 5px;            /* width of the frame between the two hairlines */
  --win-hit: 10px;            /* resize hit area, wider than the band */
  --win-min-w: 320px;
  --win-min-h: 200px;
  --win-shadow:
      0 2px 6px color-mix(in srgb, var(--shade) calc(var(--depth) * 0.8), transparent),
      0 10px 28px color-mix(in srgb, var(--shade) calc(var(--depth) * 0.9), transparent);
  --win-shadow-active:
      0 3px 8px color-mix(in srgb, var(--shade) calc(var(--depth) * 1.1), transparent),
      0 18px 48px color-mix(in srgb, var(--shade) calc(var(--depth) * 1.4), transparent);

  /* The active window's title bar. A theme may set these four to tune it.
     On the light theme the bar is filled with the accent, the classic
     active window, since a light theme's accent is dark enough to carry
     light text. */
  --win-active-bg: linear-gradient(color-mix(in srgb, #fff 10%, var(--accent)), var(--accent));
  --win-active-fg: var(--on-accent);
  --win-active-border: var(--accent-hover);
  --win-active-shadow: inset 0 1px color-mix(in srgb, #fff 18%, transparent);
}

/* A dark theme needs a bright accent for its links to read, and a bar
   filled with it glares, so the dark theme's bar is the raised gradient
   tinted toward the accent, in the normal text colour. With the tinted
   frame and the stronger shadow, that is enough to mark the active window
   without a rule under the bar, which read as a divider. */
[data-theme="dark"] {
  --win-active-bg: linear-gradient(
      color-mix(in srgb, var(--accent) 32%, var(--raise-top)),
      color-mix(in srgb, var(--accent) 20%, var(--surface-alt)));
  --win-active-fg: var(--text);
  --win-active-border: color-mix(in srgb, var(--accent) 55%, var(--raise-border));
  --win-active-shadow: inset 0 1px color-mix(in srgb, var(--light) var(--lit), transparent);
}

/* The host is the region the windows float over, usually the main area
   below the topbar. It does not scroll; the content inside it does. */
.win-host { position: relative; isolation: isolate; }
/* isolation keeps the windows' stacking inside the host, so a window never
   rises over what surrounds the host, such as a master-detail divider that
   straddles the host's edge. */

.win-layer {
  position: absolute; inset: 0; z-index: 50;
  overflow: hidden;
  pointer-events: none;       /* the content beneath stays usable */
}

/* === The window ========================================================== */
.win {
  position: absolute;
  left: calc(var(--win-x, 0.06) * 100%);
  top: calc(var(--win-y, 0.05) * 100%);
  width: calc(var(--win-w, 0.55) * 100%);
  height: calc(var(--win-h, 0.75) * 100%);
  min-width: min(var(--win-min-w), 100%);
  min-height: min(var(--win-min-h), 100%);
  display: flex; flex-direction: column;
  padding: var(--win-band);
  background: var(--surface-alt);
  border: 1px solid var(--raise-border);
  border-radius: var(--radius);
  box-shadow: var(--win-shadow);
  pointer-events: auto;
}
.win.active {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--raise-border));
  box-shadow: var(--win-shadow-active);
}

/* Maximised and snapped windows fill their slot and lose the frame, since
   they cannot be resized from it. */
.win[data-win-mode="maximized"],
.win[data-win-mode="left"],
.win[data-win-mode="right"] {
  top: 0; height: 100%;
  padding: 0; border-width: 0; border-radius: 0;
  min-width: 0; min-height: 0;
}
.win[data-win-mode="maximized"] { left: 0; width: 100%; }
.win[data-win-mode="left"] { left: 0; width: 50%; }
.win[data-win-mode="right"] { left: 50%; width: 50%; }
.win[data-win-mode="left"] { border-right-width: 1px; }
.win[data-win-mode="right"] { border-left-width: 1px; }

/* === Title bar ============================================================
   The title bar is what you drag, so it is raised. The active window's
   title bar takes the accent. */
.win-head {
  flex: none;
  display: flex; align-items: center; gap: 10px;
  min-height: 38px; padding: 5px 7px 5px 12px;
  background: var(--raise-grad);
  border: 1px solid var(--raise-border);
  border-radius: calc(var(--radius) - var(--win-band)) calc(var(--radius) - var(--win-band)) 0 0;
  box-shadow: inset 0 1px color-mix(in srgb, var(--light) var(--lit), transparent);
  cursor: move;
  user-select: none;
  touch-action: none;
}
.win-head:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--focus-ring); }
.win.active .win-head {
  background: var(--win-active-bg);
  border-color: var(--win-active-border);
  box-shadow: var(--win-active-shadow);
  color: var(--win-active-fg);
}
.win.active .win-head:focus-visible { box-shadow: var(--win-active-shadow), 0 0 0 3px var(--focus-ring); }
/* The title is usually a heading, so it resets what a page's own heading
   styles might give it. */
.win-title {
  flex: 1; min-width: 0; margin: 0; padding: 0; border: 0;
  font: 700 var(--text-md)/1.3 var(--font);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.win-chrome { flex: none; display: inline-flex; gap: 5px; align-items: center; }

/* Window buttons are small raised circles, so they stay legible on the
   accent title bar. */
.win-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0;
  border-radius: 50%;
  border: 1px solid var(--raise-border);
  background: var(--raise-grad);
  box-shadow: var(--raise-shadow);
  color: var(--text-muted);
  font: 600 var(--text-sm)/1 var(--font);
  text-decoration: none;
  cursor: pointer;
}
.win-btn:hover { color: var(--text); background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.win-btn:active { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.win-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--focus-ring); }
.win-btn[data-win-action="close"]:hover { color: var(--danger); }

/* The glyphs come from here, drawn from pudl.css's glyph masks, so the
   server renders each button empty with an aria-label, and the maximise
   button's glyph follows the window's mode. */
.win-btn[data-win-action]::before {
  content: ""; width: 12px; height: 12px;
  background: currentColor;
  -webkit-mask: var(--glyph) center / contain no-repeat;
  mask: var(--glyph) center / contain no-repeat;
}
.win-btn[data-win-action="page"]::before { --glyph: var(--glyph-open); }
.win-btn[data-win-action="minimize"]::before { --glyph: var(--glyph-minimize); }
.win-btn[data-win-action="maximize"]::before { --glyph: var(--glyph-maximize); }
.win-btn[data-win-action="close"]::before { --glyph: var(--glyph-close); }
.win:not([data-win-mode="floating"]) .win-btn[data-win-action="maximize"]::before { --glyph: var(--glyph-restore); }

.win[data-win-mode="maximized"] .win-head,
.win[data-win-mode="left"] .win-head,
.win[data-win-mode="right"] .win-head { border-radius: 0; border-width: 0 0 1px; }

/* === Body ================================================================ */
.win-body {
  flex: 1; min-height: 0; overflow: auto;
  padding: 16px 18px;
  background: var(--surface);
  border: 1px solid var(--border); border-top: 0;
  border-radius: 0 0 calc(var(--radius) - var(--win-band)) calc(var(--radius) - var(--win-band));
}
.win[data-win-mode="maximized"] .win-body,
.win[data-win-mode="left"] .win-body,
.win[data-win-mode="right"] .win-body { border: 0; border-radius: 0; }

/* === Resize handles ======================================================
   Invisible strips the script adds along each edge and at each corner. They
   straddle the frame so the band and a hair beyond it can be grabbed. */
.win-rh { position: absolute; z-index: 2; }
.win-rh[data-edge="n"], .win-rh[data-edge="s"] { left: var(--win-hit); right: var(--win-hit); height: var(--win-hit); cursor: ns-resize; }
.win-rh[data-edge="e"], .win-rh[data-edge="w"] { top: var(--win-hit); bottom: var(--win-hit); width: var(--win-hit); cursor: ew-resize; }
.win-rh[data-edge="n"] { top: calc(var(--win-hit) / -2); }
.win-rh[data-edge="s"] { bottom: calc(var(--win-hit) / -2); }
.win-rh[data-edge="w"] { left: calc(var(--win-hit) / -2); }
.win-rh[data-edge="e"] { right: calc(var(--win-hit) / -2); }
.win-rh[data-edge="nw"], .win-rh[data-edge="ne"],
.win-rh[data-edge="sw"], .win-rh[data-edge="se"] { width: calc(var(--win-hit) * 1.5); height: calc(var(--win-hit) * 1.5); }
.win-rh[data-edge="nw"] { top: calc(var(--win-hit) / -2); left: calc(var(--win-hit) / -2); cursor: nwse-resize; }
.win-rh[data-edge="se"] { bottom: calc(var(--win-hit) / -2); right: calc(var(--win-hit) / -2); cursor: nwse-resize; }
.win-rh[data-edge="ne"] { top: calc(var(--win-hit) / -2); right: calc(var(--win-hit) / -2); cursor: nesw-resize; }
.win-rh[data-edge="sw"] { bottom: calc(var(--win-hit) / -2); left: calc(var(--win-hit) / -2); cursor: nesw-resize; }
.win[data-win-mode="maximized"] .win-rh,
.win[data-win-mode="left"] .win-rh,
.win[data-win-mode="right"] .win-rh { display: none; }

/* A dashed outline shows where a window will land while it is dragged
   towards an edge. */
.win-ghost {
  position: absolute; z-index: 1000; pointer-events: none;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 2px dashed color-mix(in srgb, var(--accent) 60%, transparent);
  border-radius: var(--radius);
}

/* Dragging over an iframe or a text selection would lose the pointer, so
   the layer takes every event while a drag is in progress. */
.win-layer.dragging { pointer-events: auto; cursor: move; }
.win-layer.dragging * { user-select: none; }

/* === Child rows ==========================================================
   A list row whose link opens a window gains a row beneath it for each of
   that window's open children. The branch glyph marks the row as a child,
   so the relation does not rest on the indent alone. */
.md-row-child .md-item {
  position: relative;
  padding-inline-start: 32px;
  font-size: var(--text-xs);
}
.md-row-child .md-item::before {
  content: "";
  position: absolute; inset-inline-start: 14px; top: 50%;
  width: 12px; height: 12px; margin-top: -8px;
  background: var(--text-muted);
  -webkit-mask: var(--glyph-branch) center / contain no-repeat;
  mask: var(--glyph-branch) center / contain no-repeat;
}
.md-row-child .md-item:dir(rtl)::before { transform: scaleX(-1); }

/* === Dock ================================================================
   One tab per open window, in the order they were opened. A tab raises its
   window, and the active window's tab reads as pressed in. The glyph shows
   whether the window is visible (filled) or minimised (hollow). */
/* An empty dock draws nothing and takes no height, but it stays in the
   layout, so a toolbar that uses it as its elastic middle keeps its shape
   when the last window closes. */
.win-dock { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; min-width: 0; }
.win-tab {
  display: inline-flex; align-items: center; gap: 6px;
  max-width: 220px; padding: 3px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--raise-border);
  background: var(--raise-grad);
  box-shadow: var(--raise-shadow);
  font-size: var(--text-xs); font-weight: 600;
  color: var(--text); text-decoration: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.win-tab::before {
  content: ""; flex: none; width: 9px; height: 9px;
  background: var(--accent);
  -webkit-mask: var(--glyph-circle) center / contain no-repeat;
  mask: var(--glyph-circle) center / contain no-repeat;
}
.win-tab:hover { background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.win-tab:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--focus-ring); }
.win-tab[aria-current="true"] { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.win-tab.minimized { color: var(--text-muted); }
.win-tab.minimized::before {
  background: var(--text-muted);
  -webkit-mask-image: var(--glyph-ring);
  mask-image: var(--glyph-ring);
}

/* === Forced colours ======================================================
   The frame and buttons keep visible borders, and the active window's
   title bar and the dock's current tab take the system's highlight, since
   the shadows and tints that mark them otherwise are removed. */
@media (forced-colors: active) {
  .win, .win-head, .win-body, .win-btn, .win-tab { border: 1px solid CanvasText; }
  .win.active { border: 2px solid Highlight; }
  .win.active .win-head, .win-tab[aria-current="true"] {
    forced-color-adjust: none;
    background: Highlight; color: HighlightText; border-color: Highlight;
  }
  .win.active .win-head .win-btn { forced-color-adjust: none; background: Canvas; color: CanvasText; }
  .win-ghost { border-color: Highlight; }
  /* The glyphs are masks, painted in the colour the system has set. */
  .win-btn[data-win-action]::before { forced-color-adjust: none; background: ButtonText; }
  .win-tab::before, .md-row-child .md-item::before { forced-color-adjust: none; background: CanvasText; }
  .win-tab[aria-current="true"]::before { background: HighlightText; }
}

/* === Print ===============================================================
   The window in front prints as the page's content, title and body,
   without its frame, buttons or the dock; the others do not print. */
@media print {
  .win-host { height: auto !important; }
  .win-layer { position: static; overflow: visible; }
  .win-dock, .win-ghost, .win-rh, .win-chrome,
  .win:not(.active), .win[hidden] { display: none !important; }
  .win.active {
    position: static; width: auto; height: auto;
    padding: 0; border: 0; box-shadow: none; background: none;
  }
  /* What lies under the windows on screen is covered by them, so it does
     not print while a window does. */
  .win-host:has(.win.active) > :not(.win-layer) { display: none !important; }
  .win.active .win-head { background: none; border: 0; box-shadow: none; padding: 0 0 8px; color: #000000; }
  .win.active .win-title { font-size: var(--text-lg); white-space: normal; }
  .win.active .win-body { overflow: visible; border: 0; padding: 0; }
}
