/* Shit Talker's own layout, on top of the Windows 95 chrome in win95.css.
 *
 * The main window is not a responsive grid. Every control is placed at the
 * absolute rectangle Talker.exe stores for it -- see js/layout.js -- so this
 * file styles controls and never positions them.
 */

body {
  margin: 0;
  background: var(--desktop);
  font: var(--font);
  overflow: hidden;
  /* A window can be dragged from anywhere on it, so a press that turns into a
     drag must not also select the caption it started on -- on a phone that
     selection is what you get instead of the window moving. Editable text opts
     back in below; it is the only text here anyone needs to select. */
  user-select: none;
  -webkit-user-select: none;
}

input,
textarea,
[contenteditable] {
  user-select: text;
  -webkit-user-select: text;
}

/* A press may be held as long as you like -- the phrase fires when the button
   is let go, not when it is touched -- so iOS must not put its own long-press
   callout up on top of it. */
* {
  -webkit-touch-callout: none;
}

/* And it must not decide the press was the start of a scroll it should take
   over. Without this a long hold on a phone ends in a pointercancel, and a
   cancelled touch is one the button never hears the end of. Scrollable things
   opt back in below. */
button,
.w95-button,
.desktop-icon,
.start-item,
.menu-item {
  /* pinch-zoom, not none. `none` gave the app every touch, which is right for
     single-finger work -- a long hold has to stay a hold, not turn into a pan
     -- but it also refused the browser the one gesture worth having: this
     window is 800x600 of buttons with no gaps in it, so on a phone there is
     nowhere to start a pinch that is not a control, and there was no way to
     zoom in on anything.
     `pinch-zoom` keeps single-finger panning and double-tap zoom off, so drags
     and holds still reach the app, and lets two fingers zoom from anywhere,
     including from on top of a button. The press underneath is cancelled
     rather than fired -- see pinchState in js/drag-state.js. */
  touch-action: pinch-zoom;
}

textarea,
.folder-body,
.recorder-screen {
  touch-action: auto;
}

/* While a window is being dragged, nothing is allowed to look pressed. That
   lives in win95.css now: `.dragging-window *` sits in the pressed rule's own
   exclusion list, because that selector outranked this file's override -- the
   button stayed sunk through every drag while the rule meant to lift it lost
   the cascade. With the pressed rule simply not matching, the button reverts
   to its true unpressed face instead of an approximation of one. */


/* --- the main window ------------------------------------------------------ */

/* The 2px frame is drawn by the window's own inset bevel, showing through the
   margins on the title bar and the client. Those are vertical margins on the
   first and last children of a box with no padding and no border, so without a
   block formatting context here they collapse straight out of the window --
   taking the top and bottom of the frame with them and leaving the title bar
   flush against the desktop. The left and right never collapse, which is why
   only the horizontal edges went missing. */
.main-window {
  display: flow-root;
  flex-shrink: 0;
  padding: 0;
}

.main-window .title-bar {
  margin: 2px 2px 0;
}

.main-window .menu-bar {
  margin: 0 2px;
}

.title-icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  image-rendering: pixelated;
}

/* The client area: the coordinate space every rect in layout.json is in. */
.client {
  position: relative;
  overflow: hidden;
  margin: 0 2px 2px;
  background: var(--face);
}

/* --- controls on the client ----------------------------------------------- */

.client button {
  display: flex;
  overflow: hidden;
  align-items: center;
  justify-content: center;
  padding: 0 2px;
  font: 700 var(--font);
  line-height: 1;
}

/* Group boxes. VB3 draws the caption at the control's top edge and starts the
   groove five pixels below it, under the middle of the caption text -- measured
   off win95-800x600.jpg, where all five frames agree to the pixel. */
.frame {
  pointer-events: none;
}

.frame-groove {
  position: absolute;
  top: 5px;
  right: 0;
  bottom: 0;
  left: 0;
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight),
    inset 2px 2px 0 var(--highlight),
    inset -2px -2px 0 var(--shadow);
}

.frame-legend {
  position: absolute;
  top: 0;
  left: 8px;
  padding: 0 3px;
  background: var(--face);
  font: 700 var(--font);
  line-height: 13px;
}

/* The five black text boxes: four Quick Boxes and the announcement banner.
   The scrollbar is real DOM -- see js/components/vb-text-box.js.

   No border: the capture shows the window face running straight into the black,
   with no sunken bevel anywhere on them. VB3 BorderStyle 0. */
.vb-box {
  display: flex;
  box-sizing: border-box;
  background: #000;
}

.vb-box-text {
  flex: 1;
  min-width: 0;
  padding: 0 2px;
  border: 0;
  background: #000;
  box-shadow: none;
  color: var(--vb-red);
  /* Not bold. Every button on this form inherits VB3's bold default, but the
     text boxes were set back to regular -- plain in the banner capture. */
  font: var(--font);
  line-height: 13px;
  overflow-y: hidden;
  resize: none;
  scrollbar-width: none;
}

.vb-box-text::-webkit-scrollbar {
  display: none;
}

.vb-scrollbar {
  display: flex;
  width: 16px;
  flex-direction: column;
  flex-shrink: 0;
  background: var(--face);
}

.vb-sb-arrow {
  position: relative;
  height: 16px;
  flex-shrink: 0;
  padding: 0;
}

/* The triangles, as CSS borders so they stay crisp at any zoom. */
.vb-sb-arrow::before {
  position: absolute;
  top: 50%;
  left: 50%;
  border: 4px solid transparent;
  content: "";
  transform: translate(-50%, -50%);
}

.vb-sb-arrow.up::before {
  margin-top: -2px;
  border-bottom-color: #000;
}

.vb-sb-arrow.down::before {
  margin-top: 2px;
  border-top-color: #000;
}

/* Windows 95 greys a disabled arrow and offsets a white copy behind it, the
   same way it greys disabled button text. */
.vb-scrollbar.disabled .vb-sb-arrow.up::before {
  border-bottom-color: var(--shadow);
  filter: drop-shadow(1px 1px 0 var(--highlight));
}

.vb-scrollbar.disabled .vb-sb-arrow.down::before {
  border-top-color: var(--shadow);
  filter: drop-shadow(1px 1px 0 var(--highlight));
}

.vb-sb-track {
  position: relative;
  flex: 1;
  min-height: 0;
  background-color: var(--face);
  background-image:
    linear-gradient(45deg, var(--highlight) 25%, transparent 25%, transparent 75%, var(--highlight) 75%),
    linear-gradient(45deg, var(--highlight) 25%, transparent 25%, transparent 75%, var(--highlight) 75%);
  /* Phase matched to the capture: face in the top-left cell, not white. */
  background-position: 1px 0, 0 1px;
  background-size: 2px 2px;
}

.vb-sb-thumb {
  position: absolute;
  right: 0;
  left: 0;
  background: var(--face);
  box-shadow:
    inset -1px -1px 0 var(--dark),
    inset 1px 1px 0 var(--highlight),
    inset -2px -2px 0 var(--shadow),
    inset 2px 2px 0 var(--light);
}

/* The two single-line inputs on the top row: black and borderless, like the
   boxes below them. */
input.vb-input {
  box-sizing: border-box;
  padding: 1px 2px;
  border: 0;
  background: #000;
  box-shadow: none;
  color: var(--vb-red);
  font: var(--font);
}

/* --- windows and modals that are 1.3's own -------------------------------- */

/* Inside the screen, not beside it (main.js): a Windows 95 modal blocks the
   application, not the monitor. `fixed` inside a transformed ancestor is
   contained by that ancestor, so on the phone and tablet builds -- where the
   screen is scaled -- this covers the desktop and scales with it, while on a
   desktop, where nothing is transformed, it is the viewport as it always was. */
.modal-overlay {
  position: fixed;
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.25);
  inset: 0;
}

/* The dialog inside it is a real <dialog>, which the UA sheet dresses: absolute
   positioning, a border, 1em of padding and its own colours. .window supplies
   all of that already, so the rest is taken back off here. `static` matters
   most -- the overlay centres with flex, which an absolutely positioned child
   would sidestep, and dragByTitleBar switches to absolute itself on the first
   drag. */
