/* Generated by design/build.py from design/kit/. Do not edit by hand. */

/* ---------------------------------------------------------------------------
   NeXTSTEP Workspace

   The interface of the admin tool, built to look like the machine it
   configures. Machines and disks are things you pick up, not values you type.

   Nothing here is redrawn. The icons are the files out of a NeXTSTEP 3.3 disk
   image, and the window buttons and dock marks are cut pixel for pixel from a
   screenshot of the running system, because NeXTSTEP drew those in PostScript
   and they exist as no file. ../../design/extract.py fetches both.

   Every colour and raster below is read off that same screenshot at its own
   1120 by 832:

     Arbeitsfläche        #555577
     Titelbalken, aktiv   #000000, sonst #555555, Schrift weiss und fett
     Fensterfläche        #aaaaaa
     Fensterrahmen        1 px #000000
     Menüraster           20 px: 1 px #ffffff, 17 px #aaaaaa, #555555, #000000
     Dock-Kachel          64 px, diagonal von #aaaaaa nach #555555
     Rollbalken-Rinne     Schachbrett aus #555555 und #aaaaaa, 1 px
     versenkte Fläche     aussen #555555, innen #000000 oben und links, weiss sonst
     erhabene Fläche      weiss oben und links, aussen #000000, innen #555555
     Auswahl              eine weisse Flaeche hinter Icon und Namen, 5 px Radius
   --------------------------------------------------------------------------- */

:root {
  /* The four values a mono NeXT could draw. Everything structural uses these. */
  --white: #ffffff;
  --light: #aaaaaa;
  --dark:  #555555;
  --black: #000000;

  /* Measured off the screenshot: not a grey, the dark value with more blue. */
  --desktop: #555577;

  --live: #2f7d32;

  --icon: 48px;          /* what NeXTSTEP drew an icon at, and these are those */
  --tile: 128px;         /* the tile an icon and its name sit on, measured off
                            the original's contents band, where two icons stand
                            128 apart */
  --shelf-tile: 96px;    /* the same on a shelf, which the original packs
                            tighter: 96 between the two folders on its own */
  --slot-w: 66px;        /* hilite.tiff is 66 by 52, so the slot is too */
  --slot-h: 52px;
  --pane-pad: 8px;       /* between a window's frame and what is in it */
  --reading-gap: 5px;    /* between one reading and the next, anywhere */
  --portrait: 112px;     /* the picture beside a panel's readings */
  --portrait-gap: 14px;  /* between that picture and those readings */
  --band-gap: 6px;       /* between two framed bands of the File Viewer, which
                            is what the original leaves between its boxes */
  --slot-top: 2px;       /* air above the icon, inside the mark */
  --name-air: 3px;       /* air above and below a name, inside the mark */

  --font: Helvetica, "Helvetica Neue", Arial, sans-serif;
  --mono: Menlo, "DejaVu Sans Mono", monospace;
  /* What the interface is set at. NeXTSTEP put the names under its icons at
     this size too, and the menu entries and the window titles with them:
     measured off the running system, all of them have a capital 9 pixels
     tall. */
  --text-size: 12px;
  /* The one thing it set smaller, whose capital is 7. */
  --status-size: 10px;

  --row: 20px;           /* the menu raster, and the height of one entry */
  --title-height: 21px;  /* 19 px of bar and 2 px of the shadow under it */
  --dock-width: 68px;    /* 64 of tile and the two-pixel bevel on each side */
  --resize-bar: 8px;     /* 1 shadow, 1 light, 6 face */
  --resize-end: 29px;    /* where the upright divider sits, from either end */
  --scroller: 18px;      /* 16 of trough and the line on either side */

  /* The floor every window shares. A window that needs more says so on itself,
     and the script reads it from there. */
  --win-min-w: 280px;
  --win-min-h: 160px;

  /* PARTS: the pictures, which build.py bakes into the mockup as data URIs */
  /* As files rather than as data URIs here. A service that
     serves files should serve these as files too, so the browser caches
     them and this stylesheet stays readable. ../../design/extract.py says
     where the icons came from, and ../../design/bootpicture.py where the two
     machines came from. */
  --Workspace: url("parts/Workspace.png");
  --defaultUnixIcon: url("parts/defaultUnixIcon.png");
  --defaultAppIcon: url("parts/defaultAppIcon.png");
  --defaultIcon: url("parts/defaultIcon.png");
  --dock-marks: url("parts/dock-marks.png");
  --floppy: url("parts/floppy.png");
  --folder: url("parts/folder.png");
  --Grab: url("parts/Grab.png");
  --home: url("parts/home.png");
  --Localization: url("parts/Localization.png");
  --Monitor: url("parts/Monitor.png");
  /* NeXTSTEP's own selection was a white rounded shape, and this is the corner
     it drew, read off hilite.tiff: white begins five pixels in on the first
     row and reaches the edge on the sixth. */
  --hilite-radius: 5px;

  /* The one value outside the four above. It marks the configuration the
     emulator is actually running, which is a thing NeXTSTEP had no idea of, so
     there is no original to take a colour from. A shade under the window's own
     grey, because it has to read as a state and not as a selection. */
  --in-force: #999999;

  /* The same mark with half of it missing, for a configuration that is written
     down but has not been through a boot. NeXTSTEP drew a checkerboard of two
     greys wherever something was partial, and at this size it reads as a
     texture rather than as a pattern. */
  --dither: repeating-conic-gradient(var(--in-force) 0% 25%, var(--light) 0% 50%);
  --dither-size: 2px;
  --net: url("parts/net.png");
  --nextcube: url("parts/nextcube.png");
  --nextstation: url("parts/nextstation.png");
  --optical: url("parts/optical.png");
  --png: url("parts/png.png");
  --Preferences: url("parts/Preferences.png");
  --root: url("parts/root.png");
  --scsi: url("parts/scsi.png");
  --Terminal: url("parts/Terminal.png");
  --tiff: url("parts/tiff.png");
  --trash: url("parts/trash.png");
  --wbtn-close: url("parts/wbtn-close.png");
  --wbtn-mini: url("parts/wbtn-mini.png");
  --winchester: url("parts/winchester.png");
}

