/* jjscoolwebsite.com — projects page.

   Somebody's desktop, rather than a list. Two folders of documents; a .DOC
   opens in the word processor (Michaelsoft Clurd, further down), the same
   raised --face panel with a sunken well used by the inventory/music player
   (quest.css), so a project reads as a file on this machine.

   Open/closed state is the URL fragment only — `#win-x`, :target — no
   script needed (CSP is script-src 'self'). The inline script in
   projects.njk only adds escape/click-outside/space/focus handling on top. */

/* <main> is the screen: the band between the top bar and the bottom of
   the window, and the box both the desktop and the boot sequence are
   measured against. Scoped to this sheet, so no other page's <main>
   moves.

   It has to be said here rather than left to chance, because the quest
   spends its opening seconds animating a filter on this same element.
   A filter is a containing block for positioned descendants, so while it
   is still running <main> is one whether this rule exists or not — and
   the moment the transition lands on none, it stops being one. Anything
   positioned in here would otherwise be measured against <main> or the
   viewport depending on how far through that animation it happened to
   start, which is not a thing to leave to the clock. */
main {
  position: relative;
  /* The wallpaper file is the real pixel grid (47x27) rather than a photo,
     so the browser has to scale it up with nearest-neighbour sampling —
     without image-rendering: pixelated, `cover` blurs it back into the
     photo it was pixelated to avoid. */
  background-image: url("/assets/web/pages/projects/background.jpg");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  image-rendering: pixelated;
  image-rendering: -moz-crisp-edges;
}

.projects {
  padding-block: 2.5rem 3rem;
}

.projects__intro {
  max-width: 65ch;
  margin: 2rem auto 0;
  line-height: 1.6;
  text-align: center;
}


/* Only true above the breakpoint, where windows float and can be dragged. */
.projects__hint-drag {
  display: none;
}

/* --- the desktop ---------------------------------------------------------
   The two folders are windows lying on the desktop, offset with a corner of
   one over the other, signalling "these move" before anyone drags one.

   Above the breakpoint the desktop becomes a layer over the whole of <main>
   (the band between top bar and ticker) instead of a column box, so windows
   can go anywhere including over the page's own title/subtitle. It's
   pointer-events: none so the page underneath still scrolls and links work.

   Positions are in vw/vh (not rem) so the pair lands the same place on a
   laptop and a 4K monitor; main.js writes left/top in px, clamped to the
   layer. Below the breakpoint windows go back to stacked panels — no room
   to overlap two on a phone. Drag code checks the computed position, so
   both layouts stay driven by this one decision. */

.desktop {
  position: relative;
  /* Wide enough for a window plus its offset, no wider, so the pair sits
     centred rather than in the corner of a 1100px wrap. */
  max-width: 46rem;
  margin: 2.5rem auto 0;
}

@media (min-width: 60rem) {
  .projects__hint-drag {
    display: inline;
  }

  .desktop {
    position: absolute;
    inset: 0;
    max-width: none;
    margin: 0;
    /* Over the page's title/subtitle, under the top bar (5) and ticker (2)
       — this layer is the fence the drag in main.js keeps windows inside. */
    z-index: 1;
    pointer-events: none;
  }

  /* Both selectors qualified by .desktop: the stacked layout's own
     `.folder + .folder` gap (below) would otherwise tie on specificity and
     win on source order, adding 40px of daylight to an absolute window. */
  .desktop .folder,
  .desktop .folder + .folder {
    position: absolute;
    margin: 0;
    width: 34rem;
    max-width: calc(100vw - 2rem);
    pointer-events: auto;
  }

  .folder--fun {
    left: 38vw;
    top: 18vh;
    z-index: 3;
  }

  /* Offset by less than a window so the overlap is obvious — two windows
     merely near each other read as layout, not as things left lying
     around. WORK is in front: it opened last. */
  .folder--work {
    left: 27vw;
    top: 36vh;
    z-index: 2;
  }

  /* Tucked below and behind the other two, to be found rather than seen. */
  .folder--secret {
    left: 16vw;
    top: 62vh;
    z-index: 1;
  }

  /* Opens over the pile rather than beside it — it was asked for, so it
     arrives in front. Wider than a folder: it has four columns of results
     to fit, not a grid of icons. */
  .desktop .folder--find {
    left: 30vw;
    top: 14vh;
    width: 38rem;
    z-index: 4;
  }

  /* This is the page's headline now, standing in for the h1/subtitle that
     used to open the page as body copy — so it sits top and centre, above
     the folders rather than in the pile with them, and opens in front of
     everything else on the desktop. Centred with calc rather than a
     transform: main.js drags folders by writing left/top in px, and a
     transform used only for the resting position would fight that the
     first time someone moves it. */
  .desktop .folder--mumnote {
    left: calc(50% + 5rem);
    top: 2vh;
    width: 28rem;
    z-index: 5;
  }

  /* Wallpaper icons don't come to front on touch — only windows do. */
  .desk-icon {
    position: absolute;
    left: 1.5vw;
    width: 5.5rem;
    pointer-events: auto;
    z-index: 1;
  }

  .desk-icon--fun {
    top: 4vh;
  }

  .desk-icon--work {
    top: 22vh;
  }

  .desk-icon--secret {
    top: 40vh;
  }

  .desk-icon--mumnote {
    top: 58vh;
  }
}

/* --- open, closed, and getting it back -----------------------------------
   A window is open while its checkbox is checked; its two labels are the X
   in the title bar and the wallpaper icon — no script needed either way.
   The input is off-screen (not display: none) so it stays tabbable, with
   the focus ring painted on whichever label is currently visible. */

.folder__state {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

#open-fun:not(:checked) ~ .folder--fun,
#open-work:not(:checked) ~ .folder--work,
#open-secret:not(:checked) ~ .folder--secret,
#open-find:not(:checked) ~ .folder--find,
#open-mumnote:not(:checked) ~ .folder--mumnote {
  display: none;
}

#open-fun:focus-visible ~ .desk-icon--fun,
#open-work:focus-visible ~ .desk-icon--work,
#open-secret:focus-visible ~ .desk-icon--secret,
#open-mumnote:focus-visible ~ .desk-icon--mumnote,
#open-fun:focus-visible ~ .folder--fun .folder__btn--close,
#open-work:focus-visible ~ .folder--work .folder__btn--close,
#open-secret:focus-visible ~ .folder--secret .folder__btn--close,
#open-find:focus-visible ~ .folder--find .folder__btn--close,
#open-mumnote:focus-visible ~ .folder--mumnote .folder__btn--close {
  outline: var(--rule) solid var(--yellow);
  outline-offset: 2px;
}

/* Minimised: off the screen, still ticked, so the taskbar button stays up
   and is the only way back. That split — ticked means running, this class
   means hidden — is why minimise needed a taskbar before it could exist
   at all. Set by main.js; !important is not needed, since the rule it has
   to beat (`:checked ~ .folder` showing the window) is only ever the
   absence of a display: none. */