.modal-overlay > dialog.window {
  position: static;
  inset: auto;
  width: auto;
  height: auto;
  max-width: none;
  max-height: none;
  border: 0;
  margin: 0;
  color: inherit;
}

/* A property sheet is the same <dialog> inside one of the shell's holders
   (under createToggleView's host div), and the holder is what positions it;
   the UA's absolute placement, border and colours would fight that. */
.shell-window dialog.window {
  position: static;
  inset: auto;
  height: auto;
  max-width: none;
  max-height: none;
  border: 0;
  margin: 0;
  color: inherit;
}

.window-body {
  padding: 6px;
}

/* --- Notepad ------------------------------------------------------------- */

.notepad-menu {
  padding: 0 2px;
}

/* Notepad's client is a read-only textarea, in the system font at the system
   size -- Notepad's own default, and the reason the timestamps and the text no
   longer disagree about typeface. `resize: none` because the shell's grip owns
   resizing; the browser's own corner handle would fight it. */
.notepad-page {
  display: block;
  width: 100%;
  height: 100%;
  flex: 1;
  padding: 3px 4px;
  border: 0;
  border-radius: 0;
  background: var(--window);
  color: var(--window-text);
  font: var(--font);
  line-height: 14px;
  resize: none;
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight),
    inset 2px 2px 0 var(--dark),
    inset -2px -2px 0 var(--face);
}

.notepad-page.no-wrap {
  white-space: pre;
  overflow-x: auto;
}

.notepad-page.empty {
  color: var(--shadow);
}

.field-row {
  display: flex;
  align-items: center;
  gap: 6px;
}

/* The voice dialog's fields were all a couple of pixels short. Windows 95 edits
   and buttons are 21px and 23px; these match. */
/* DOC.EXE's dialog is a C program's, drawn in MS Sans Serif -- not one of
   Talker.exe's VB3 frames, which inherit that form's bold. Its group captions
   are plain, and the capture shows both "Characteristics" and "Test text:"
   that way. */
.voices-window legend {
  font-weight: 400;
}

.voices-window input[type="text"],
.voices-window textarea {
  min-height: 21px;
  padding: 3px 6px 2px;
}

.voices-window textarea {
  padding: 3px 4px;
}

/* Scoped to the body on purpose: unscoped, this ties with
   `.title-bar-controls button` and wins on load order, which grew the close box
   into a 23px square hanging out of the title bar. */
.voices-window .window-body button {
  min-height: 23px;
  padding: 0 10px;
}

.voice-row {
  margin-bottom: 3px;
}

.voice-row .slider {
  flex: 1;
}

/* Every caption in Characteristics. Narrow, because the dropdowns took the
   room: they are right-justified, so what the caption gives up they gain. */
.voice-label {
  width: 62px;
  flex: none;
  /* "Rate (wpm):" is the longest of them and wraps to two lines at anything
     narrower, which drags its whole row taller than the one above it. */
  white-space: nowrap;
}

/* The caption-and-edit half of a Pitch or Rate row: its slider is on the next
   line, so the gap under it closes up and the pair reads as one control. */
.voice-row-tight {
  margin-bottom: 1px;
}

/* The slider line: the whole width of the group box now that the arrows have
   gone up to sit with the number they step. */
.voice-slider-row {
  gap: 0;
}

/* --- "Program me you sick little monkey!" ---------------------------------- */

.monkey-body {
  position: relative;
  background: var(--face);
}

.monkey-slot {
  position: absolute;
  display: flex;
  flex-direction: column;
}

.monkey-input {
  height: 19px;
  flex-shrink: 0;
  font: 700 var(--font);
}

.monkey-preview {
  height: 19px;
  flex-shrink: 0;
  overflow: hidden;
  padding: 0 2px;
  font: 700 var(--font);
  line-height: 1;
  white-space: nowrap;
}

.monkey-action {
  position: absolute;
  width: 56px;
  height: 24px;
  font: 700 var(--font);
}

/* The two photographs, one 60x120 bitmap out of Talker.exe. `Clear all` ends
   at 32px and the original leaves about 4px under it, not 8. */
.monkey-faces {
  position: absolute;
  top: 36px;
  left: 544px;
  width: 60px;
  height: 120px;
  image-rendering: pixelated;
}

/* Shit Talker itself while the Custom Buttons dialog is open. Not display:none
   -- the shell measures this node, and it must come back where it was. */
.main-window.behind-modal {
  visibility: hidden;
}

/* --- Sound Recorder ------------------------------------------------------- */

/* The master level, under the seek bar. Not part of the 1996 dialog: the
   engine's voices differ by more than 12dB between a whispered Child and a Big
   Male, and matching the app to a pair of speakers is a thing this rebuild can
   be asked to do that a PC speaker could not. */
.recorder-gain {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 8px 0;
  font: var(--font);
}

.recorder-gain-bar {
  flex: 1;
}

.recorder-gain-value {
  width: 4ch;
  text-align: right;
}

/* On the end of the gain row: a thing done to a take that is already there,
   next to the other control that shapes what you end up with. */
.recorder-trim {
  height: 18px;
  flex: none;
  padding: 0 8px;
  white-space: nowrap;
}

.recorder-gain-reset {
  height: 18px;
  flex: none;
  padding: 0 6px;
}

.recorder-window {
  min-width: 320px;
}

.recorder-body {
  display: flex;
  min-height: 0;
  flex: 1;
  flex-direction: column;
  padding: 4px;
  color: #000;
  font: var(--font);
}

/* Position on the left, Length on the right, the wave between them -- the
   accessory's arrangement, and the reason the panes stretch and the two
   readouts do not. */
.recorder-meters {
  display: flex;
  min-height: 0;
  flex: 1;
  gap: 6px;
}

.recorder-meter {
  width: 58px;
  flex-shrink: 0;
  line-height: 14px;
}

.recorder-meter.right {
  text-align: right;
}

.recorder-value {
  padding-top: 2px;
}

.recorder-screens {
  display: flex;
  min-width: 0;
  min-height: 0;
  flex: 1;
  flex-direction: column;
  gap: 3px;
}

/* The green trace sits in a sunken well, as it does in the accessory. The
   overview gets the room; the zoom is a fixed strip under it. */
.recorder-screen {
  min-height: 0;
  flex: 1;
  padding: 2px;
  background: #000;
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight),
    inset 2px 2px 0 var(--dark),
    inset -2px -2px 0 var(--face);
}

.recorder-screen.zoom {
  height: 32px;
  flex: none;
}

/* The stereo level meter: a slim well of its own under the gain row, running
   whether or not anything is armed -- the point of a meter is seeing your
   level before committing to record at it. */
.recorder-vu {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  gap: 6px;
  margin-top: 4px;
}

.recorder-screen.vu {
  height: 20px;
  min-width: 0;
  flex: 1;
}

.recorder-wave {
  display: block;
}

/* The position slider, drawn as the Windows 95 scrollbar the accessory used:
   a dithered trough and a raised square thumb. */
.recorder-seek {
  width: 100%;
  height: 17px;
  flex-shrink: 0;
  appearance: none;
  padding: 0;
  margin: 5px 0 0;
  background:
    repeating-conic-gradient(var(--face) 0% 25%, #fff 0% 50%) 0 0 / 2px 2px;
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight);
}

.recorder-seek::-webkit-slider-thumb {
  width: 15px;
  height: 17px;
  appearance: none;
  background: var(--face);
  box-shadow:
    inset -1px -1px 0 var(--dark),
    inset 1px 1px 0 var(--highlight),
    inset -2px -2px 0 var(--shadow),
    inset 2px 2px 0 var(--light);
}

.recorder-seek:disabled::-webkit-slider-thumb {
  visibility: hidden;
}

.recorder-status {
  overflow: hidden;
  padding: 5px 1px 4px;
  white-space: nowrap;
}

/* The transport spans the window, so the buttons divide it between them. */
.recorder-transport {
  display: flex;
  flex-shrink: 0;
  gap: 2px;
}