* { box-sizing: border-box; }

body {
  margin: 0;
  height: 100vh;
  background: var(--desktop);
  font: var(--text-size)/1.4 var(--font);
  color: var(--black);
  overflow: hidden;
  /* A workspace is not a document. The prefixed form is written out because
     Safari has only understood the plain one since version 17, and nextstep.js
     refuses the gesture outright for the browsers that begin a selection
     anyway. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-font-smoothing: none;
}

/* A custom element carries its own display, and that beats what the hidden
   attribute means, so every one of them would need its own rule saying the
   obvious. This says it once for all of them. */
/* Important, because every rule below that gives something a display of its
   own would otherwise beat this one on nothing but being later in the file.
   That is what happened to the info window's file line: it carried hidden,
   .readings said grid four hundred lines further down, and an empty row sat
   there with a field squashed to a hairline. The attribute is the browser's
   own promise that a thing is not shown, and nothing here has any business
   overruling it by accident. */
[hidden] { display: none !important; }

/* Every picture in here is a bitmap at its own size, so it is never smoothed. */
.art {
  background-position: center;
  background-repeat: no-repeat;
  image-rendering: pixelated;
}

/* A rectangle on its way from one place to another, which is how the desk
   says that what was in there is now in here. An outline and nothing else:
   filled, it would be a black flash across half the window. */
.zooming {
  position: fixed;
  z-index: 85;
  pointer-events: none;
  border: 1px solid var(--black);
}

/* --- the two edges everything in this interface is built from ------------ */

/* NeXTSTEP lit its surfaces from the top left, and every edge in the system is
   one of these two. Both are measured off the original, and the count of lines
   differs by side: the lit side of a sunken surface carries two, its shadowed
   side one, and a raised surface is that turned around.
   Sunken: outer #555555, inner #000000 on top and left, #ffffff below and right.
   Raised: #ffffff on top and left, outer #000000, inner #555555 below and right. */
.sunken,
nx-portrait,
nx-field,
nx-scroller {
  border: 1px solid;
  border-color: var(--dark) var(--white) var(--white) var(--dark);
  box-shadow: inset 1px 1px 0 var(--black);
}

.raised,
button,
nx-tile {
  border: 1px solid;
  border-color: var(--white) var(--black) var(--black) var(--white);
  box-shadow: inset -1px -1px 0 var(--dark);
}

/* --- nx-window ---------------------------------------------------------- */

nx-window {
  position: absolute;
  border: 1px solid var(--black);
  background: var(--light);
  display: flex;
  flex-direction: column;
  min-width: var(--win-min-w);
  min-height: var(--win-min-h);
}

/* The bar is 19 px tall and sits on a one-pixel grey ledge, with two pixels of
   shadow beneath it. Drawing all three as one gradient keeps the three numbers
   in one place instead of spread over a border, a margin and a pseudo-element. */
nx-window .titlebar {
  height: var(--title-height);
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 0 2px 2px;
  margin: 1px 1px 0;
  color: var(--white);
  cursor: move;
  background: linear-gradient(to bottom,
    var(--black) 0 19px, var(--dark) 19px 20px, var(--black) 20px 21px);
}
/* The bar is 19 rows whether it is active or not, and the two rows under it
   are the shadow. On an active bar the first of those reads as grey against
   black; on an inactive one it would be the bar's own colour and would simply
   make the bar look a row taller, so it is black here. Measured off the real
   File Viewer, whose grey bar ends after 19 rows with two black ones under it. */
nx-window[inactive] .titlebar {
  background: linear-gradient(to bottom,
    var(--dark) 0 19px, var(--black) 19px 21px);
}

/* Both window buttons are the originals, 15 by 15, bevel and glyph in one
   bitmap. The miniaturise one is a little window, not a filled square. */
nx-window .wbtn {
  width: 15px; height: 15px;
  flex: 0 0 auto;
  cursor: default;
}
nx-window .wbtn.mini { background-image: var(--wbtn-mini); }
nx-window .wbtn.close { background-image: var(--wbtn-close); }

nx-window .title { flex: 1; text-align: center; font-weight: bold; }

/* Most windows hold their content clear of the frame. A terminal does not: its
   text view runs to the edge on every side, which is what flush says. */
/* Eight pixels of the window's own grey between the frame and what is in it,
   which is what the original leaves: one row of border, then eight of face,
   then the content. Measured in the File Viewer and again in the Preferences
   panel, so it is what a window does rather than what one window does. */
nx-window .pane {
  flex: 1 1 auto;
  padding: var(--pane-pad);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
nx-window[flush] .pane { padding: 0; }

/* Except at the foot of a window that can be resized, where the content runs
   straight into the bar. Measured in the File Viewer: its contents reach row
   534, the bar's shadow is row 535, and there is no grey between them. A
   window with no bar keeps its eight, which the Preferences panel does. */
nx-window:not([fixed]) .pane { padding-bottom: 0; }

/* A resizable window ends in a bar across its whole width, not in a corner.
   Measured off the original: one row of shadow, one of light, then six of
   face, with an upright divider 29 pixels in from either end. */
nx-window .resizer {
  height: var(--resize-bar);
  flex: 0 0 auto;
  display: flex;
  cursor: ns-resize;
  background: linear-gradient(to bottom,
    var(--dark) 0 1px, var(--white) 1px 2px, var(--light) 2px 100%);
}

/* The two ends take the corner, so a drag there changes width and height at
   once. Each carries the same two-line divider on its inner edge. */
nx-window .resizer .end {
  width: var(--resize-end);
  cursor: nesw-resize;
}
nx-window .resizer .end:first-child {
  border-right: 1px solid var(--white);
  box-shadow: inset -2px 0 0 var(--dark);
}
nx-window .resizer .end:last-child {
  cursor: nwse-resize;
  border-left: 1px solid var(--dark);
  box-shadow: inset 2px 0 0 var(--white);
}
nx-window .resizer .middle { flex: 1; }

/* --- nx-menu ------------------------------------------------------------ */

/* The menu sits in a black outline, and every entry carries its own light on
   top and its own shadow underneath. That three-line divider is the whole
   grammar of the look, so it is written once and reused. */
nx-menu {
  position: absolute;
  display: block;
  /* As wide as its longest entry, and never narrower than the 148 the
     original's own menu measures. NeXTSTEP's menus were the width of what
     was in them: the Workspace's holds ten entries and Preferences' five,
     and neither wraps. An entry that wraps writes its second line over the
     next one, which is what a fixed width does the first time a wording
     runs long. */
  min-width: 148px;
  width: max-content;
  z-index: 60;
  border: 1px solid var(--black);
  background: var(--light);
}

nx-menu .title,
nx-menu-item {
  height: var(--row);
  display: flex;
  align-items: center;
  padding: 0 6px;
}

nx-menu .title {
  background: var(--black);
  color: var(--white);
  font-weight: bold;
  cursor: move;
  /* One line, whatever it says. The bar is one row tall, so a title that
     wraps writes its second line over the first entry, and the name of a
     thing is exactly what runs long: a file's name is as long as somebody
     made it. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
  line-height: var(--row);
  padding: 0 6px;
  /* And it has no say in how wide the menu is. A title is the name of a
     thing, which can be any length, whilst the entries are what the menu is
     for. Zero takes it out of the width the entries decide, and the minimum
     puts it back across the whole of it afterwards. */
  width: 0;
  min-width: 100%;
}

nx-menu-item {
  /* Whatever an entry carries stays together on the left. The shortcut letter
     takes itself to the right edge, which is what its own margin does. */
  justify-content: flex-start;
  gap: 0;
  /* One line each. An entry is one row tall, so a wrapped one writes over the
     entry below it. */
  white-space: nowrap;
  border-top: 1px solid var(--white);
  /* One pixel of black at the very bottom, one of grey above it. The later
     shadow is painted under the earlier one, so black wins the last row. */
  box-shadow: inset 0 -2px 0 var(--dark), inset 0 -1px 0 var(--black);
  cursor: default;
}
nx-menu-item:last-child,
nx-menu-item[last] { box-shadow: none; }

/* The entry under the pointer takes the white a selection takes elsewhere in
   here, and holding the mouse on it inverts it. Two steps rather than one, so
   the pointer says where it is and the press says what is about to happen.

   NeXTSTEP itself did only the second, and lit nothing on hover. It also had
   one pointer and no trackpad. */
nx-menu-item:hover { background: var(--white); }
nx-menu-item:active { background: var(--black); color: var(--white); }

/* An entry that would do nothing. The lettering greys and the entry stays in
   place, so the menu keeps its shape and a reader sees that the thing exists
   and is not available now. Nothing lights under the pointer, because nothing
   would happen. */
nx-menu-item[disabled] { color: var(--dark); }
nx-menu-item[disabled]:hover,
nx-menu-item[disabled]:active { background: none; color: var(--dark); }

/* The place a picture goes, at the size NeXTSTEP put beside a line of menu
   text rather than at icon size. Every entry in a menu that has pictures keeps
   the place, so the words all begin at the same column; a menu where nothing
   has one keeps no column at all. */
nx-menu-item .mark { display: none; }
nx-menu[with-icons] nx-menu-item .mark {
  display: block;
  width: 16px; height: 16px;
  background-size: contain;
  margin-right: 6px;
  flex: 0 0 auto;
}

/* A menu that answers a right click stands above everything else. Its title
   says which thing it is about, because unlike the main menu it appears over
   whatever was clicked and then goes away again. */
nx-menu[context] {
  z-index: 80;
}
nx-menu[context] .title { cursor: default; }

/* Pushed to the right edge on its own, so an entry carrying a picture keeps
   the picture and its words together on the left. */
nx-menu-item .key { font-size: 11px; margin-left: auto; }

/* --- nx-dock ------------------------------------------------------------ */

/* A column of slots, each one tile high, running the height of the screen.
   The tiles touch, because the original leaves no gap and the column has no
   padding either, so the dock is one strip against the right edge.

   A grid rather than a stack, because a person arranges this column and what
   they arrange includes the empty slots: a tile sits on the row it was put
   on, and a row nobody put anything on stays open. A stack would close every
   gap the moment it was made. */
nx-dock {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: var(--dock-width);
  display: grid;
  grid-auto-rows: var(--dock-width);
  align-content: start;
  z-index: 50;
}

/* The slot a travelling tile would land on. Flat and pale, with none of a
   tile's bevel or diagonal face, so it reads as a place rather than as a
   second tile. It is in the column only whilst something is being carried. */
nx-dock .slot {
  grid-column: 1;
  background: var(--light);
  pointer-events: none;
}

nx-tile {
  width: var(--dock-width); height: var(--dock-width);
  display: grid;
  place-items: center;
  position: relative;
  cursor: default;
  flex: 0 0 auto;
  grid-column: 1;
  /* The tile's bevel is two pixels rather than the usual one. */
  border-width: 2px;
  border-color: var(--white) var(--black) var(--black) var(--white);
  box-shadow: none;
  /* The face runs diagonally, not down. Measured at the four corners of a tile
     it reads #aaaaaa top left and #555555 bottom right, with the other two
     corners landing between them. */
  background: linear-gradient(135deg, var(--light), var(--dark));
}
/* No pressed state, on purpose. A tile is not a button: NeXTSTEP's did not
   depress under the pointer, and a click on one is not an answer to anything.
   What a tile does say is whether its application is running, which is the
   marks below. */

/* Lifted out of the column and carried. Above the tiles it passes, so that it
   is seen crossing them rather than behind them. */
nx-tile[carried] { z-index: 60; }

/* An icon on its way to the floor. Above everything, because it passes over
   the windows, and deaf to the pointer, because it is a picture of something
   rather than the thing. The rectangles that draw the way from a folder to
   its contents are in base.css, beside the rest of what the desk itself
   draws. */
.flying {
  position: fixed;
  z-index: 80;
  pointer-events: none;
  background-size: contain;
  background-repeat: no-repeat;
}

/* The floor of the screen, where an application that is not in the dock puts
   its icon. From the left corner rightwards, which is the order they were
   started in, and clear of the dock at the other end. */
nx-floor {
  position: fixed;
  left: 0; bottom: 0;
  right: var(--dock-width);
  display: flex;
  z-index: 50;
}
nx-tile .art { width: 56px; height: 56px; background-size: contain; }

/* Three marks pressed into the corner said the application was not started. In
   the screenshot the Workspace Manager carries none of them, and it is the one
   application that is always running. The bitmap is the original, three pixels
   by two apiece. */
nx-tile[idle]::after {
  content: "";
  position: absolute;
  left: 4px; bottom: 4px;
  width: 13px; height: 2px;
  background-image: var(--dock-marks);
  image-rendering: pixelated;
}

/* --- nx-scroller -------------------------------------------------------- */

/* NeXTSTEP put its scroller on the left of the view, and both its arrows at
   the bottom rather than one at either end. The trough is a one-pixel chequer
   of #555555 and #aaaaaa, which reads as a grey halfway between them and is
   how a machine with four greys drew a fifth.

   Every sunken surface that could ever scroll goes inside one of these. That
   is the rule, and this rule block is what enforces it: nx-scroller is the one
   place in the whole stylesheet that hands out overflow, so a surface outside
   one cannot scroll and therefore cannot show the browser's own bar. The
   sunken edge belongs to the scroller as well, not to the view inside it. */
/* The bar down the left, the view beside it, and where the element asks for
   one, a second bar along the foot of the view. A grid rather than a row,
   because the foot's bar runs under the view and not under the other bar:
   the original keeps that corner for the vertical one. */
nx-scroller {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-rows: 1fr auto;
}
nx-scroller > .bar { grid-row: 1 / -1; grid-column: 1; }
nx-scroller > .bar.across { grid-row: 2; grid-column: 2; }
nx-scroller > :not(.bar) { grid-row: 1; grid-column: 2; }

/* A view with no scroller down its side keeps no room for one. */
nx-scroller[bars="across"] > .bar.across { grid-column: 1 / -1; }
nx-scroller[bars="across"] > :not(.bar) { grid-column: 1 / -1; }

nx-scroller .bar {
  width: var(--scroller);
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--black);
  background: repeating-conic-gradient(var(--dark) 0 25%, var(--light) 0 50%) 0 0 / 2px 2px;
}

/* The same bar turned on its side: as tall as the other is wide, its line
   against the view along its top rather than its right, and its arrows at the
   far end, which for this one is the right. */
nx-scroller .bar.across {
  width: auto;
  height: var(--scroller);
  flex-direction: row;
  border-right: 0;
  border-top: 1px solid var(--black);
}

/* The trough holds the knob and gives it the room it slides in. */
nx-scroller .trough { flex: 1 1 auto; position: relative; min-height: 0; min-width: 0; }

nx-scroller .knob {
  position: absolute;
  left: 1px; right: 1px;
  top: 0;
  background: var(--light);
  border: 1px solid;
  border-color: var(--white) var(--black) var(--black) var(--white);
  box-shadow: inset -1px -1px 0 var(--dark);
  cursor: default;
}
/* The dimple in the middle of the knob, which is what you take hold of. */
nx-scroller .knob::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 5px; height: 5px;
  margin: -3px 0 0 -3px;
  border-radius: 50%;
  border: 1px solid var(--dark);
  border-bottom-color: var(--white);
  border-right-color: var(--white);
}