.folder--min {
  display: none;
}

/* --- an icon on the wallpaper --------------------------------------------
   A folder and its name, where desktop icons live. Below the breakpoint
   (no wallpaper) these become a row of buttons that reopen a closed folder. */

.desk-icon {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  padding: 0.4rem 0.2rem;
  color: var(--yellow);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1.3;
  text-align: center;
  cursor: var(--cursor-link);
  /* Dragging one must not select its label instead. */
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}

/* A manila folder: body, back tab, and a lighter front flap so the two
   halves read as two pieces of card. */
.desk-icon__glyph {
  position: relative;
  width: 2.9rem;
  height: 2.2rem;
  margin-top: 0.5rem;
  background: var(--yellow-dark);
  border: 1px solid var(--bg-dim);
}

.desk-icon__glyph::before {
  content: "";
  position: absolute;
  top: -0.5rem;
  left: -1px;
  width: 1.3rem;
  height: 0.5rem;
  background: var(--yellow-dark);
  border: 1px solid var(--bg-dim);
  border-bottom: none;
}

.desk-icon__glyph::after {
  content: "";
  position: absolute;
  inset: 0.4rem -1px -1px -1px;
  background: var(--yellow);
  border: 1px solid var(--bg-dim);
}

.desk-icon__label {
  padding: 0.1rem 0.3rem;
  overflow-wrap: anywhere;
}

.desk-icon:hover .desk-icon__label,
.desk-icon:active .desk-icon__label {
  background: var(--yellow);
  color: var(--bg);
}

.desk-icon:active .desk-icon__glyph {
  translate: 1px 1px;
}

/* The open window's own icon is dimmed, like a shortcut already on screen. */
#open-fun:checked ~ .desk-icon--fun,
#open-work:checked ~ .desk-icon--work,
#open-secret:checked ~ .desk-icon--secret,
#open-mumnote:checked ~ .desk-icon--mumnote {
  opacity: 0.55;
}

/* A single sheet of paper rather than a folder, for the one desktop icon
   that isn't a directory — same fold-corner trick .icon__tile uses inside
   a folder's well, at desk-icon scale. */
.desk-icon__glyph--doc {
  width: 2.1rem;
  height: 2.5rem;
  margin-top: 0.25rem;
  background: #f4f2ea;
  clip-path: polygon(0 0, calc(100% - 0.55rem) 0, 100% 0.55rem, 100% 100%, 0 100%);
}

.desk-icon__glyph--doc::before {
  content: none;
}