.recorder-button {
  display: flex;
  height: 25px;
  flex: 1;
  align-items: center;
  justify-content: center;
  color: #000;
}

.recorder-button:disabled {
  color: var(--disabled);
}

/* Rolling: the button is held down and its dot pulses, so the transport reads
   as running rather than as dead. It is disabled while it rolls -- Stop is what
   backs out of it -- and the disabled grey would otherwise say "nothing is
   happening", which is the opposite of what is happening. (This was the armed
   state's paint. Record no longer waits for a sound, but a Record button that
   is doing something and cannot be pressed still needs to say so.) */
.recorder-button.rolling:disabled {
  color: #000;
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight),
    inset 2px 2px 0 var(--dark),
    inset -2px -2px 0 var(--light);
}

.recorder-button.rolling:disabled .recorder-glyph .dot {
  animation: recorder-roll 1s steps(1, end) infinite;
}

@keyframes recorder-roll {
  0%, 50% { background: #c00000; }
  50.01%, 100% { background: #400000; }
}

@media (prefers-reduced-motion: reduce) {
  .recorder-button.rolling:disabled .recorder-glyph .dot {
    animation: none;
    background: #c00000;
  }
}

/* Glyphs are child elements rather than pseudo-elements: a button that stretches
   has to keep its icon in the middle of itself, and flex centring only works on
   something that is actually in the flow. */
.recorder-glyph {
  display: flex;
  align-items: center;
  gap: 1px;
  pointer-events: none;
}

.recorder-glyph i {
  display: block;
}

.recorder-glyph .left {
  border: 5px solid transparent;
  border-left-width: 0;
  border-right-color: currentcolor;
}

.recorder-glyph .right {
  border: 5px solid transparent;
  border-right-width: 0;
  border-left-color: currentcolor;
}

.recorder-glyph .bar {
  width: 2px;
  height: 10px;
  background: currentcolor;
}

.recorder-glyph .pipe {
  width: 3px;
  height: 10px;
  margin: 0 1px;
  background: currentcolor;
}

.recorder-glyph .square {
  width: 9px;
  height: 9px;
  background: currentcolor;
}

.recorder-glyph .dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #a00;
}

.recorder-button:disabled .recorder-glyph .dot {
  background: var(--disabled);
}

/* --- the Windows 95 shell ------------------------------------------------- */

.app-container {
  display: flex;
  min-height: 100vh;
  padding: 0;
  /* Shows only when Display Properties has set a desktop area smaller than the
     browser, and then it is the dark around a VM's screen. */
  background: #101010;
}

/* `margin: auto` rather than `align-items: center`: on a viewport shorter than
   768px, centring with align-items pushes the top of the screen out of reach
   and you cannot scroll back to it. */
.app-container > * {
  margin: auto;
}

/* The screen is the browser window: resize it and the desktop resizes, the way
   a VM window does. */
.screen {
  position: relative;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  background: var(--desktop);
}

/* A desktop area chosen in Display Properties. The browser window cannot be
   resized from inside the page, so the screen shrinks within it instead and the
   host shows around the edge -- which is what a VM looks like anyway. */
.screen.fixed-area {
  max-width: 100vw;
  max-height: 100vh;
  box-shadow: 0 0 0 1px #000;
}

.desktop-icons {
  position: absolute;
  top: 4px;
  left: 4px;
  /* Bounded rather than fixed-width, so the icons wrap into a second column
     when they reach the taskbar -- which is what Windows 95 does, and what a
     single column of twelve did not: the last two fell off the bottom of a
     640x480 desktop and could not be reached at all. 32px, not 4px: the icon
     layer runs the full height of the screen, so the taskbar's own 28px has to
     come off here or the last icon in a column sits behind it. */
  bottom: 32px;
  z-index: 1;               /* always behind every window */
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 14px;
}

.window-layer {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;     /* only the windows themselves take clicks */
}

.window-layer > * {
  position: absolute;
  pointer-events: auto;
}

/* Anything the shell manages: the title bar is the drag handle. */
.shell-window .title-bar {
  cursor: default;
}

/* A resizable window is sized by its holder, so everything between the holder
   and the client area has to hand the height down rather than wrap it. */
.shell-window.resizable {
  display: flex;
}

.shell-window.resizable > *:not(.window-edge) {
  display: flex;
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
}

.shell-window.resizable .window {
  display: flex;
  width: 100%;
  min-width: 0;
  height: 100%;
  min-height: 0;
  flex-direction: column;
}

.shell-window.resizable .window-body {
  display: flex;
  min-height: 0;
  flex: 1;
  flex-direction: column;
}

/* Windows 95 has no visible grip -- you grab the frame itself, from any edge or
   corner, and the pointer changes shape as soon as it is over one. These are
   the invisible hit zones that do that. They sit slightly outside the window so
   the frame itself is grabbable rather than only the pixels inside it, and they
   are above the window body so the cursor wins over whatever is underneath. */
.window-edge {
  position: absolute;
  z-index: 2;
}

.window-edge.n,
.window-edge.s {
  right: 4px;
  left: 4px;
  height: 5px;
  cursor: ns-resize;
}

.window-edge.e,
.window-edge.w {
  top: 4px;
  bottom: 4px;
  width: 5px;
  cursor: ew-resize;
}

.window-edge.n { top: -2px; }
.window-edge.s { bottom: -2px; }
.window-edge.w { left: -2px; }
.window-edge.e { right: -2px; }

/* The corners are bigger than the edges, as they are on the real thing --
   a 5px corner is a target nobody can hit. */
.window-edge.ne,
.window-edge.nw,
.window-edge.se,
.window-edge.sw {
  width: 12px;
  height: 12px;
}

.window-edge.nw { top: -2px; left: -2px; cursor: nwse-resize; }
.window-edge.se { right: -2px; bottom: -2px; cursor: nwse-resize; }
.window-edge.ne { top: -2px; right: -2px; cursor: nesw-resize; }
.window-edge.sw { bottom: -2px; left: -2px; cursor: nesw-resize; }

/* Where a dragged window would land if it were let go here. Windows 7 draws
   this above the other windows and beneath the one in hand; the shell puts the
   dragged window at 101 for the length of the gesture to keep that order. */
.snap-ghost {
  position: absolute;
  z-index: 100;
  display: none;
  box-sizing: border-box;
  pointer-events: none;     /* an outline, not a target */
  /* The active title bar's navy, spelled out: the phone build's WebView is old
     enough that color-mix() would leave the ghost invisible. */
  background: rgba(0, 0, 128, 0.28);
  border: 2px solid var(--title);
  transition: left 90ms, top 90ms, width 90ms, height 90ms;
}

.snap-ghost.on {
  display: block;
}

/* A maximised window fills the desktop and cannot be resized or dragged, so its
   edges stop being handles. */
.shell-window.maximised .window-edge {
  display: none;
}

.shell-window.maximised .title-bar {
  cursor: default;
}

.desktop-icon {
  display: flex;
  width: 84px;
  flex-direction: column;
  align-items: center;
  padding: 2px 0;
  border: 1px dotted transparent;
  background: none;
  box-shadow: none;
  color: #fff;
  cursor: default;
  font: var(--font);
  line-height: 13px;
  text-align: center;
}

/* A desktop icon is a button, so it inherits the Windows 95 press: a bevel and
   a one-pixel shove. Neither belongs on an icon, and neither could be turned off
   by `.desktop-icon:active` -- `button:active:not(:disabled)` carries an extra
   pseudo-class and outranks it. Matching that specificity is what makes these
   stick. The shift is off by name in the press rule; this is only the bevel. */
.desktop-icon:active:not(:disabled) {
  box-shadow: none;
}

.desktop-icon:focus-visible:not(:disabled) {
  outline: none;
}

.desktop-icon-image {
  position: relative;
  display: block;
  width: 32px;
  height: 32px;
  margin-bottom: 4px;
}

.desktop-icon-image img {
  display: block;
  width: 32px;
  height: 32px;
  image-rendering: pixelated;
}

/* The shortcut badge: a white tile in the bottom-left with a black arrow. */
.desktop-icon-image.shortcut::after {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 9px;
  height: 9px;
  border: 1px solid var(--shadow);
  background: #fff;
  color: #000;
  content: "\2197";
  font-size: 9px;
  line-height: 8px;
  text-align: center;
  transform: scaleX(-1);
}

.desktop-icon-label {
  padding: 0 2px;
}

.desktop-icon.selected .desktop-icon-label {
  background: var(--hilight);
  color: var(--hilight-text);
  outline: 1px dotted var(--hilight-text);
}

.desktop-icon.selected .desktop-icon-image img {
  filter: brightness(0.7) saturate(1.5);
}

/* --- taskbar -------------------------------------------------------------- */

.taskbar {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 3;
  display: flex;
  height: 28px;
  align-items: center;
  gap: 4px;
  /* No vertical padding: the 22px buttons are centred in the 28px bar, which
     leaves 3px above and below -- the gap Windows 95 leaves, and half what was
     here before. It also brings the bar to a true 28px, which is what
     TASKBAR_H in shell.js has always assumed; at 32px a maximised window was
     running 4px under the taskbar. */
  padding: 0 2px;
  background: var(--face);
  box-shadow: inset 0 1px 0 var(--highlight);
}

.start-button {
  display: flex;
  height: 22px;
  flex-shrink: 0;
  align-items: center;
  gap: 4px;
  padding: 0 6px 0 3px;
  font: 700 var(--font);
}

/* Held down for as long as the menu is open, which :active cannot express --
   the button is not actually being pressed that whole time. The same
   transparent-border shift win95.css uses, so it stays true whatever padding
   this button is given. */
.start-button.pressed {
  border-width: 1px 0 0 1px;
}

/* Held down for as long as the menu is open. The bevel is inverted the same way
   :active does it in win95.css; that rule cannot be reused because the button
   is not actually being pressed the whole time the menu is up. */
.start-button.pressed {
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight),
    inset 2px 2px 0 var(--dark),
    inset -2px -2px 0 var(--light);
}