/* A knob in the bar along the foot slides the other way, so what is pinned is
   its height rather than its width. */
nx-scroller .bar.across .knob {
  left: 0; right: auto;
  top: 1px; bottom: 1px;
}

/* Both arrows sit at the far end of the bar, side by side, and they go away
   with the knob when there is nothing to scroll. */
nx-scroller .arrows { flex: 0 0 auto; }
nx-scroller .bar.across .arrows { display: flex; }
nx-scroller .step {
  width: 100%;
  height: var(--scroller);
  display: grid;
  place-items: center;
  background: var(--light);
  border: 1px solid;
  border-color: var(--white) var(--black) var(--black) var(--white);
  box-shadow: inset -1px -1px 0 var(--dark);
  cursor: default;
}
nx-scroller .step:active {
  border-color: var(--dark) var(--white) var(--white) var(--dark);
  box-shadow: inset 1px 1px 0 var(--black);
}
nx-scroller .step i {
  width: 0; height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
}
nx-scroller .step.up i { border-bottom: 6px solid var(--black); }
nx-scroller .step.down i { border-top: 6px solid var(--black); }

nx-scroller .bar.across .step { width: var(--scroller); height: 100%; }
nx-scroller .bar.across .step i {
  border-left: 0; border-right: 0;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
}
nx-scroller .step.left i { border-right: 6px solid var(--black); }
nx-scroller .step.right i { border-left: 6px solid var(--black); }