.desk-icon__glyph--doc::after {
  inset: auto;
  top: 0;
  right: 0;
  width: 0.55rem;
  height: 0.55rem;
  background: linear-gradient(225deg, var(--bg-dim) 0 50%, #cfcbbd 50%);
}

/* Stacked layout: no wallpaper, so icons are a button row above the
   folders instead of sitting in a corner. */
@media (max-width: 59.999rem) {
  .desk-icon {
    display: inline-flex;
    width: 6rem;
    vertical-align: top;
  }
}

/* --- a folder --------------------------------------------------------
   An Explorer window: title bar, menu strip, icon well, status bar.

   Grey, unlike the rest of the site — deliberately: these read as the OS's
   own windows on top of the page, not the site's purple widgets (though
   cooled toward the page's purple, not flat #c0c0c0, so it still belongs
   to the same screenshot). Each redeclares --face/--lit/--dim; bevels
   follow automatically. */

.folder {
  --face: #b4b0c6;
  --lit: #e6e3f0;
  --dim: #4b465f;
  --ink: #1e1b2b;
  margin: 0 auto;
  max-width: 46rem;
  /* Padding of exactly the bevel so the title bar runs edge to edge, like
     a real window. Body breathing room is on .folder__inner instead. */
  padding: var(--bevel);
  background: var(--face);
  box-shadow: var(--raised);
  border: var(--rule) solid var(--dim);
}

.folder + .folder {
  margin-top: 2.5rem;
}

/* --- the title bar -----------------------------------------------------
   Its own dark strip, full width, with a gradient for the era's
   active-window cue and the controls in the corner. */

.folder__bar {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.35rem;
  background: linear-gradient(to right, #1b1830, #4d3f7a);
  /* Separates the bar from the window face below it. */
  border-bottom: 2px solid var(--dim);
  /* Grab handle — must not start a text selection or scroll gesture. */
  cursor: move;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}

/* Document glyph, drawn (not fetched) as two gradients — no icon font. */
.folder__icon {
  flex: 0 0 auto;
  width: 0.85rem;
  height: 0.95rem;
  background:
    linear-gradient(225deg, #1b1830 0 0.3rem, transparent 0.3rem) top right / 0.35rem 0.35rem no-repeat,
    var(--yellow);
  border: 1px solid #1b1830;
}

/* Find's own title-bar icon: the magnifier again, at bar scale. */
.folder__icon--find {
  width: 0.85rem;
  height: 0.85rem;
  background: #cfe6ff;
  border-radius: 50%;
  border: 1px solid #1b1830;
  box-shadow: 2px 2px 0 0 var(--yellow);
}

.folder__title {
  flex: 1;
  min-width: 0;
  margin: 0;
  color: var(--white);
  font-size: clamp(0.7rem, 2.2vw, 0.8rem);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-shadow: 1px 1px 0 #16121f;
  /* Paths are already upper case; break anywhere rather than overflow. */
  overflow-wrap: anywhere;
}

/* Minimise/maximise are decorative (aria-hidden); close is real. */
.folder__controls {
  flex: 0 0 auto;
  display: flex;
  gap: 2px;
}

/* Minimise is a <button> but maximise is a <span> and close a <label>, so
   the UA's own button padding, border and font have to be cleared here or
   the one real control never lines up with the two beside it. */
.folder__btn {
  --bevel: 2px;
  appearance: none;
  display: grid;
  place-items: center;
  width: 1.1rem;
  height: 1rem;
  margin: 0;
  padding: 0;
  border: 0;
  background: var(--face);
  box-shadow: var(--raised);
  color: var(--ink);
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 1;
}

/* Drawn rather than the underscore character: `_` sits near the bottom of
   its own em box by font design, so no amount of centering or translate
   puts its ink where the box's center actually is. */
.folder__btn--min::after {
  content: "";
  width: 0.45rem;
  height: 2px;
  background: var(--ink);
  translate: -1px 2px;
}

.folder__btn--close {
  cursor: var(--cursor-link);
}

.folder__btn--close:hover {
  background: var(--lit);
}

.folder__btn--close:active {
  box-shadow: var(--sunken);
  translate: 1px 1px;
}

/* Menu strip, names only — none of these open anything. */
.folder__menu {
  display: flex;
  gap: 0.9rem;
  margin: 0;
  padding: 0.25rem 0.5rem;
  border-bottom: 1px solid var(--dim);
  color: var(--ink);
  font-size: 0.7rem;
  user-select: none;
  -webkit-user-select: none;
}

.folder__inner {
  padding: calc(0.9rem + var(--bevel));
}

/* Icons sit in a well of the page's own navy, not the panel's lavender —
   same rule as the ticker track and now-playing readout. */
.folder__well {
  --lit: var(--bg-lit);
  --dim: var(--bg-dim);
  padding: calc(0.9rem + var(--bevel));
  background: var(--bg);
  box-shadow: var(--sunken);
}

/* auto-fill, not auto-fit: two icons stay two, not stretched to fill the
   width; 6.5rem still gives two columns on a 400px phone. */
.folder__icons {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
  gap: 1rem 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* What a folder says in place of icons — currently only C:\TOP SECRET,
   which is empty to anyone not wearing the cool sunglasses (see
   syncSecretFolder() in main.js). A row of the icon grid rather than a
   paragraph beside it, so it gets the full width instead of the first
   7.5rem column. */
.folder__empty {
  grid-column: 1 / -1;
  margin: 0;
  max-width: 46ch;
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.6;
}

/* Count along the bottom, sunken into the window's grey (frame), not the
   navy well above it. */
.folder__status {
  --bevel: 2px;
  margin: 0.75rem 0 0;
  padding: 0.2rem calc(0.5rem + var(--bevel));
  background: var(--face);
  box-shadow: var(--sunken);
  color: var(--ink);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
}

/* Set by main.js while a pointer holds the bar. */
.folder--dragging,
.desk-icon--dragging,
.folder--flash {
  outline: 2px dotted var(--yellow);
  outline-offset: 2px;
}

/* --- Scribblepad: asdfghjkl.TXT --------------------------------------
   The plainest window on the machine on purpose: no icon well, no menu
   that means anything, just a sheet of paper with actual sentences on it.
   .folder__inner's own padding is dropped so the paper can run edge to
   edge, the way a real text editor's page does.

   Comic Sans rather than the Courier New every other document listing on
   this page uses — this page's whole bit is a machine that thinks it's
   cool, and a real word processor wouldn't set its own headline in a
   typewriter font. Same font quest.css already reaches for when a joke
   needs to look like a mid-90s GeoCities page rather than a serious one. */

.folder__inner--notepad {
  padding: 0;
}

.notepad__text {
  margin: 0;
  max-height: min(60vh, 26rem);
  overflow-y: auto;
  padding: 1.1rem 1.2rem 1.3rem;
  background: #fbfaf5;
  color: #22202c;
  font-family: "Comic Sans MS", "Comic Sans", cursive, sans-serif;
  font-size: clamp(1rem, 2.6vw, 1.2rem);
  line-height: 1.65;
  white-space: pre-wrap;
}

/* The one line that used to be the page's own <h1> — still gets to look
   like one: bold, bigger, and in the site's own purple rather than the
   usual yellow, which all but vanished against the paper's near-white
   background. Hardcoded rather than var(--face)/var(--bg): both are
   redefined inside .folder for the grey Explorer chrome, so either
   custom property would hand back that grey here, not the site's purple. */
.notepad__headline {
  display: inline-block;
  color: #45375e;
  font-size: clamp(1.3rem, 4vw, 1.75rem);
  font-weight: 700;
  text-shadow: 0 0 10px rgba(69, 55, 94, 0.35);
}

/* --- an icon --------------------------------------------------------
   A tile and label: paper with a dog-eared corner and the word
   processor's W badge, the way a .DOC's icon looked. */

.icon {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.2rem;
  color: var(--yellow);
  text-decoration: none;
  text-align: center;
}

/* Portrait and white (not panel grey) so it reads as paper against the
   navy well. Fold is a clip + shadow triangle over the same corner. No
   bevel — paper doesn't have one. */
.icon__tile {
  position: relative;
  width: 2.75rem;
  height: 3.35rem;
  overflow: hidden;
  background: #f4f2ea;
  border: 1px solid var(--bg-dim);
  clip-path: polygon(0 0, calc(100% - 0.7rem) 0, 100% 0.7rem, 100% 100%, 0 100%);
}

/* The fold: the corner the clip removed, drawn back as shadow. */
.icon__tile::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  width: 0.7rem;
  height: 0.7rem;
  background: linear-gradient(225deg, var(--bg-dim) 0 50%, #cfcbbd 50%);
}

/* Page preview, edge to edge; the W badge sits on top of it. */
.icon__thumb {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
}

/* No screenshot: drawn as text lines instead, at icon scale. Two hard
   stops per band, no blur — this is pixel art, not a soft gradient. */
.icon__lines {
  display: block;
  height: 68%;
  margin: 0 0.3rem;
  background-image: repeating-linear-gradient(
    to bottom,
    #9a968b 0 2px,
    transparent 2px 6px
  );
  background-position: 0 0.35rem;
  background-repeat: no-repeat;
  background-size: 100% 1.6rem;
}

/* The word processor's own mark, stamped on anything it owns. */
.icon__w {
  position: absolute;
  left: 0;
  bottom: 0;
  display: grid;
  place-items: center;
  width: 1.1rem;
  height: 1.1rem;
  background: var(--clurd-blue, #2a3f8f);
  color: var(--white);
  font-size: 0.7rem;
  font-weight: 700;
  font-family: "Times New Roman", Times, serif;
  line-height: 1;
  box-shadow: 0 0 0 1px var(--bg-dim);
}

/* Selection: label inverts, tile presses in; same on keyboard + focus ring. */
.icon__label {
  padding: 0.1rem 0.3rem;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

.icon[href]:hover .icon__label,
.icon[href]:focus-visible .icon__label {
  background: var(--yellow);
  color: var(--bg);
}

/* Paper doesn't press in, so selection tints it with the label's colour
   instead. */
.icon[href]:hover .icon__tile,
.icon[href]:focus-visible .icon__tile {
  background: #fff6c8;
  border-color: var(--yellow);
}

.icon[href]:active .icon__tile {
  translate: 1px 1px;
}

/* Nothing to open — drained like a locked achievement, so the art alone
   says "nothing here". */
.icon--dead {
  cursor: var(--cursor-default);
  opacity: 0.45;
}

.icon--dead .icon__tile {
  filter: grayscale(1) brightness(0.8);
}

.icon--dead .icon__label {
  color: var(--muted);
}

/* Skull pickup: a button dressed as this icon (no document tile, just
   the sprite), since pressing it is the whole action. One-shot —
   see pickupSkull() in main.js — the icon is hidden outright once taken
   rather than kept in a second "on" state.

   It arrives the same way it leaves. The button spends most of the visit
   hidden and is un-hidden the moment the cool sunglasses go on, and a
   `display: none` element that starts being displayed runs its animations
   from the top — so pickup-resolve plays itself on every reveal without
   anything having to add a class at the right moment. On the button rather
   than on .icon__sprite because that one is already spinning. */
.icon--pickup {
  font: inherit;
  background: none;
  border: none;
  cursor: var(--cursor-link);
  animation: pickup-resolve 520ms steps(1, end) both;
}

/* .icon's own display: flex ties the UA [hidden] rule on specificity and
   wins — without this, .hidden set in syncSecretFolder() left the button
   visible after the skull was taken. */
.icon--pickup[hidden] {
  display: none;
}

/* A file the machine is having trouble drawing: three held frames of
   nothing-quite-there before it settles, like a sprite being decoded one
   pass at a time. Stepped, not eased — this screen doesn't do gradients. */
@keyframes pickup-resolve {
  0% {
    opacity: 0;
  }

  20% {
    opacity: 0.35;
    filter: blur(2px);
  }

  45% {
    opacity: 0.15;
    filter: blur(1px);
  }

  70% {
    opacity: 0.8;
    filter: blur(1px);
  }

  100% {
    opacity: 1;
    filter: none;
  }
}

/* The reveal is information — it says the folder isn't empty any more — so
   it stays, minus the strobe: one frame, straight in. */
@media (prefers-reduced-motion: reduce) {
  .icon--pickup {
    animation: none;
  }
}

.icon--pickup .icon__tile {
  display: grid;
  place-items: center;
  background: none;
  border: none;
  clip-path: none;
}

.icon--pickup .icon__tile::after {
  content: none;
}

/* Spins in place until taken (quest-spin/glow reused from quest.css,
   loaded on every page). Hidden on pickup, so nothing needs to stop it. */
.icon__sprite {
  display: block;
  width: 32px;
  height: 32px;
  image-rendering: crisp-edges;
  image-rendering: pixelated;
  filter: drop-shadow(0 0 8px rgba(251, 242, 84, 0.55));
  animation: quest-spin 1.8s linear infinite;
}

.icon--pickup:hover .icon__tile,
.icon--pickup:focus-visible .icon__tile {
  background: #fff6c8;
  border-color: var(--yellow);
}

.icon--pickup:hover .icon__label,
.icon--pickup:focus-visible .icon__label {
  background: var(--yellow);
  color: var(--bg);
}

.icon--pickup:active .icon__tile {
  translate: 1px 1px;
}

/* --- a window: Michaelsoft Clurd -----------------------------------------
   The layer is the scrim and centring; the panel inside is the window.
   Closed by default, so a browser without :target shows the icon grid.

   A .DOC opens here: grey frame, blue title bar, menu, dead toolbar, grey
   workspace, and a sheet of white paper with the project typed in Times —
   the only white on the site, a deliberate 90s-desktop joke.

   main.js drags it by the title bar as an enhancement only — the window
   opens, reads and closes without it. */

.window-layer {
  display: none;
}

.window-layer:target {
  position: fixed;
  inset: 0;
  /* Under quest.css's own windows (45+), over everything else. */
  z-index: 44;
  display: grid;
  place-items: center;
  padding: 1.25rem;
  background: rgba(16, 14, 26, 0.72);
}

/* Same machine grey as the folder it opened from — an OS window, not a
   page panel. */
.window {
  --face: #b4b0c6;
  --lit: #e6e3f0;
  --dim: #4b465f;
  --ink: #1e1b2b;
  --clurd-blue: #2a3f8f;
  --paper: #fbfaf5;
  --paper-ink: #22202c;
  width: 100%;
  max-width: 38rem;
  /* Grid items are min-width: auto by default — without this the widest
     strip (toolbar/status bar) could exceed the viewport. */
  min-width: 0;
  /* Capped, not scrolling the scrim, so an overflow clips at the bottom
     rather than clipping the title bar at the top. */
  max-height: calc(100dvh - 2.5rem);
  display: flex;
  flex-direction: column;
  /* Padding of exactly the bevel, like the folders. */
  padding: var(--bevel);
  background: var(--face);
  box-shadow: var(--raised);
  border: var(--rule) solid var(--dim);
  animation: window-open 140ms cubic-bezier(0.1, 0.9, 0.2, 1) both;
}

/* Same dotted drag outline the folders get. */
.window--dragging {
  outline: 2px dotted var(--yellow);
  outline-offset: 2px;
}

/* Zooms out of nothing, like a window opening from its icon. */
@keyframes window-open {
  from { scale: 0.8; opacity: 0; }
  to   { scale: 1; opacity: 1; }
}

/* Blue, not the Explorer windows' near-black, so the two window kinds are
   told apart at a glance. */
.window__bar {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.35rem;
  background: linear-gradient(to right, #17224f, var(--clurd-blue));
  border-bottom: 2px solid var(--dim);
  /* Grab handle — must not start a text selection or scroll gesture. */
  cursor: move;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}

.clurd__logo {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 1rem;
  height: 1rem;
  background: var(--white);
  color: var(--clurd-blue);
  font-family: "Times New Roman", Times, serif;
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1;
}

.window__title {
  flex: 1;
  min-width: 0;
  margin: 0;
  color: var(--white);
  font-size: clamp(0.7rem, 2.2vw, 0.8rem);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-shadow: 1px 1px 0 #101733;
  overflow-wrap: anywhere;
}

.clurd__controls {
  flex: 0 0 auto;
  display: flex;
  gap: 2px;
}

/* Minimise/maximise are scenery; close is the real, identically-drawn
   control that makes reaching for it obvious.

   padding-bottom is the optical centring, and it is load-bearing. A raised
   bevel is a light top-left edge and a dark bottom-right one, and the two
   do not read alike: the highlight is within a hair of the face's own
   lightness, so the eye takes it for more face and puts the top of the
   button at the box's top edge, while the dark edge contrasts hard and
   reads as where the face stops. The field the eye sees therefore runs
   from the top edge down to the *top* of the shadow, and its middle is
   half a bevel above the box's true middle — so anything centred
   geometrically lands low. (This is why Windows drew these glyphs a pixel
   up and left of centre rather than dead centre.) One bevel of bottom
   padding, with border-box sizing, shortens the content box to exactly
   that field, so place-items centres in what's actually seen. It's on
   .clurd__btn, not on the minimise rule, so all three marks shift as one
   and keep their alignment with each other. */
.clurd__btn {
  --bevel: 2px;
  display: grid;
  place-items: center;
  width: 1.15rem;
  height: 1.05rem;
  background: var(--face);
  box-shadow: var(--raised);
  color: var(--ink);
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1;
  text-decoration: none;
}

a.clurd__btn:hover,
a.clurd__btn:focus-visible {
  background: var(--lit);
}

a.clurd__btn:active {
  box-shadow: var(--sunken);
  translate: 1px 1px;
}

/* Minimise is drawn as a short rule rather than set with the underscore
   character: an underscore's ink sits near the bottom of its em box (it's
   meant to run under following glyphs), so a centred box still reads as a
   low mark whatever font renders it. A drawn line centres exactly. */
.clurd__btn--min::after {
  content: "";
  width: 0.5rem;
  height: 2px;
  background: var(--ink);
  translate: 0 2px;
}

/* Menu strip with underlined access keys — none open anything. */
.clurd__menu {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.9rem;
  margin: 0;
  padding: 0.25rem 0.5rem;
  border-bottom: 1px solid var(--dim);
  color: var(--ink);
  font-size: 0.7rem;
  user-select: none;
  -webkit-user-select: none;
}

/* Toolbar: bold/italic/underline plus three unlabelled buttons. */
.clurd__tools {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  margin: 0;
  padding: 0.3rem 0.4rem;
  border-bottom: 1px solid var(--dim);
  user-select: none;
  -webkit-user-select: none;
}

.clurd__tool {
  --bevel: 2px;
  display: grid;
  place-items: center;
  width: 1.3rem;
  height: 1.3rem;
  background: var(--face);
  box-shadow: var(--raised);
  color: var(--ink);
  font-family: "Times New Roman", Times, serif;
  font-size: 0.8rem;
  font-weight: 700;
}

.clurd__tool--i {
  font-style: italic;
}

.clurd__tool--u {
  text-decoration: underline;
}

/* Sunken grey workspace the document floats on; scrolls if paper is
   taller than the window. */
.window__body {
  --lit: #6f6b84;
  --dim: #2e2b3d;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0.85rem 0.85rem 1rem;
  background: #625e78;
  box-shadow: var(--sunken);
}

/* The paper: shadowed sheet, ruled top margin, set in Times. */
.clurd__page {
  max-width: 34rem;
  margin: 0 auto;
  padding: 1.1rem 1.25rem 1.4rem;
  background: var(--paper);
  color: var(--paper-ink);
  border-top: 1px solid #d8d4c6;
  box-shadow: 3px 3px 0 rgba(20, 18, 30, 0.35);
  font-family: "Times New Roman", Times, Georgia, serif;
}

/* The document title: big, centred, ruled underneath. */
.window__head {
  margin: 0 0 0.6rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid #d8d4c6;
}

.window__name {
  font-size: clamp(1.15rem, 3vw, 1.5rem);
  font-weight: 700;
  color: var(--paper-ink);
}

.window__tag {
  --bevel: 2px;
  --lit: #d4585c;
  --dim: #5c1414;
  display: inline-block;
  margin-left: 0.6rem;
  background: var(--red);
  box-shadow: var(--raised);
  color: var(--white);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 0.15rem 0.5rem;
  vertical-align: middle;
  text-shadow: 1px 1px 0 var(--dim);
}

/* Green rather than the usual red WIP chip: this one is a reward for
   having looked, not a warning that the page isn't finished. */
.window__tag--found {
  --lit: #c6f39a;
  --dim: #2f5c14;
  background: var(--green);
  color: var(--bg);
  text-shadow: none;
}

/* A listing pasted into the document: the paper's Times gives way to a
   fixed pitch, because a batch file set in a serif would be a different
   joke than the one intended. Scrolls rather than widening the window. */
.clurd__listing {
  margin: 0.7rem 0 0;
  padding: 0.6rem 0.7rem;
  overflow-x: auto;
  background: #efece0;
  border: 1px solid #d8d4c6;
  color: #3a3746;
  font-family: "Courier New", Courier, monospace;
  font-size: 0.72rem;
  line-height: 1.55;
}

/* A pasted picture: hairline frame and shadow, not the site's bevel. */
.window__image {
  display: block;
  width: 100%;
  max-width: 320px;
  height: auto;
  margin-top: 0.75rem;
  border: 1px solid #b8b3a4;
  box-shadow: 2px 2px 0 rgba(20, 18, 30, 0.2);
}

.clurd__page p {
  margin: 0.6rem 0 0;
  line-height: 1.55;
  font-size: 1rem;
  color: var(--paper-ink);
}

/* Status bar, counting a document that's always page 1 of 1. */
.clurd__status {
  --bevel: 2px;
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.35rem;
  margin: 0;
  padding: 0.2rem 0.25rem 0;
  color: var(--ink);
  font-size: 0.65rem;
  letter-spacing: 0.04em;
  user-select: none;
  -webkit-user-select: none;
}

.clurd__status span {
  padding: 0.15rem 0.4rem;
  background: var(--face);
  box-shadow: var(--sunken);
}

/* Last field is the language readout, historically the widest one. */
.clurd__status span:last-child {
  flex: 1;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.window__cta {
  --lit: #fffbb4;
  --dim: #8a6d0f;
  display: inline-block;
  margin-top: 0.9rem;
  background: var(--yellow);
  box-shadow: var(--raised);
  color: var(--bg);
  font-weight: 700;
  font-size: 0.85rem;
  text-decoration: none;
  padding: 0.45rem 0.9rem;
}

.window__cta:hover,
.window__cta:focus-visible {
  filter: brightness(1.1);
}

.window__cta:active {
  box-shadow: var(--sunken);
  translate: 1px 1px;
}

/* --- the taskbar ---------------------------------------------------------
   The strip along the bottom of the desktop: Start, a button per running
   window, a tray and a clock. Same machine grey as the folders, because it
   is the same machine.

   Two layouts, like everything else on this page. Above the breakpoint the
   desktop is a layer over <main> and the bar is pinned to the bottom of it
   — main.js fences dragged windows above it, reading its height off the
   element rather than a number written twice. Below the breakpoint the
   desktop is a stacked column and the bar sticks to the bottom of the
   viewport as you scroll, which is what a taskbar does anyway. */

/* The bag and achievements buttons keep the offset quest.css gives them
   on every other page. That used to clear the news bar; this page has no
   news bar, and the taskbar that stands there instead is shorter than the
   ticker was, so the same offset clears it with room to spare and nothing
   needs moving.

   The Start menu is the exception, and it can't be solved by moving
   anything: it rises out of the same bottom-left corner the bag button
   sits in and is far taller than any offset that would still leave the
   button on screen. Stacking can't settle it either — see .taskbar's
   z-index below — so the menu simply takes the corner for as long as it
   is open, exactly as it would take a desktop icon's. main.js sets the
   attribute alongside the menu's own hidden state, so the two can never
   disagree. */
[data-start-open] .quest__bag-btn,
[data-start-open] .quest__achievements-btn {
  display: none;
}

.taskbar {
  --face: #b4b0c6;
  --lit: #e6e3f0;
  --dim: #4b465f;
  --ink: #1e1b2b;
  position: sticky;
  bottom: 0;
  /* Above every window on the desktop. raiseFolder() in main.js counts
     upward from 3 and renormalises well below this, so no amount of
     clicking windows to the front can bury the bar they sit above.

     This number reaches no further than the desktop, whatever it says:
     .desktop is itself a stacking context (position + z-index: 1), so
     everything in here is sealed under that 1 as far as the rest of the
     page is concerned. Which is why the quest's floating buttons, at 30
     in the page's own stacking order, cannot be beaten from in here and
     are dealt with by data-start-open below instead. */
  z-index: 20;
  display: flex;
  align-items: stretch;
  gap: 0.25rem;
  margin-top: 2.5rem;
  padding: 3px;
  background: var(--face);
  box-shadow: var(--raised);
  border-top: var(--rule) solid var(--dim);
  /* The desktop layer is click-through; its furniture is not. */
  pointer-events: auto;
}

@media (min-width: 60rem) {
  .taskbar {
    position: absolute;
    inset: auto 0 0 0;
    margin-top: 0;
  }
}

/* Start: bold, with the four-pane flag. Held pressed while its menu is
   open, which is the only way to tell an open Start menu from a menu that
   is merely near the bottom left of the screen. */
.taskbar__start {
  --bevel: 2px;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  flex: 0 0 auto;
  padding: 0.25rem 0.6rem 0.25rem 0.35rem;
  background: var(--face);
  box-shadow: var(--raised);
  border: none;
  color: var(--ink);
  font: inherit;
  font-size: 0.75rem;
  font-weight: 700;
  cursor: var(--cursor-link);
}

.taskbar__start:hover {
  background: var(--lit);
}

.taskbar__start[aria-expanded="true"],
.taskbar__start:active {
  box-shadow: var(--sunken);
}

.taskbar__start[aria-expanded="true"] .taskbar__flag,
.taskbar__start:active .taskbar__flag {
  translate: 1px 1px;
}

/* The flag: four panes, drawn as a 2x2 conic sweep rather than four
   elements, skewed the way the original leaned into the wind. */
.taskbar__flag {
  flex: 0 0 auto;
  width: 1rem;
  height: 0.9rem;
  background:
    conic-gradient(from 0deg at 50% 50%,
      var(--red) 0 25%, var(--green) 0 50%,
      #3c7ddd 0 75%, var(--yellow) 0 100%);
  border: 1px solid var(--dim);
  transform: skewX(-8deg);
}

/* The running windows. min-width: 0 on the item and the button both, or a
   long path refuses to be truncated and pushes the clock off the bar. */
.taskbar__tasks {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: stretch;
  gap: 0.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.taskbar__tasks li {
  flex: 0 1 11rem;
  min-width: 0;
}

.taskbar__task {
  --bevel: 2px;
  display: block;
  width: 100%;
  padding: 0.25rem 0.5rem;
  background: var(--face);
  box-shadow: var(--raised);
  border: none;
  color: var(--ink);
  font: inherit;
  font-size: 0.7rem;
  text-align: left;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  cursor: var(--cursor-link);
}

.taskbar__task:hover {
  background: var(--lit);
}

/* The focused window's button is held in, and a shade lighter, exactly as
   it was. A minimised window's button is the plain raised one. */
.taskbar__task--on {
  box-shadow: var(--sunken);
  background: var(--lit);
  font-weight: 700;
}

.taskbar__task--on span {
  translate: 1px 1px;
  display: inline-block;
}

/* The tray: a sunken well holding a fake speaker icon and the clock. */
.taskbar__tray {
  --bevel: 2px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0;
  padding: 0.25rem 0.5rem;
  background: var(--face);
  box-shadow: var(--sunken);
  color: var(--ink);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
}

/* A speaker: a square with a cone cut off one side by a triangle clip. */
.taskbar__tray-icon {
  width: 0.75rem;
  height: 0.75rem;
  background: var(--ink);
  clip-path: polygon(0 30%, 35% 30%, 70% 0, 70% 100%, 35% 70%, 0 70%);
}

/* --- the Start menu ------------------------------------------------------
   Anchored to the bottom left of the desktop, over the taskbar's own
   corner. Only Find does anything; the rest are drawn disabled rather than
   left out, because a Start menu with one item in it is not a Start menu. */

.startmenu {
  --face: #b4b0c6;
  --lit: #e6e3f0;
  --dim: #4b465f;
  --ink: #1e1b2b;
  position: absolute;
  left: 0;
  bottom: 100%;
  z-index: 7;
  display: flex;
  width: 15rem;
  max-width: calc(100vw - 1rem);
  padding: var(--bevel);
  background: var(--face);
  box-shadow: var(--raised);
  border: var(--rule) solid var(--dim);
  pointer-events: auto;
}

.startmenu[hidden] {
  display: none;
}

/* The spine: the one detail that makes it unmistakable. Text running up
   the left edge over a gradient.

   writing-mode plus a half turn, rather than a rotate() — a rotated box
   still takes its unrotated space in the layout, so the transform version
   needed absolute positioning and a pile of guesses about its own height
   to land anywhere near right. Vertical writing mode makes the box
   genuinely tall and narrow, so the flex row below can simply centre it;
   vertical-rl reads top-to-bottom, and the 180 turn is what makes it read
   bottom-to-top, which is the way this particular strip always read. */
.startmenu__spine {
  flex: 0 0 1.7rem;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  margin: 0;
  padding-bottom: 0.7rem;
  background: linear-gradient(to bottom, #4d3f7a, #1b1830);
}

.startmenu__spine span {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  white-space: nowrap;
  color: var(--white);
  font-size: 0.85rem;
  letter-spacing: 0.02em;
  text-shadow: 1px 1px 0 #0d0a16;
}

.startmenu__spine b {
  font-weight: 700;
  color: var(--yellow);
}

.startmenu__list {
  flex: 1;
  min-width: 0;
  margin: 0;
  padding: 0.2rem 0;
  list-style: none;
}

.startmenu__rule {
  height: 0;
  margin: 0.25rem 0.3rem;
  border-top: 1px solid var(--dim);
  border-bottom: 1px solid var(--lit);
}

.startmenu__item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.4rem 0.5rem;
  background: none;
  border: none;
  color: var(--ink);
  font: inherit;
  font-size: 0.8rem;
  text-align: left;
  cursor: var(--cursor-link);
}

button.startmenu__item:hover,
button.startmenu__item:focus-visible {
  background: var(--clurd-blue, #2a3f8f);
  color: var(--white);
  outline: none;
}

/* Drawn, greyed, and honest about it: these are the hooks for the next
   pass rather than things pretending to be finished. */
.startmenu__item--dead {
  color: #6f6b84;
  cursor: var(--cursor-blocked);
}

.startmenu__sub {
  color: inherit;
  opacity: 0.75;
  font-size: 0.7rem;
}

.startmenu__arrow {
  margin-left: auto;
  font-size: 0.55rem;
}

/* One tile per entry, each a different scribble in the same 1rem box — no
   icon font and no extra requests, so they are drawn out of gradients the
   way .folder__icon above is. */
.startmenu__glyph {
  flex: 0 0 auto;
  width: 1.1rem;
  height: 1.1rem;
  background: var(--yellow-dark);
  border: 1px solid var(--dim);
}

.startmenu__glyph--programs {
  background:
    linear-gradient(to bottom, transparent 0 35%, var(--bg) 35% 45%, transparent 45%),
    var(--yellow-dark);
}

.startmenu__glyph--docs {
  background:
    repeating-linear-gradient(to bottom, var(--bg) 0 1px, transparent 1px 4px) 2px 3px / 60% 70% no-repeat,
    #f4f2ea;
}

/* A magnifier: a ring plus a stub of handle. */
.startmenu__glyph--find {
  background: #cfe6ff;
  border-radius: 50%;
  box-shadow: 0 0 0 1px var(--dim), 2px 2px 0 0 var(--ink);
}

.startmenu__glyph--help {
  background: #f4f2ea;
}

.startmenu__glyph--run {
  background:
    linear-gradient(to bottom, var(--bg) 0 30%, #f4f2ea 30%);
}

.startmenu__glyph--off {
  background: var(--red);
  border-radius: 50%;
}

/* --- Find: All Files -----------------------------------------------------
   The dialog inside the window: tabs, a criteria panel, and a results list
   that main.js rebuilds on every keystroke. */

.find__tabs {
  display: flex;
  gap: 2px;
  margin: 0 0 -1px;
  padding: 0;
}

.find__tab {
  --bevel: 2px;
  padding: 0.25rem 0.6rem;
  background: #9d99b2;
  box-shadow: var(--raised);
  color: #4b465f;
  font-size: 0.68rem;
}

/* The one tab that does anything sits a pixel proud of the others and in
   the panel's own colour, so it reads as the front of the stack. */
.find__tab--on {
  background: var(--face);
  color: var(--ink);
  font-weight: 700;
  padding-bottom: 0.35rem;
}

.find__panel {
  padding: 0.8rem 0.7rem 0.6rem;
  background: var(--face);
  box-shadow: var(--raised);
}

.find__form {
  margin: 0;
}

.find__row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.45rem;
}

.find__label {
  flex: 0 0 4.2rem;
  color: var(--ink);
  font-size: 0.72rem;
  text-align: right;
}

.find__input {
  --bevel: 2px;
  --lit: #e6e3f0;
  --dim: #4b465f;
  flex: 1;
  min-width: 0;
  padding: 0.3rem 0.4rem;
  background: #f4f2ea;
  box-shadow: var(--sunken);
  border: none;
  color: var(--bg);
  font: inherit;
  font-size: 0.78rem;
}

.find__input:focus-visible {
  outline: 2px solid var(--clurd-blue, #2a3f8f);
  outline-offset: -2px;
}

/* Look in: a field that is a field in every way except being one. */
.find__input--dead {
  flex: 1;
  background: #d9d6e4;
  color: #4b465f;
  cursor: var(--cursor-blocked);
}

.find__check {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--ink);
  font-size: 0.72rem;
}

.find__tick {
  --bevel: 2px;
  --lit: #e6e3f0;
  --dim: #4b465f;
  display: grid;
  place-items: center;
  width: 0.9rem;
  height: 0.9rem;
  background: #f4f2ea;
  box-shadow: var(--sunken);
  color: var(--bg);
  font-size: 0.6rem;
}

.find__buttons {
  display: flex;
  justify-content: flex-end;
  gap: 0.4rem;
  margin: 0.6rem 0 0;
}

.find__btn {
  --bevel: 2px;
  padding: 0.3rem 0.8rem;
  background: var(--face);
  box-shadow: var(--raised);
  border: none;
  color: var(--ink);
  font: inherit;
  font-size: 0.72rem;
  cursor: var(--cursor-link);
}

.find__btn:hover {
  background: var(--lit);
}

.find__btn:active {
  box-shadow: var(--sunken);
  translate: 1px 1px;
}

.find__results {
  margin-top: 0.7rem;
}

/* One column track shared by the header and every row, declared once here
   so the two can't drift apart. */
.find__cols,
.find__row-file {
  display: grid;
  grid-template-columns: minmax(0, 2.4fr) minmax(0, 2fr) 3.5rem minmax(0, 1.9fr);
  gap: 0.35rem;
}

.find__cols {
  margin: 0;
  padding: 0;
}

.find__col {
  --bevel: 2px;
  padding: 0.2rem 0.4rem;
  background: var(--face);
  box-shadow: var(--raised);
  color: var(--ink);
  font-size: 0.65rem;
  font-weight: 700;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* The well the results sit in: the page's navy, same rule as the icon
   wells above. Capped and scrolling, so a search that matches everything
   doesn't grow the window past the desktop. */
.find__well {
  --lit: var(--bg-lit);
  --dim: var(--bg-dim);
  max-height: 11rem;
  overflow-y: auto;
  padding: var(--bevel);
  background: var(--bg);
  box-shadow: var(--sunken);
}

.find__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.find__row-file {
  width: 100%;
  padding: 0.25rem 0.3rem;
  background: none;
  border: none;
  color: var(--yellow);
  font: inherit;
  font-size: 0.7rem;
  text-align: left;
  text-decoration: none;
  cursor: var(--cursor-link);
}

.find__row-file:hover,
.find__row-file:focus-visible {
  background: var(--clurd-blue, #2a3f8f);
  color: var(--white);
  outline: none;
}

.find__row-file span {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* The filename keeps its little document tick so a row reads as a file
   rather than a table cell. */
.find__row-file .find__name::before {
  content: "";
  display: inline-block;
  width: 0.5rem;
  height: 0.62rem;
  margin-right: 0.35rem;
  vertical-align: -1px;
  background: #f4f2ea;
  border: 1px solid var(--bg-dim);
}

.find__muted {
  color: var(--muted);
}

/* Nothing matched. Not an error — a shrug, in the OS's own voice. */
.find__none {
  margin: 0;
  padding: 0.6rem 0.5rem;
  color: var(--muted);
  font-size: 0.72rem;
  line-height: 1.5;
}

/* Below the breakpoint there is no room for four columns; size and type
   are the two nobody came for. */
@media (max-width: 47.999rem) {
  .find__cols,
  .find__row-file {
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  }

  .find__col--size,
  .find__col--type,
  .find__row-file .find__size,
  .find__row-file .find__type {
    display: none;
  }

  .find__label {
    flex-basis: 3.4rem;
  }
}

/* --- the boot ------------------------------------------------------------
   POST, then the splash. Fixed over the whole viewport rather than over
   <main> alone: this is the moment the page stops being a page, and
   leaving the site's own bars visible around it would undercut exactly
   the thing it is there to say.

   Shown once a visit by main.js and never by CSS alone — with the script
   absent the markup stays hidden and the desktop is simply there. */

/* Absolute inside <main>, which is to say exactly the screen and not one
   pixel of the top bar above it. The bar is the website; this is a
   machine drawn inside it, and a boot that swallowed the nav would read
   as the site restarting rather than as the computer on the page.

   Absolute rather than fixed, and measured by CSS rather than by reading
   the bar's height in script: the bar wraps to two or three rows on a
   narrow screen, so its height is not a number worth chasing, and <main>
   is already exactly the box we want. */
.boot {
  position: absolute;
  inset: 0;
  /* Over everything else in here, quest.css's own top layer (60, the skip
     button) included — a machine that has not finished starting up has
     nothing on it. It reaches no further than <main>, though: the rule
     below seals this element's stacking inside it for the duration, so
     the top bar's own layer stays clear above. */
  z-index: 70;
  display: grid;
  background: #05060a;
  color: #cfd6e4;
  font-family: "Courier New", Courier, monospace;
  cursor: var(--cursor-default);
}

.boot[hidden] {
  display: none;
}

/* For the length of the boot only, and both halves matter.

   isolation makes <main> a stacking context, which keeps the 70 above
   from outranking the top bar's 5 — without it that number is measured
   against the whole page and the bar goes under. It is set here rather
   than on <main> permanently because an open document window (44, fixed)
   is meant to cover the bar, and sealing <main> for good would quietly
   take that away.

   overflow stops the page scrolling out from under a screen that is
   absolutely positioned against <main>'s visible box. It beats
   styles.css's own `.has-chrome > main { overflow-y: auto }` on source
   order, both being one class and one element. */
[data-booting] main {
  isolation: isolate;
  overflow: hidden;
}

/* Leaving: the CRT collapsing to a line and out, which is also the shape
   the splash hands over on. */
.boot--off {
  animation: boot-off 320ms steps(6, end) both;
}

@keyframes boot-off {
  0%   { opacity: 1; scale: 1 1; }
  60%  { opacity: 1; scale: 1 0.06; }
  100% { opacity: 0; scale: 0.4 0.01; }
}

.boot__screen {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: clamp(0.9rem, 4vw, 2.5rem);
  /* A very slight green cast and a vignette — a monitor, not a text file. */
  background:
    radial-gradient(120% 100% at 50% 40%, rgba(90, 130, 110, 0.10), transparent 70%),
    #05060a;
}

.boot__screen[hidden] {
  display: none;
}

/* The corner badge every real POST screen had one of — an energy-board
   logo, a graphics-card maker's mark, whoever paid for the space. Ours is
   Radical Systems, planted top right the same way theirs always were:
   sharp-edged pixel art, not smoothed to fit a vector-era screen. */
.boot__oem-logo {
  position: absolute;
  top: clamp(0.9rem, 4vw, 2.5rem);
  right: calc(clamp(3rem, 14vw, 7rem) + 20px);
  width: clamp(10rem, 32vw, 20rem);
  height: auto;
  image-rendering: pixelated;
  opacity: 0.92;
}

.boot__post {
  margin: 0;
  /* Reserved on the right so a long line wraps clear of the badge rather
     than running under it. */
  padding-right: clamp(11rem, 36vw, 22rem);
  font-size: clamp(1rem, 2.6vw, 1.55rem);
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* The cursor block on the last line, which is what makes a still frame of
   text read as a machine still doing something. */
.boot__post::after {
  content: "";
  display: inline-block;
  width: 0.55em;
  height: 1em;
  vertical-align: -0.15em;
  background: #cfd6e4;
  animation: boot-caret 1s steps(1, end) infinite;
}

@keyframes boot-caret {
  0%, 50% { opacity: 1; }
  50.01%, 100% { opacity: 0; }
}

.boot__skip {
  margin: 1.5rem 0 0;
  color: #5d6678;
  font-size: clamp(0.6rem, 1.5vw, 0.75rem);
  letter-spacing: 0.08em;
}

/* The splash: the wordmark, a line of small print, and a bar that runs
   without ever claiming to measure anything. */
.boot__splash {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 0.6rem;
  padding: 1.5rem;
  background: linear-gradient(160deg, #1b1830 0%, #3b2f63 55%, #16121f 100%);
  text-align: center;
}

.boot__splash[hidden] {
  display: none;
}

.boot__logo {
  display: grid;
  justify-items: center;
  gap: 0.2rem;
  margin: 0;
  font-family: Verdana, Geneva, sans-serif;
}

.boot__flag {
  width: 3.2rem;
  height: 2.7rem;
  margin-bottom: 0.5rem;
  background:
    conic-gradient(from 0deg at 50% 50%,
      var(--red) 0 25%, var(--green) 0 50%,
      #3c7ddd 0 75%, var(--yellow) 0 100%);
  border: 2px solid #16121f;
  transform: skewX(-10deg);
  box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.45);
}

.boot__brand {
  color: var(--white);
  font-size: clamp(0.8rem, 2.4vw, 1.05rem);
  letter-spacing: 0.02em;
  text-shadow: 2px 2px 0 #0d0a16;
}

.boot__os {
  color: var(--yellow);
  font-size: clamp(1.6rem, 6vw, 3rem);
  font-weight: 700;
  line-height: 1;
  text-shadow: 3px 3px 0 #0d0a16;
}

.boot__year {
  margin-left: 0.3rem;
  color: var(--white);
}

.boot__tagline {
  margin: 0.4rem 0 1rem;
  color: var(--muted);
  font-family: Verdana, Geneva, sans-serif;
  font-size: clamp(0.62rem, 1.7vw, 0.75rem);
}

/* A bar with no percentage behind it: a block sliding left to right under
   a sunken groove, which is all the era's ever meant either. */
.boot__bar {
  --bevel: 2px;
  --lit: #7d78ad;
  --dim: #0d0a16;
  display: block;
  width: min(20rem, 70vw);
  height: 1.1rem;
  padding: 3px;
  background: #16121f;
  box-shadow: var(--sunken);
  overflow: hidden;
}

.boot__bar-run {
  display: block;
  width: 30%;
  height: 100%;
  background: repeating-linear-gradient(
    to right,
    var(--yellow) 0 0.55rem,
    transparent 0.55rem 0.75rem
  );
  animation: boot-bar 1.1s linear infinite;
}

@keyframes boot-bar {
  from { translate: -110% 0; }
  to   { translate: 340% 0; }
}

/* The site's own floating furniture has no business on top of a machine
   that has not finished starting up. The attribute is set on <html> by
   main.js for exactly as long as the boot lasts. */
[data-booting] .quest__bag-btn,
[data-booting] .quest__achievements-btn,
[data-booting] .music {
  display: none;
}

/* Still appears, just doesn't animate in — same as quest.css's panels. */
@media (prefers-reduced-motion: reduce) {
  .window {
    animation: none;
  }

  /* The boot is skipped outright rather than shown still — it is a
     sequence of timed reveals and nothing else, so there is no static
     version of it worth watching. main.js checks the same query and
     marks the machine booted without playing anything; these are here
     for the case where it plays anyway. */
  .boot--off,
  .boot__bar-run,
  .boot__post::after {
    animation: none;
  }
}