.start-flag {
  width: 16px;
  height: 16px;
  image-rendering: pixelated;
}

.taskbar-tasks {
  display: flex;
  min-width: 0;
  flex: 1;
  gap: 3px;
}

.task-button {
  display: flex;
  width: 160px;
  min-width: 0;
  height: 22px;
  align-items: center;
  gap: 4px;
  padding: 0 6px;
  font: var(--font);
  text-align: left;
}

.task-button span {
  overflow: hidden;
  white-space: nowrap;
}

.task-icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  image-rendering: pixelated;
}

/* A raised task button is the window that has focus; the rest sit flat. Held
   down the way .start-button.pressed is, and shifted the same way. */
.task-button.active {
  border-width: 1px 0 0 1px;
  font-weight: 700;
  box-shadow:
    inset 1px 1px 0 var(--dark),
    inset -1px -1px 0 var(--highlight),
    inset 2px 2px 0 var(--shadow),
    inset -2px -2px 0 var(--light);
}

.tray {
  display: flex;
  height: 22px;
  flex-shrink: 0;
  align-items: center;
  gap: 4px;
  padding: 0 6px;
  font: var(--font);
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight);
}

.tray-icon {
  width: 16px;
  height: 16px;
  image-rendering: pixelated;
}

/* A little air between the icons and the time, as the real tray has. */
.tray-clock {
  margin-left: 2px;
}

/* The icons are buttons, drawn as nothing but the icon. */
.tray-button {
  display: flex;
  width: 16px;
  height: 16px;
  padding: 0;
  margin: 0;
  border: 0;
  background: none;
  cursor: default;
  align-items: center;
}

.tray-button:focus-visible {
  outline: 1px dotted #000;
}

/* The Volume control over the speaker icon; see components/volume-control.js. */
.volume-panel {
  position: absolute;
  z-index: 3000;
  width: 84px;
  padding: 6px 0 4px;
  font: var(--font);
  color: var(--button-text);
  background: var(--face);
  box-shadow:
    inset -1px -1px 0 var(--dark),
    inset 1px 1px 0 var(--highlight),
    inset -2px -2px 0 var(--shadow),
    inset 2px 2px 0 var(--face);
}

.volume-title {
  margin-bottom: 8px;
  text-align: center;
}

.volume-body {
  display: flex;
  height: 132px;
  justify-content: center;
  gap: 4px;
}

/* The wedge beside the slider: wide at the top, a point at the bottom. */
.volume-wedge {
  width: 14px;
  height: 120px;
  margin-top: 6px;
  background: var(--highlight);
  clip-path: polygon(0 0, 100% 0, 100% 100%);
}

.volume-slider {
  padding: 5px 20px;
  border: 1px dotted var(--button-text);
}

.volume-mute {
  display: flex;
  margin: 6px 8px 0;
  align-items: center;
  gap: 4px;
}

.popup-item.bold .popup-label {
  font-weight: bold;
}

/* The keyboard focus rectangle on a menu-bar item. Buttons get theirs from
   win95.css; these are divs, so they need their own. */
.menu-item:focus-visible {
  outline: 1px dotted #000;
  outline-offset: -3px;
}

/* Test text is a caption over an edit box, with no group box round it -- which
   is what DOC.EXE draws. The caption sits where a legend used to. */
.test-text-label {
  display: block;
  margin-bottom: 3px;
}

/* Every dropdown in Characteristics is pushed to the right edge of the group,
   so the four of them line up with each other and with the check boxes past
   them rather than hanging off captions of different lengths. */
.characteristics .combo {
  margin-left: auto;
}

/* The include-in-Randomize boxes, last on their row. Pitch and Rate carry
   theirs on the caption line, since their slider is on a line of its own. */
.characteristics .w95-check {
  margin-left: 6px;
  margin-right: 2px;
}

.voice-row-tight .w95-check {
  margin-left: auto;
}

/* The column header over the include boxes. Right-aligned to the same 2px the
   boxes sit off the edge, and pulled up tight under the legend so naming the
   column costs almost no height. */
.randomize-head {
  text-align: right;
  font: var(--font);
  margin: -2px 2px 3px 0;
}

/* Reset and Randomize, on the line the group box leaves under Quality. Indented
   to the column the combos start in, so they read as part of the stack rather
   than as a footer. */
.voice-actions {
  /* Clear of the Quality row above it: these two are not part of the stack of
     settings, and butting them up against it said they were. */
  margin-top: 10px;
  padding-left: 62px;
  gap: 8px;
}

.voice-action {
  height: 20px;
  flex: none;
  padding: 0 8px;
}

/* A check box, painted the way an edit box is -- sunken, white, 13x13, which is
   what Windows 95 draws. The tick is two borders of a rotated box, the same
   trick the spin arrows use for their triangles. */
.w95-check {
  width: 13px;
  height: 13px;
  flex: none;
  appearance: none;
  margin: 0;
  padding: 0;
  background: #fff;
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight),
    inset 2px 2px 0 var(--dark),
    inset -2px -2px 0 var(--face);
}

.w95-check:disabled {
  background: var(--face);
}

.w95-check:checked::after {
  display: block;
  width: 3px;
  height: 7px;
  margin: 1px 0 0 4px;
  border: solid #000;
  border-width: 0 2px 2px 0;
  content: "";
  transform: rotate(45deg);
}

.w95-check:focus {
  outline: 1px dotted #000;
  outline-offset: 1px;
}

/* The spin arrows either side of the Pitch and Rate sliders. Narrow, because
   the room they take is room the slider loses, but full height so there is
   something to hit with a thumb. */
.spin-arrow {
  display: flex;
  width: 14px;
  height: 20px;
  flex: none;
  align-items: center;
  justify-content: center;
  padding: 0;
}

.spin-arrow i {
  display: block;
  border: 4px solid transparent;
}

.spin-arrow .left {
  border-left-width: 0;
  border-right-color: #000;
  margin-right: 2px;
}

.spin-arrow .right {
  border-right-width: 0;
  border-left-color: #000;
  margin-left: 2px;
}

/* While the on-screen keyboard is up the taskbar would otherwise float just
   above it, which is not something Windows 95 ever did on a phone. */