/* The view beside the bar scrolls; the bar draws the state, so the browser's
   own scrollbar stays out of the way. WebKit ignores scrollbar-width, which is
   why the pseudo-element is there too. */
nx-scroller > :not(.bar) {
  min-width: 0;
  min-height: 0;
  overflow: auto;
  scrollbar-width: none;
}
nx-scroller > :not(.bar)::-webkit-scrollbar { display: none; }

/* Where what scrolls is deeper in, which the element says by carrying this,
   the child is a frame around it and scrolls nothing itself. Two scrolling
   boxes inside one another would each take part of the wheel. */
nx-scroller[inner] > :not(.bar) { overflow: hidden; }

/* --- nx-shelf and nx-thing ---------------------------------------------- */

/* The file viewer's floor is the same grey as the window. Icons sit on it
   loose, without a tile of their own. */
nx-shelf {
  display: grid;
  align-content: start;
  /* Whole tiles of a fixed width, filled from the left. A column that stretched
     to the window would put the first icon further in and the next one further
     off the more room there was, and NeXTSTEP held the same step whatever the
     window was doing. */
  grid-template-columns: repeat(auto-fill, var(--tile));
  justify-content: start;
  grid-auto-rows: min-content;
  /* More between the rows than between the columns, because a name that runs
     to two lines reaches down towards the icon below it. The step across is
     the tile itself, so there is nothing to add between them. */
  gap: 14px 0;
  padding: 8px;
  background: var(--light);
}

nx-thing {
  display: grid;
  justify-items: center;
  align-content: start;
  /* No gap: the two halves of a mark have to touch, or the window's grey shows
     between them and one thing reads as two. */
  gap: 0;
  padding: 2px;
  cursor: default;
  /* What a mark is painted in. Nothing, until the thing is chosen or is the
     one in force, and then both halves and the fillets take it from here. */
  --mark: transparent;
}

/* The icon sits in a slot the size of hilite.tiff, so turning the selection on
   changes a colour and never a size. */
nx-thing .slot {
  width: var(--slot-w);
  /* Two pixels taller than the icon's box, spent above it, because the mark
     otherwise sits closer to the icon at the top than at the sides. */
  height: calc(var(--slot-h) + var(--slot-top));
  padding-top: var(--slot-top);
  box-sizing: border-box;
  display: grid;
  place-items: center;
  position: relative;
  background: var(--mark);
  /* The two halves are one shape, so only its outside is rounded. The slot's
     lower corners lie inside that shape and stay square: rounding them would
     cut notches out of the middle of the mark. */
  border-radius: var(--hilite-radius) var(--hilite-radius) 0 0;
}

/* The two inner corners where the slot meets the wider name below it. Left
   square they are the one sharp angle in a shape that is otherwise all curves,
   so each is filled with a square of the mark's own colour that has its far
   corner rounded away, which turns the angle into a concave curve.

   They work because the slot has a fixed width and the name is never narrower
   than it, so the step is always the same way round. */