.screen.keyboard-open .taskbar {
  display: none;
}

/* --- Taskbar Properties ----------------------------------------------------
   Auto hide: the bar slides down to a 2px strip, which is how much of it
   Windows 95 left showing, and comes back up for the pointer or the Start
   menu (shell.js decides when). The slide is a transform so the bar keeps its
   28px box for the strip to belong to. */
.screen.taskbar-auto-hide .taskbar {
  transition: transform 0.12s ease-out;
  transform: translateY(26px);
}

.screen.taskbar-auto-hide .taskbar.shown {
  transform: none;
}

/* Not always on top: the bar goes under the windows -- above the icon layer,
   which is 1, and below the window layer, which is 2 -- until it is clicked,
   which brings it back over them until the next window is raised. */
.screen.taskbar-below .taskbar {
  z-index: 1;
}

.screen.taskbar-below.taskbar-raised .taskbar {
  z-index: 3;
}

/* --- message boxes -------------------------------------------------------- */

.message-box {
  min-width: 260px;
  max-width: 400px;
}

.message-box .window-body {
  padding: 10px 10px 8px;
}

.message-box-body {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font: var(--font);
}

/* The stock icons were 32x32 bitmaps. These are the same three shapes drawn in
   CSS, which is close enough at this size and costs nothing to ship. */
.message-box-icon {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: 50%;
  color: #fff;
  font: 700 22px "Times New Roman", serif;
  line-height: 32px;
  text-align: center;
}

.message-box-icon.error { background: #c00; }
.message-box-icon.warning { background: #e0c000; color: #000; }
.message-box-icon.info { background: #fff; color: #000080; box-shadow: inset 0 0 0 2px #000080; }

.message-box-text {
  padding-top: 4px;
  color: #000;
  line-height: 15px;
}

/* The command you are meant to copy, as a Windows 95 read-only edit: same
   21px box as the ask-box field, monospaced so a path reads as a path. */
.message-box-command {
  display: block;
  width: 100%;
  height: 21px;
  padding: 3px 3px 2px;
  margin: 4px 0;
  font-family: 'Courier New', monospace;
  cursor: text;
}

.message-box-buttons {
  display: flex;
  justify-content: center;
  /* Two buttons need to be told apart. One was always centred alone, so the
     gap never came up until Save/Cancel did. */
  gap: 8px;
  padding-top: 12px;
}

/* Windows 95 sizes every dialog button the same, default or not -- 75x23. The
   default button says so itself; the ones beside it were left at whatever their
   caption came to. */
.message-box-buttons button {
  min-width: 75px;
  height: 23px;
  padding: 0 8px;
}

/* The message box with a field in it: the caption sits above the edit rather
   than beside it, and the edit runs the width of the dialog. Written against
   .ask-box so it outranks .message-box-body's flex row. */
.ask-box {
  min-width: 300px;
}

.ask-box .message-box-body {
  display: block;
}

.ask-box-field {
  width: 100%;
  /* 21px, which is what a Windows 95 single-line edit is and what the voices
     dialog's own fields use. Left to itself the box came out 14px -- an 11px
     pixel font in a 14px box with a 2px sunken border either side has three
     pixels for a line, and the text was cut off top and bottom. */
  height: 21px;
  padding: 3px 3px 2px;
  margin-top: 8px;
}

/* --- folder windows ------------------------------------------------------- */

.folder-window {
  min-width: 240px;
}

.folder-pane {
  display: flex;
  min-height: 0;
  flex: 1;
  align-content: flex-start;
  flex-wrap: wrap;
  gap: 4px;
  padding: 4px;
  overflow-y: auto;
  background: var(--window);
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight),
    inset 2px 2px 0 var(--dark),
    inset -2px -2px 0 var(--face);
}

.folder-icon {
  display: flex;
  width: 76px;
  flex-direction: column;
  align-items: center;
  padding: 3px 0;
  background: none;
  box-shadow: none;
  color: var(--window-text);
  cursor: default;
  font: var(--font);
  line-height: 13px;
  text-align: center;
}

/* Same trap as the desktop icons: the generic pressed bevel has to be matched
   at its own specificity to be turned off. */
.folder-icon:active:not(:disabled) {
  box-shadow: none;
}

.folder-icon img {
  display: block;
  margin-bottom: 3px;
  image-rendering: pixelated;
}

.folder-icon-label {
  padding: 0 2px;
  word-break: break-word;
}

.folder-icon.selected .folder-icon-label {
  background: var(--hilight);
  color: var(--hilight-text);
}

.folder-status {
  padding: 2px 4px;
  margin-top: 2px;
  color: #000;
  font: var(--font);
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight);
}

/* --- Start menu ----------------------------------------------------------- */

.start-menu {
  position: absolute;
  bottom: 28px;
  left: 2px;
  z-index: 4;
  display: flex;
  padding: 3px;
  background: var(--menu);
  box-shadow:
    inset -1px -1px 0 var(--dark),
    inset 1px 1px 0 var(--highlight),
    inset -2px -2px 0 var(--shadow),
    inset 2px 2px 0 var(--light);
}

/* The grey banner down the left, with the name running bottom to top. */
.start-banner {
  display: flex;
  width: 23px;
  align-items: flex-start;
  padding: 5px 0;
  background: var(--title-inactive);
  color: #c0c0c0;
  font: 21px "Franklin Gothic Medium", "Arial Narrow", sans-serif;
  letter-spacing: 1px;
  white-space: nowrap;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
}

.start-banner b {
  color: #fff;
  font-weight: 700;
}

.start-list {
  min-width: 168px;
  font: var(--font);
}

.start-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 24px 4px 6px;
  color: var(--menu-text);
  cursor: default;
  white-space: nowrap;
}

.start-item:hover,
/* The keyboard highlight. Same paint as hover -- Windows 95 draws one selection,
   however you got to it. */
.start-item.cursor {
  background: var(--hilight);
  color: var(--hilight-text);
}

.start-item .disabled {
  color: var(--disabled);
  text-shadow: 1px 1px 0 var(--highlight);
}

.start-item-icon {
  width: 16px;
  height: 16px;
  image-rendering: pixelated;
}

/* The top level draws the shell's real 32x32 icons; flyouts stay at 16, which is
   what Windows 95 does and what keeps a long Programs column from towering. The
   flyout columns are `depth-0:0.` and so on, so `.depth-0` only ever matches the
   first column. */
.start-list.depth-0 {
  min-width: 190px;
}

.start-list.depth-0 > .start-item {
  gap: 8px;
  padding: 2px 24px 2px 4px;
}

.start-list.depth-0 > .start-item > .start-item-icon {
  width: 32px;
  height: 32px;
}

/* "Show small icons in Start menu": no banner, and the top level drawn like a
   flyout -- 16px icons on tight rows, a narrower column. */
.start-menu.small-icons .start-banner {
  display: none;
}

.start-menu.small-icons .start-list.depth-0 {
  min-width: 168px;
}

.start-menu.small-icons .start-list.depth-0 > .start-item {
  gap: 6px;
  padding: 4px 24px 4px 6px;
}

.start-menu.small-icons .start-list.depth-0 > .start-item > .start-item-icon {
  width: 16px;
  height: 16px;
}

/* The arrow that says a flyout is there. */
.start-item.has-flyout::after {
  position: absolute;
  right: 6px;
  border: 4px solid transparent;
  border-left-color: currentcolor;
  content: "";
}

/* A flyout hangs off its own item, level with it.
   Every flyout is in the DOM from the start -- the menu is built once so that
   hovering never rebuilds the item under the pointer -- so `.open` is what
   decides which ones are on screen. */
.start-item > .start-list {
  position: absolute;
  bottom: -3px;
  left: 100%;
  z-index: 5;
  display: none;
  padding: 3px;
  background: var(--menu);
  color: var(--menu-text);
  box-shadow:
    inset -1px -1px 0 var(--dark),
    inset 1px 1px 0 var(--highlight),
    inset -2px -2px 0 var(--shadow),
    inset 2px 2px 0 var(--light);
}

.start-item.open > .start-list {
  display: block;
}