nx-thing .slot::before,
nx-thing .slot::after {
  content: "";
  position: absolute;
  bottom: 0;
  width: var(--hilite-radius);
  height: var(--hilite-radius);
}
/* A radial gradient rather than a border-radius. A rounded corner keeps the
   disc and throws the rest away, which puts a bump in the crook instead of a
   hollow. This keeps everything except the disc, which is the hollow. */
nx-thing .slot::before {
  left: calc(var(--hilite-radius) * -1);
  background: radial-gradient(circle at top left,
    transparent var(--hilite-radius), var(--mark) 0);
}
nx-thing .slot::after {
  right: calc(var(--hilite-radius) * -1);
  background: radial-gradient(circle at top right,
    transparent var(--hilite-radius), var(--mark) 0);
}
nx-thing .icon { width: var(--icon); height: var(--icon); background-size: var(--icon); }

nx-thing .name {
  font-size: var(--text-size);
  line-height: 1.3;
  text-align: center;
  /* Air above and below the lettering, so a name on one line gets a mark with
     some height to it rather than a strip the size of the type. */
  padding: var(--name-air) 3px;
  max-width: 100%;
  /* Always wider than the slot above it by at least the radius on each side,
     so the step between the two always goes the same way and there is always a
     real crook for the fillets to round. */
  min-width: calc(var(--slot-w) + 2 * var(--hilite-radius));
  box-sizing: border-box;
  background: var(--mark);
  /* All four, because the slot above is narrower: the upper corners stand out
     on either side of it and are part of the outline. What is not part of it
     is the stretch of top edge the slot covers, and the fillets round the two
     places where the one meets the other. */
  border-radius: var(--hilite-radius);
}

/* The machine the emulator is actually running. The same shape as a selection
   so the two read as one language, in grey rather than white so they are never
   confused, with the icon dimmed because this machine is a fact rather than
   something to act on.

   Written before the selection below, so choosing the machine that is already
   running shows it as chosen. */
nx-thing.current { --mark: var(--in-force); }
nx-thing.current .icon { opacity: .6; }
nx-thing.current .name { font-weight: bold; }

/* The file has been written since that machine started, so what is on the
   screen and what is on the card are two different machines and nobody has
   tried the second one. Previous reads its configuration once, at startup, and
   this is the only place that difference can be seen.

   The mark keeps its shape and loses half its ink. The pattern is fixed to the
   window rather than to each element, so it runs across the slot, the name and
   the two fillets as one surface instead of restarting at every edge. */
nx-thing.current.untried .slot,
nx-thing.current.untried .name,
nx-thing.current.untried .slot::before,
nx-thing.current.untried .slot::after {
  background-image: var(--dither);
  background-size: var(--dither-size) var(--dither-size);
  background-attachment: fixed;
}

/* The fillets are a gradient that draws the hollow, so with a pattern in the
   background the hollow has to be cut instead of painted. */
nx-thing.current.untried .slot::before {
  mask-image: radial-gradient(circle at top left,
    transparent var(--hilite-radius), black 0);
}
nx-thing.current.untried .slot::after {
  mask-image: radial-gradient(circle at top right,
    transparent var(--hilite-radius), black 0);
}

/* Selection is white behind the icon and white behind the name, and the
   lettering stays black. No second colour enters the interface.

   Two shapes that touch rather than one box around both. Each follows what it
   is behind, so a short name gives a narrow one, and the pair reads as one
   thing because nothing shows between them. A single rectangle around the two
   would take in the window's grey on either side of the icon, which belongs to
   the desk rather than to the machine that is chosen. */
nx-thing[chosen] { --mark: var(--white); }

/* Whilst a machine is being carried, the thing it came from dims. */
nx-thing[lifting] { opacity: .45; }

/* --- the panel that asks before it changes anything --------------------- */

.scrim {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: none;
  place-items: center;
}
.scrim[data-open] { display: grid; }

.panel {
  width: 430px;
  border: 1px solid var(--black);
  background: var(--light);
  box-shadow: 4px 4px 0 rgba(0,0,0,.35);
  display: flex;
  flex-direction: column;
}
.panel .titlebar {
  height: var(--title-height);
  display: flex;
  align-items: center;
  padding: 0 2px 2px;
  margin: 1px 1px 0;
  color: var(--white);
  background: linear-gradient(to bottom,
    var(--black) 0 19px, var(--dark) 19px 20px, var(--black) 20px 21px);
}
.panel .title { flex: 1; text-align: center; font-weight: bold; }
.panel .pane { padding: 14px; }
.panel-body { display: grid; grid-template-columns: auto 1fr; gap: 14px; align-items: start; }
.panel-body .icon { width: 48px; height: 48px; background-size: contain; }
.panel-body p { margin: 0 0 8px; }
.panel-body p:last-child { margin: 0; }

/* The line a panel takes a token on. NeXT's text fields are white, sunken and
   one line tall, and this is the same field the readings use, made editable. */
.panel .entry {
  width: 100%;
  margin-top: 10px;
  font: 12px var(--mono);
  color: var(--black);
  background: var(--white);
  border: 1px solid var(--dark);
  box-shadow: inset 1px 1px 0 var(--black);
  padding: 3px 5px;
}
.panel .entry:focus { outline: 1px solid var(--black); outline-offset: 1px; }

/* --- nx-viewer ---------------------------------------------------------- */

/* Four bands, and the window gives them all the room it has. Only the last
   one grows: the shelf is as tall as what is on it, the status is one line,
   and the path is one row of icons. */