/* An open flyout keeps its parent looking selected, so the trail back to the
   top of the menu stays visible while you are down inside it. */
.start-item.open {
  background: var(--hilight);
  color: var(--hilight-text);
}

/* --- Display Properties --------------------------------------------------- */

.dialog-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 6px;
  padding-top: 8px;
}

/* Ordinary dialog buttons are the same size as the default one; only the black
   default ring should tell them apart. */
.dialog-buttons button {
  height: 23px;
  min-width: 75px;
  padding: 0 12px;
}

/* --- BitchX -------------------------------------------------------------- */

/* A terminal in a window: the client area is the black box itself, edge to
   edge, and everything inside it is in the one fixed-pitch face. */
.bitchx-window .window-body {
  padding: 0;
  background: #000;
}

.bx-term {
  display: flex;
  flex: 1;
  flex-direction: column;
  width: 100%;
  height: 100%;
  min-height: 0;
  background: #000;
  color: #c0c0c0;
  font: 13px/15px "Lucida Console", "Consolas", "Courier New", monospace;
  cursor: text;
}

/* Inside the terminal, dragging selects text rather than moving the window;
   shell.js and window.js both exclude .bx-term. The title bar still drags. */
.bx-scroll {
  flex: 1;
  min-height: 0;
  padding: 2px 4px;
  overflow-x: hidden;
  overflow-y: auto;
  scrollbar-gutter: stable;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  user-select: text;
  -webkit-user-select: text;
}

.bx-line { min-height: 15px; }

/* Scene art at startup: the VGA ROM font (CP437, all the block characters at
   the right widths) at its native 8x16, 80 columns wide. Rows never wrap: a
   window narrower than 640px clips the right edge, as a small terminal did. */
@font-face {
  font-family: "Web437 IBM VGA";
  src: url(Web437_IBM_VGA_8x16.woff) format("woff");
  font-display: swap;
}
.bx-art {
  font: 16px/16px "Web437 IBM VGA", "Lucida Console", monospace;
  color: #aaaaaa;
  white-space: pre;
  overflow: hidden;
  max-width: 100%;
  margin: 0 -4px 4px;
  padding: 0 4px;
  overflow-wrap: normal;
  -webkit-font-smoothing: none;
}
.bx-art-row { height: 16px; white-space: pre; }
.bx-playback { color: #808080; }
.bx-b { color: #ffffff; font-weight: bold; }
.bx-nick { color: #55ffff; }
/* A word marked for the echo. Bold and nothing else -- it keeps the colour of
   the line it is in, because it is a word somebody said and not a different
   kind of line. The stars that marked it are not drawn: knowing the trick is
   the trick. */
.bx-echo { font-weight: bold; }
.bx-me { color: #ffff55; }
.bx-act { color: #ff55ff; }
.bx-join { color: #55ff55; font-weight: bold; }
.bx-part { color: #ff5555; font-weight: bold; }
.bx-sys { color: #55ffff; font-weight: bold; }
.bx-notice { color: #ff55ff; }
.bx-err { color: #ff5555; }
.bx-host { color: #808080; }
.bx-playback .bx-nick,
.bx-playback .bx-me,
.bx-playback .bx-b { color: #a0a0a0; }

/* BitchX's status bar: reverse video on the classic blue, the clock and the
   nick on the left, the version on the right, never wrapping. */
.bx-status {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 0 4px;
  overflow: hidden;
  background: #0000aa;
  color: #ffffff;
  white-space: nowrap;
}

.bx-prompt {
  display: flex;
  align-items: center;
  padding: 0 4px;
  white-space: pre;
}

/* The input line is part of the terminal, not a Windows text box: no bevel,
   no white field, the same face as everything above it. */
.bitchx-window input.bx-input {
  flex: 1;
  min-width: 0;
  padding: 0;
  margin: 0;
  border: 0;
  border-radius: 0;
  outline: 0;
  box-shadow: none;
  background: transparent;
  color: #ffffff;
  font: inherit;
  caret-color: #ffffff;
}

.bitchx-window input.bx-input:focus {
  outline: 0;
  box-shadow: none;
}



/* The boot and shutdown screens, which are the machine and not the desktop:
   full-bleed black with the 320x400 image stretched back to the 4:3 it was
   always displayed at. Below the boot-error box's z-index on purpose -- if the
   app cannot start, the reason has to be readable over this. */
#boot-screen {
  position: fixed;
  inset: 0;
  z-index: 9000;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
}

#boot-screen[hidden] {
  display: none;
}

/* The blue screen: 80x25 VGA text cells at 8x16 = 640x400, laid into the same
   4:3 box as the boot images. See showBlueScreen in js/boot-screen.js. */
.blue-screen {
  box-sizing: border-box;
  aspect-ratio: 4 / 3;
  width: 100%;
  height: auto;
  max-height: 100%;
  max-width: min(calc(100vh * 4 / 3), var(--boot-w, 100vw));
  margin: 0;
  padding: 0;
  background: #0000aa;
  color: #fff;
  /* 80 columns of an 8-wide glyph fill the box's width and 25 rows of it fill
     the height, whichever of the two the viewport limits, so the text gets the
     same stretch the boot images do. Glyph width is half the font size. The
     blue runs on to the edges of the window; the text does not. */
  font-family: 'Web437 IBM VGA', monospace;
  /* Set from the box's own size by showBlueScreen; this is the first frame. */
  font-size: min(2.5vw, 3.3333vh);
  line-height: min(3vw, 4vh);
  white-space: pre;
  overflow: hidden;
}

#boot-screen.crashed {
  background: #0000aa;
  cursor: none;
}

.blue-screen-title {
  background: #aaa;
  color: #0000aa;
}

.blue-screen-cursor {
  color: #aaa;
  animation: blue-screen-blink 1s steps(1) infinite;
}

@keyframes blue-screen-blink {
  50% { visibility: hidden; }
}

.boot-image {
  /* 320x400 stored, 640x480 shown: the aspect the boot loader stretched it to.
     Letterboxed rather than cropped, so nothing of it is ever cut off; the
     black of #boot-screen runs on to the edges. (Filling the window was tried
     on 2026-09-20 and looked wrong.) */
  aspect-ratio: 4 / 3;
  width: 100%;
  height: auto;
  max-height: 100%;
  /* --boot-w is the desktop area from Display Properties; see setBootArea. */
  max-width: min(calc(100vh * 4 / 3), var(--boot-w, 100vw));
  /* Chunky by design: it was 640x480 on a CRT, not a smooth upscale. */
  image-rendering: pixelated;
}

/* --- Internet Explorer ---------------------------------------------------- */

/* IE 3.0's frame, in the guest's own pixels (assets/ie/, out of SHDOCVW.DLL;
   see tools/win95/extract_ie_assets.py). The chrome stacks over a client that
   is an <iframe>, which takes whatever height the rest leaves. */
.ie-window .window-body {
  display: flex;
  flex-direction: column;
  padding: 0;
}

.ie-menu {
  padding: 0 2px;
}

/* The toolbar sits on bitmap 259's swirl. Each button is a 30x20 face off the
   420x20 strip with its caption under it, the way IE 3 laid them out. */
.ie-toolbar {
  display: flex;
  align-items: center;
  height: 38px;
  padding: 0 2px;
  border-top: 1px solid var(--button-highlight, #fff);
  border-bottom: 1px solid var(--button-shadow, #808080);
  background: #c0c0c0 url(/assets/ie/texture.png) 0 0 repeat;
  user-select: none;
}

.ie-tool {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  min-width: 40px;
  padding: 1px 3px;
  border: 1px solid transparent;
  background: transparent;
  box-shadow: none;
  font: var(--font);
  color: #000;
  cursor: default;
}

.ie-tool:hover:not(:disabled) {
  border-color: #fff #808080 #808080 #fff;
}

.ie-tool:active:not(:disabled) {
  border-color: #808080 #fff #fff #808080;
}

.ie-tool:disabled {
  color: #808080;
}

.ie-tool:focus-visible {
  outline: 1px dotted #000;
  outline-offset: -3px;
}

.ie-glyph {
  display: block;
  width: 30px;
  height: 20px;
  background: url(/assets/ie/toolbar.png) 0 0 no-repeat;
  image-rendering: pixelated;
}

.ie-tool:hover:not(:disabled) .ie-glyph {
  background-image: url(/assets/ie/toolbar-hot.png);
}

/* The grey faces were drawn for a grey toolbar; a button that does nothing
   fades them the way Windows fades a disabled glyph. */
.ie-tool:disabled .ie-glyph {
  opacity: 0.5;
}

.ie-caption {
  line-height: 12px;
}

/* Bitmap 267: 46 frames of the 'e', stacked; the window steps
   background-position-y through them while a page is on its way. */
.ie-throbber {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  margin-left: auto;
  background: url(/assets/ie/throbber.png) 0 0 no-repeat;
  image-rendering: pixelated;
}

.ie-address-bar {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 2px 4px;
  border-bottom: 1px solid var(--button-shadow, #808080);
  font: var(--font);
}

.ie-address-label {
  flex-shrink: 0;
}

.ie-address {
  flex: 1;
  min-width: 0;
  height: 21px;
  padding: 2px 3px;
  font: var(--font);
}

.ie-client {
  flex: 1;
  min-height: 0;
  background: #fff;
}

.ie-page {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

.ie-status {
  display: flex;
  padding: 2px;
  font: var(--font);
}

.ie-status-text {
  flex: 1;
  height: 16px;
  padding: 0 3px;
  overflow: hidden;
  border: 1px solid;
  border-color: #808080 #fff #fff #808080;
  line-height: 16px;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* The shell drags and resizes by listening for pointermove on window (see
   shell.js); an iframe under the pointer would swallow the moves and stall the
   drag the moment the pointer crossed it. The class is on the screen while a
   shell window is being dragged, and on the body for a modal. */
.dragging-window iframe {
  pointer-events: none;
}

/* --- popup menus ------------------------------------------------------------
 * The desktop's right-click menu and a window's system menu
 * (js/components/popup-menu.js). Same panel as a menu bar's list; the entries
 * are divs, so the pressed-button rule never sees them.
 */

.popup-menu {
  position: absolute;
  top: auto;
  z-index: 3000;
  min-width: 150px;
}

.popup-list {
  position: relative;
}

.popup-item {
  position: relative;
  display: flex;
  align-items: center;
  /* Room on the left for a check mark, on the right for a flyout arrow. */
  padding: 3px 24px 3px 20px;
  color: var(--menu-text);
  cursor: default;
  white-space: nowrap;
}

.popup-item:hover:not(.disabled),
.popup-item.open {
  background: var(--hilight);
  color: var(--hilight-text);
}

.popup-item.disabled {
  color: var(--disabled);
  text-shadow: 1px 1px 0 var(--highlight);
}

.popup-label {
  flex: 1;
}

.popup-accel {
  margin-left: 16px;
}

.popup-item.checked::before {
  position: absolute;
  top: 4px;
  left: 7px;
  width: 3px;
  height: 7px;
  border: solid currentcolor;
  border-width: 0 2px 2px 0;
  content: "";
  transform: rotate(45deg);
}

.popup-item.has-flyout::after {
  position: absolute;
  top: 50%;
  right: 6px;
  border: 4px solid transparent;
  border-left-color: currentcolor;
  content: "";
  transform: translateY(-50%);
}

/* A submenu hangs off its entry's right edge, beside its top. Hidden until the
   entry opens it. */
.popup-item > .popup-list {
  position: absolute;
  top: -3px;
  left: 100%;
  z-index: 1;
  display: none;
  min-width: 130px;
  padding: 2px;
  background: var(--menu);
  color: var(--menu-text);
  box-shadow:
    inset -1px -1px 0 var(--dark),
    inset 1px 1px 0 var(--highlight),
    inset -2px -2px 0 var(--shadow),
    inset 2px 2px 0 var(--light);
}

.popup-item.open > .popup-list {
  display: block;
}

.title-icon {
  cursor: default;
}

/* --- radio buttons, spinners, list boxes -------------------------------------
 * The rest of the Windows 95 control set Display Properties needed
 * (js/components/win95-controls.js).
 */

.radio-group {
  display: flex;
  gap: 12px;
  align-items: center;
}

.radio-label {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--button-text);
  font: var(--font);
}

.radio-label.disabled {
  color: var(--disabled);
  text-shadow: 1px 1px 0 var(--highlight);
}

/* 12x12: a sunken white circle with a black dot when it is on. The rings are
   the check box's bevels bent round. */
.w95-radio {
  width: 12px;
  height: 12px;
  flex: none;
  appearance: none;
  margin: 0;
  border-radius: 50%;
  background: var(--window);
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight),
    inset 2px 2px 0 var(--dark),
    inset -2px -2px 0 var(--face);
}

.w95-radio:disabled {
  background: var(--face);
}

.w95-radio:checked::after {
  display: block;
  width: 4px;
  height: 4px;
  margin: 4px 0 0 4px;
  border-radius: 50%;
  background: var(--window-text);
  content: "";
}

.w95-radio:disabled:checked::after {
  background: var(--disabled);
}

.w95-radio:focus {
  outline: 1px dotted var(--window-text);
  outline-offset: 1px;
}

.spinner-box {
  display: inline-flex;
  height: 20px;
  align-items: stretch;
}

.spinner-field {
  box-sizing: border-box;
  height: 20px;
  padding: 2px 3px;
  border: 0;
  border-radius: 0;
  background: var(--window);
  color: var(--window-text);
  font: var(--font);
  text-align: left;
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight),
    inset 2px 2px 0 var(--dark),
    inset -2px -2px 0 var(--face);
}

.spinner-field:disabled {
  background: var(--face);
  color: var(--disabled);
}

.spinner-box .spinner {
  height: 20px;
}

.spinner-box .spinner button {
  height: 10px;
}

.spinner-box .spinner button:disabled::before {
  border-top-color: var(--disabled);
  border-bottom-color: var(--disabled);
}

.spinner-box .spinner button.up:disabled::before {
  border-top-color: transparent;
}

.spinner-box .spinner button.down:disabled::before {
  border-bottom-color: transparent;
}

.list-box {
  box-sizing: border-box;
  padding: 2px;
  overflow-y: auto;
  background: var(--window);
  color: var(--window-text);
  font: var(--font);
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight),
    inset 2px 2px 0 var(--dark),
    inset -2px -2px 0 var(--face);
}

.list-box:focus {
  outline: none;
}

.list-box-item {
  padding: 1px 3px;
  cursor: default;
  white-space: nowrap;
}

.list-box-item.selected {
  background: var(--hilight);
  color: var(--hilight-text);
}

.list-box:focus .list-box-item.selected {
  outline: 1px dotted var(--hilight-text);
  outline-offset: -1px;
}

/* --- a saver's own Settings dialog ---------------------------------------- */

.saver-setup-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  font: var(--font);
}

.saver-setup-body fieldset {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 6px 8px 8px;
}

.saver-setup-body legend {
  font-weight: 400;
}

/* A Min-to-Max trackbar with its two end labels, as every one of these
   dialogs drew it. */
.saver-slider {
  display: flex;
  align-items: center;
  gap: 6px;
  justify-content: space-between;
}

/* --- the screen saver ---------------------------------------------------- */

.screen-saver {
  position: absolute;
  inset: 0;
  z-index: 3000;
  background: #000;
  cursor: none;
}

.screen-saver-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* --- Display Properties ----------------------------------------------------
 * A four-page property sheet (js/components/modals/display-properties.js).
 * 404 wide, as the real one was: the frame, then 8px of page margin.
 */

/* One width for every tab -- Settings' -- rather than each page's own. A
   property sheet is not modal in Windows 95: it is a window of its own that
   stays up while you use the desktop behind it, so it lives in the shell
   rather than over an overlay, and opens without the ding a modal makes.
   Taskbar Properties is built the same way (taskbar-properties.js). */
.display-properties,
.taskbar-properties {
  width: 404px;
}

/* Taskbar Properties' preview: EXPLORER.EXE's own bitmaps (out of the guest by
   tools/win95/extract_taskbar_assets.py) layered as the dialog layered them --
   the small-icon menu and the hidden bar's outline are in the base picture, and
   the large-icon menu, the bar and a clockless tray paint over it. */
.taskbar-preview {
  position: relative;
  width: 274px;
  height: 130px;
  margin: 12px auto 4px;
  background: url("/assets/taskbar/preview.png");
  image-rendering: pixelated;
}

.taskbar-preview > img {
  position: absolute;
  image-rendering: pixelated;
}

.taskbar-preview .preview-menu {
  top: 0;
  left: 2px;
}

.taskbar-preview .preview-bar {
  bottom: 0;
  left: 2px;
}

.taskbar-preview .preview-tray {
  right: 6px;
  bottom: 4px;
}

.taskbar-body {
  min-height: 300px;
}

.taskbar-options {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 4px 30px;
}

.tab-strip {
  position: relative;
  z-index: 2;
  display: flex;
  gap: 0;
  padding: 0 2px;
  /* The strip hangs 2px into the page below it. Without this the page's top
     border ran unbroken beneath both tabs, so the selected one read as a button
     floating above the page rather than as the front of it. */
  margin-bottom: -2px;
  font: var(--font);
}

/* An unselected tab sits a pixel low and stops short of the page. */
.tab-button {
  position: relative;
  padding: 3px 7px 2px;
  margin-top: 2px;
  /* Unselected tabs pull back out of the page again; only the selected one is
     allowed to cover its edge. */
  margin-bottom: 2px;
  font-weight: 400;
  box-shadow:
    inset 1px 1px 0 var(--highlight),
    inset -1px 0 0 var(--dark),
    inset 2px 1px 0 var(--light),
    inset -2px 0 0 var(--shadow);
}

/* The selected one is taller, and paints over the page's top edge so the two
   read as one surface -- which is the only thing that says which tab you are
   looking at. */
.tab-button.selected {
  z-index: 2;
  padding: 3px 9px 5px;
  margin-top: 0;
  margin-bottom: 0;
  /* Opaque, so it paints out the page's top edge where the two meet. */
  background: var(--face);
}

.display-body {
  position: relative;
  z-index: 1;
  min-height: 352px;
  padding: 8px;
  box-shadow:
    inset -1px -1px 0 var(--dark),
    inset 1px 1px 0 var(--highlight),
    inset -2px -2px 0 var(--shadow),
    inset 2px 2px 0 var(--light);
}

.display-tab {
  display: flex;
  flex-direction: column;
  gap: 8px;
  color: var(--button-text);
  font: var(--font);
}

/* The monitor: DESKCP16.DLL's own 184x170 bitmap (resource 1250, out of the
   guest by tools/win95/extract_display_assets.py), with a 152x104 screen at
   16,17 that each page paints its own preview into. Settings leaves the
   bitmap's built-in desktop showing, as the real page did. */
.display-monitor {
  position: relative;
  width: 184px;
  height: 170px;
  margin: 2px auto 0;
  background: url("/assets/display/monitor.png") no-repeat;
  image-rendering: pixelated;
}

.display-glass {
  position: absolute;
  top: 17px;
  left: 16px;
  width: 152px;
  height: 104px;
  overflow: hidden;
  background: var(--desktop);
}

.display-glass canvas {
  display: block;
  width: 152px;
  height: 104px;
}

.display-row {
  display: flex;
  gap: 8px;
  align-items: flex-start;
}

.display-row > fieldset {
  flex: 1;
  min-width: 0;
}

.display-tab fieldset {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 6px 8px 8px;
}

.display-tab legend {
  font-weight: 400;
}

.field-label {
  color: var(--button-text);
}

.field-label.disabled,
.display-tab .disabled-text {
  color: var(--disabled);
  text-shadow: 1px 1px 0 var(--highlight);
}

.display-tab .control-row {
  display: flex;
  align-items: center;
  gap: 6px;
}

.display-tab .control-row.spread {
  justify-content: space-between;
}

.display-tab .control-row.right {
  justify-content: flex-end;
}

/* Options -> FX. A short property sheet, so it borrows .display-tab's spacing
   rather than inventing its own; only the three-column knob row is new. */
.fx-dialog {
  width: 300px;
  padding: 8px;
}

.fx-row {
  padding: 3px 4px;
}

.fx-name {
  width: 62px;
}

/* Right-aligned and fixed width so the numbers do not shuffle the slider
   sideways as they gain and lose a digit under the pointer. */
.fx-value {
  width: 58px;
  text-align: right;
  color: var(--button-text);
}

.fx-hint {
  padding: 6px 4px 2px;
}

.display-tab button.dialog-button {
  min-width: 70px;
  height: 23px;
  padding: 0 8px;
}

/* --- Appearance: a desktop in miniature, painted in the draft's colours. --- */

.appearance-preview {
  position: relative;
  height: 178px;
  overflow: hidden;
  background: var(--desktop);
  color: var(--window-text);
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight),
    inset 2px 2px 0 var(--dark),
    inset -2px -2px 0 var(--face);
}

.appearance-preview .window {
  position: absolute;
  box-sizing: border-box;
}

.appearance-preview .preview-inactive {
  top: 10px;
  left: 14px;
  width: 268px;
  height: 46px;
}

.appearance-preview .preview-active {
  top: 32px;
  left: 30px;
  width: 268px;
  height: 128px;
}

.appearance-preview .preview-message {
  top: 108px;
  left: 60px;
  width: 168px;
  height: 66px;
}

.appearance-preview .menu-bar {
  height: 18px;
  padding: 0 2px;
}

.appearance-preview .menu-item.disabled {
  color: var(--disabled);
  text-shadow: 1px 1px 0 var(--highlight);
}

.appearance-preview .menu-item.selected {
  background: var(--hilight);
  color: var(--hilight-text);
}

.appearance-preview .preview-client {
  height: 62px;
  padding: 2px 4px;
  overflow-y: scroll;
  background: var(--window);
  color: var(--window-text);
  font: 700 var(--font);
  box-shadow:
    inset 1px 1px 0 var(--shadow),
    inset -1px -1px 0 var(--highlight),
    inset 2px 2px 0 var(--dark),
    inset -2px -2px 0 var(--face);
}

.appearance-preview .preview-message-text {
  padding: 6px 6px 4px;
}

.appearance-preview .preview-message button {
  display: block;
  width: 70px;
  height: 22px;
  margin: 0 auto;
}

.colour-picker {
  position: relative;
  display: inline-flex;
  height: 21px;
  align-items: center;
  gap: 2px;
  padding: 2px;
  font: var(--font);
}

.colour-picker .swatch {
  width: 22px;
  height: 13px;
  box-shadow: inset 0 0 0 1px var(--dark);
}

.colour-picker .combo-button {
  width: 14px;
  height: 100%;
}

.colour-picker.disabled .swatch {
  background: var(--face) !important;
}

.colour-picker-panel {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 300;
  display: grid;
  grid-template-columns: repeat(5, 18px);
  gap: 2px;
  padding: 4px;
  background: var(--face);
  box-shadow:
    inset -1px -1px 0 var(--dark),
    inset 1px 1px 0 var(--highlight),
    inset -2px -2px 0 var(--shadow),
    inset 2px 2px 0 var(--light);
}

.display-swatch {
  width: 18px;
  height: 14px;
  box-shadow: inset 0 0 0 1px var(--dark);
}

.display-swatch.selected {
  outline: 1px dotted var(--window-text);
  outline-offset: 1px;
}

.combo.disabled .combo-field {
  background: var(--face);
}

.appearance-grid {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 4px 10px;
  align-items: end;
}

.appearance-grid .stack {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

/* --- Settings ---------------------------------------------------------------- */

.palette-bar {
  display: block;
  width: 100%;
  height: 16px;
  image-rendering: pixelated;
  object-fit: fill;
}

.desktop-area {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 4px 6px;
  align-items: center;
}

.desktop-area .area-label {
  grid-column: 1 / -1;
  text-align: center;
}

.display-note {
  color: var(--button-text);
}