nx-viewer {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* The shelf sits on the window's own grey with no frame, which is what makes
   it read as part of the window rather than as another view. */
nx-viewer .keep {
  /* The original packs its shelf tighter than the band below it, which is the
     one place the two differ. */
  --tile: var(--shelf-tile);
  flex: 0 0 auto;
  min-height: calc(var(--slot-h) + var(--slot-top) + 2 * var(--name-air) + 20px);
}

/* Whilst something is being carried over it, the shelf says it will take it,
   the same way a window that accepts drops does. */
nx-viewer .keep[droppable] { outline: 2px solid var(--white); outline-offset: -3px; }

/* One line about the whole, in the smaller lettering NeXTSTEP used for it and
   in the grey that says it is not a reading anybody acts on. */
nx-viewer .status {
  flex: 0 0 auto;
  margin: 0;
  padding: 1px 3px 3px;
  font-size: var(--status-size);
  color: var(--dark);
}

/* The way here, as one row that does not wrap. A path too long for the window
   is scrolled rather than folded, which is what the scroller around it is
   for. */
nx-viewer .way { flex: 0 0 auto; }
nx-viewer .path {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px;
  width: max-content;
  min-width: 100%;
  box-sizing: border-box;
  background: var(--light);
}

/* The mark between two steps, drawn the way the scroller's arrows are: three
   borders, one of them the colour. */
nx-viewer .step-arrow {
  flex: 0 0 auto;
  width: 0;
  height: 0;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  border-left: 6px solid var(--dark);
}

/* Room under the path, so the two framed bands do not touch. Six pixels of
   the window's own grey, measured between the two boxes in the original. */
nx-viewer > nx-scroller + nx-scroller { margin-top: var(--band-gap); }

/* The last band has no bottom edge. It runs to the foot of the window, where
   the resize bar closes it, and the original draws no line there: its left and
   right edges reach the bar and stop. */
nx-viewer > nx-scroller:last-child { border-bottom: 0; }

/* --- the info panel ----------------------------------------------------- */

/* Picture on the left, readings on the right. The picture is also the target:
   a machine dropped anywhere in this window lands on it. */
.info-body {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--portrait-gap);
}

/* A second group of readings under the picture is the same list continued, so
   the step between the two groups is the step inside them, and it stands in
   the same column. Indented by what the picture and its gap take, every label
   in the window ends on one line and every field is the same length, which is
   what makes seven readings read as one list rather than as two. */
.info-body + .readings {
  margin-top: var(--reading-gap);
  padding-left: calc(var(--portrait) + var(--portrait-gap));
}

nx-portrait {
  display: grid;
  justify-items: center;
  align-content: start;
  align-self: start;
  gap: 6px;
  padding: 8px 6px;
  width: var(--portrait);
  background: var(--light);
}
nx-portrait .icon { width: 48px; height: 48px; background-size: contain; }
nx-portrait .caption { text-align: center; line-height: 1.3; }

/* The whole window answers a drag, so the eye is not asked to hit 48 pixels. */
nx-window[droppable] { outline: 2px solid var(--white); outline-offset: -4px; }
nx-window[droppable] nx-portrait { background: var(--white); }

.readings {
  min-height: 0;
  overflow: hidden;
  display: grid;
  grid-auto-rows: min-content;
  gap: var(--reading-gap);
  align-content: start;
}

nx-row { display: grid; grid-template-columns: 88px 1fr; gap: 8px; align-items: center; }
nx-row label { text-align: right; }

nx-field {
  display: block;
  background: var(--white);
  padding: 2px 6px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.state { display: flex; align-items: center; gap: 7px; }
.lamp { width: 9px; height: 9px; border: 1px solid var(--black); background: var(--live); }

button {
  font: var(--text-size) var(--font);
  color: var(--black);
  background: var(--light);
  padding: 3px 14px;
  cursor: pointer;
}
/* Pressed, a button wears the sunken edge instead. */
button:active {
  border-color: var(--dark) var(--white) var(--white) var(--dark);
  box-shadow: inset 1px 1px 0 var(--black);
  padding: 4px 13px 2px 15px;
}
button.default { outline: 1px solid var(--black); outline-offset: 2px; font-weight: bold; }

.buttons {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 10px;
  padding-top: 9px;
  border-top: 1px solid var(--dark);
  flex: 0 0 auto;
}

/* A row of buttons at the foot of a window that can be resized brings its own
   air with it. The pane gives such a window no padding at the bottom, because
   the File Viewer's bands are meant to run into the resize bar, and a button
   is not a band: without this it sits on the bar with its lower edge cut off.
   */
nx-window:not([fixed]) .buttons:last-child { padding-bottom: var(--pane-pad); }

.note { margin: 8px 0 0; font-size: 11px; flex: 0 0 auto; }
/* A note with nothing in it takes no room at all. Its margin would otherwise
   hold a gap open under whatever is above it, which in the File Viewer is the
   band that has to reach the resize bar. */
.note:empty { display: none; }

/* --- the terminal ------------------------------------------------------- */

/* NeXT's Terminal was black on white in a bitmap face called Ohlfs, so this
   one is black on white too. The session it shows is the Pi underneath. */
.terminal {
  background: var(--white);
  padding: 5px 7px;
  font: 12px/1.35 var(--mono);
  white-space: pre-wrap;
  -webkit-user-select: text;
  user-select: text;
  cursor: text;
}
.terminal .cursor {
  display: inline-block;
  width: 7px; height: 13px;
  background: var(--black);
  vertical-align: text-bottom;
  animation: blink 1.06s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .terminal .cursor { animation: none; } }
