/* ==========================================================================
   WINDOW — the shell, its bars, its menus and its status strip
   --------------------------------------------------------------------------
   CONTENTS
     01  the frame, and closing / minimising it
     02  title bar
     03  menu bar
     04  menu panels
     05  sliders and two-column tables inside menus
     06  status bar
   ========================================================================== */


/* ==========================================================================
   01  THE FRAME
   The bevel belongs to the window. The panes inside it are flat, separated
   only by the rules the workspace grid shows through, so there is one raised
   object on the desktop rather than three nested ones.
   ========================================================================== */

.window {
  position: relative;
  /* Above the backdrop. The backdrop exists to close a menu when you click
     the desktop; it should not be swallowing clicks meant for the close box,
     the page arrows or a fold control. Anything inside the window is now
     reachable whether or not a menu is open. */
  z-index: 56;
  /* THE DESIGN SIZE, AND ONLY THE DESIGN SIZE.

     This used to be min(1280px, 100vw - 40px) — the window squeezed its own
     panes when the viewport was narrower, so a smaller display did not get
     the site smaller, it got a different site: three columns at three
     different proportions, the status bar cells crowding, the outline
     wrapping. Below 1180 x 700 a media query gave up and blanked the page
     entirely, which is how visitors on an ordinary laptop were shown a
     notice instead of a website.

     The size is fixed here and the fitting is done by --fit, a plain scale
     factor the script writes onto <html> (see fitStage in faux.js). Every
     length inside the frame keeps its designed value and the whole object is
     drawn at one multiple of it, so the layout is identical at every viewport
     instead of merely similar.

     These two numbers are therefore the BASE size rather than the maximum:
     --fit goes above 1 on a display with room to spare, and the window grows
     to fill it rather than sitting small in the middle of it. */
  width:  1280px;
  height: 872px;
  transform: scale(var(--fit, 1));
  /* Centred on itself: the desktop grid centres the layout box, the scale
     shrinks it about that same centre, and the result stays centred. */
  transform-origin: center center;
  display: flex;
  flex-direction: column;

  background: var(--face);
  border: 2px solid var(--ink);
  /* A small radius, and overflow clipped so the title bar and status strip
     are cut by the same curve rather than poking through the corners. The
     outer drop shadow is unaffected — box-shadow is not clipped by overflow.
     This is the one place the blocky rule is relaxed; everything inside the
     frame still has square corners. */
  border-radius: 7px;
  overflow: hidden;
  box-shadow:
    inset  2px  2px 0 0 var(--edge-lt),
    inset -2px -2px 0 0 var(--edge-dk),
    var(--drop-lg);
}

/* Minimised: the bars alone, and — the point — still where the window was.
   The desktop centres its contents, so simply shrinking the window would
   float it up to the middle of the screen. align-self keeps the top edge on
   the line it was already on, so it rolls up in place. */
/* The origin moves to the top edge for this one case. Rolled up, the layout
   box is only as tall as the two bars, and scaling that about its centre
   would pull its top edge downward by half the shrinkage — the window would
   creep down the screen as --fit fell. Anchoring the scale to the top edge
   means the margin below decides where the top sits and nothing else does,
   so the rolled-up window is on the same line as the full one at any fit. */
body:has(#win-min:checked) .window {
  height: auto;
  align-self: start;
  transform-origin: center top;
  margin-top: calc((100vh - 872px * var(--fit, 1)) / 2);
}
body:has(#win-min:checked) .workspace,
body:has(#win-min:checked) .statusbar { display: none; }

/* AN OPEN MENU HAS TO SURVIVE BEING ROLLED UP.

   The menu was never actually closing. .window clips to `overflow: hidden`
   so the frame's radius cuts the bars, and .mpanel is absolutely positioned
   inside the menu bar — so it hangs below it and is clipped by the window.
   At full height there is room and nothing shows; rolled up, the window is
   only a title bar and a menu bar tall, and the whole panel fell outside it
   and was clipped away. The radio stayed checked and the button stayed lit,
   but there was nothing to see, which reads as the menu having closed.

   So the clip is released while minimised. The two bars then have to round
   their own outer corners, since the frame is no longer doing it for them:
   2px border inside a 7px radius leaves 5px. */
body:has(#win-min:checked) .window { overflow: visible; }
body:has(#win-min:checked) .titlebar {
  border-top-left-radius: 5px;
  border-top-right-radius: 5px;
}
body:has(#win-min:checked) .menubar {
  border-bottom-left-radius: 5px;
  border-bottom-right-radius: 5px;
}

/* closed: gone, with an icon left behind to open it again */
body:has(#win-closed:checked) .window { display: none; }

/* ---------------------------------------------------------------------------
   THE DESKTOP GRID
   An invisible 88 x 96 lattice pinned to the top-left, the way a desktop has
   always worked. Icons are placed on it by cell rather than by pixel, so an
   icon dropped anywhere lands square, and a second app added later just
   takes the next free cell. The script does the dragging; the grid is the
   only thing that decides where an icon can come to rest.
   --------------------------------------------------------------------------- */

body { --cell-w: 88px; --cell-h: 96px; }

.deskgrid {
  position: fixed;
  inset: 0;
  z-index: 5;                    /* under the window, over the wallpaper */
  pointer-events: none;
}

.deskicon {
  display: none;
  position: absolute;
  left: calc(var(--gx, 0) * var(--cell-w));
  top:  calc(var(--gy, 0) * var(--cell-h));
  width: var(--cell-w);
  /* Shrinks with the window, or the icon is the one thing on the desktop
     still at full size once --fit drops. The origin is the top-left corner
     on purpose: the grid cell decides where the plate is anchored and the
     drag maths in faux.js reads pointer position against cell size, neither
     of which knows or needs to know how large the plate is drawn. Scaling
     about the centre would move the plate off its own anchor and the icon
     would land a few pixels from where it was dropped. */
  transform: scale(var(--fit, 1));
  transform-origin: left top;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 10px 4px 8px;
  /* THE ICON CARRIES ITS OWN GROUND.

     It used to be drawn in --light directly on the desktop, which cannot
     work: the desktop is one of the same five palette colours, and on the
     light ground it was --light on a darkened --light. That measured under
     3:1 in all thirty-nine palettes, and Randomize Pattern now picks the
     ground at random, so it came up regularly rather than only if you chose
     it. No fixed palette colour can be safe here — the ground can be any of
     the five at any darkness — so the icon stops relying on one and brings
     its own face, ink and border instead. The 2px border is the guarantee:
     it is drawn in the darkest value the scheme has, so the plate has an
     edge whatever it is sitting on. */
  background: var(--face);
  border: 2px solid var(--ink);
  box-shadow: var(--drop-md);
  color: var(--ink);
  font-size: var(--f-sm);
  letter-spacing: 1px;
  text-align: center;
  pointer-events: auto;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
}
body:has(#win-closed:checked) .deskicon { display: flex; }
.deskicon.is-dragging { opacity: 0.7; }
.deskicon:hover { color: var(--sel-ink); background: var(--sel); }
.deskicon svg {
  width: 48px; height: 48px;
  shape-rendering: crispEdges;
}
.deskicon .i-case { fill: var(--ink); }
.deskicon .i-screen { fill: var(--pop); }


/* ==========================================================================
   02  TITLE BAR
   Name, pinstripes, and the two pieces of window furniture — nothing else.
   Every setting lives on the menu bar below.
   ========================================================================== */

.titlebar {
  flex: none;
  height: 28px;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 5px;
  color: var(--accent-ink);
  background-color: var(--accent);
  border-bottom: 2px solid var(--ink);
}

.tb-grip {
  flex: 1;
  min-width: 12px;
  height: 11px;
  background-image: repeating-linear-gradient(
    180deg,
    color-mix(in srgb, var(--accent-ink) 62%, transparent) 0 1px,
    transparent 1px 3px);
}

/* the name is also the way back up when the window is rolled down */
.tb-title { cursor: pointer; }
.tb-title:hover { color: var(--sel-ink); background: var(--sel); }

.tb-title {
  font-family: var(--f-head);
  font-weight: 700;
  font-size: var(--f-bar);
  line-height: var(--f-bar);
  letter-spacing: 1px;
  padding: 0 12px;
  background: var(--accent);     /* knocks a hole in the pinstripe */
  white-space: nowrap;
}

/* close on the left, minimise on the right */
.tb-btn {
  flex: none;
  width: 15px; height: 15px;
  background: var(--accent);
  border: 1px solid var(--ink);
  box-shadow:
    inset  1px  1px 0 0 color-mix(in srgb, var(--accent-ink) 55%, transparent),
    inset -1px -1px 0 0 var(--accent-dk);
  cursor: pointer;
}
.tb-btn:hover { background: var(--sel); }
.tb-btn:active {
  box-shadow:
    inset  1px  1px 0 0 var(--accent-dk),
    inset -1px -1px 0 0 color-mix(in srgb, var(--accent-ink) 55%, transparent);
}
/* The close X, drawn as two stepped diagonals of 1px blocks inside a 7x7
   field with 3px of padding around it — a glyph would depend on the font and
   a rotated rule would land between pixels. */
.tb-btn.is-close::after {
  content: "";
  display: block;
  width: 7px; height: 7px;
  margin: 3px 0 0 3px;
  background: var(--ink);
  clip-path: polygon(
    0 0, 1px 0, 3px 2px, 4px 2px, 6px 0, 7px 0, 7px 1px, 5px 3px, 5px 4px,
    7px 6px, 7px 7px, 6px 7px, 4px 5px, 3px 5px, 1px 7px, 0 7px, 0 6px,
    2px 4px, 2px 3px, 0 1px);
}

/* the minimise bar, sitting on the baseline of the same field */
.tb-btn.is-min::after {
  content: "";
  display: block;
  width: 7px; height: 2px;
  margin: 8px 0 0 3px;
  background: var(--ink);
}


/* ==========================================================================
   03  MENU BAR
   Intro, Sounds, Font, Desktop, Theme, and the brightness switch.
   ========================================================================== */

.menubar {
  flex: none;
  height: 30px;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 5px;
  background: var(--face-alt);
  border-bottom: 2px solid var(--ink);
  position: relative;
  z-index: 60;                   /* stays live while a menu is open */
}
.mspacer { flex: 1; }

.menuwrap { position: relative; display: flex; }

/* A radio cannot untick itself, so a second label sits invisibly over the
   button while that menu is open and points at mn-none. Clicking the button
   again therefore closes it, which is what a menu bar has always done. */
.mclose { display: none; position: absolute; inset: 0; z-index: 3; cursor: pointer; }
body:has(#mn-intro:checked)  .menuwrap:has([for="mn-intro"])  .mclose,
body:has(#mn-sound:checked)  .menuwrap:has([for="mn-sound"])  .mclose,
body:has(#mn-font:checked)   .menuwrap:has([for="mn-font"])   .mclose,
body:has(#mn-desk:checked)   .menuwrap:has([for="mn-desk"])   .mclose,
body:has(#mn-theme:checked)  .menuwrap:has([for="mn-theme"])  .mclose,
body:has(#mn-screen:checked) .menuwrap:has([for="mn-screen"]) .mclose { display: block; }

/* the accelerator letter, bold in the button and matching the key that
   opens that menu */
/* the name is one flex item so the button gap falls between the label
   and the caret, not inside the word */
/* One label, one flex item. Both .mbtn and .mopt are flex rows with a gap,
   so an accelerator marked up as <b> inside the text becomes a SECOND item
   and the gap opens between the letter and the rest of the word — "I ntro"
   on the menu bar, "R andomize" on the rows below. Wrapping the whole label
   in one element puts the gap back where it belongs, between the tick and
   the text, and leaves the <b> as ordinary inline markup inside it. */
.mbtn .lbl,
.mopt .lbl { white-space: nowrap; }
/* The accelerator letter.
   Bold was the first attempt and does nothing here: Departure ships a single
   400 weight, so there is no bold face to switch to, and synthetic emboldening
   of a pixel design either does not fire or smears the stems. An underline is
   the older convention for a menu accelerator anyway — it is what Windows has
   always drawn — and it renders identically in every face on the list. The
   weight is left in for the faces that do have a bold. */
.acc {
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}

.mbtn {
  gap: 8px;
  height: 22px;
  padding: 0 10px;
  font-size: var(--f-size);
}

/* the pixel caret on a button that opens a panel */
.mbtn.has-panel::after {
  content: "";
  width: 7px; height: 4px;
  background: currentColor;
  clip-path: polygon(0 0, 7px 0, 7px 1px, 6px 1px, 6px 2px, 5px 2px,
                     5px 3px, 2px 3px, 2px 2px, 1px 2px, 1px 1px, 0 1px);
}

body:has(#mn-intro:checked) .mbtn[for="mn-intro"],
body:has(#mn-sound:checked) .mbtn[for="mn-sound"],
body:has(#mn-font:checked)  .mbtn[for="mn-font"],
body:has(#mn-desk:checked)  .mbtn[for="mn-desk"],
body:has(#mn-screen:checked) .mbtn[for="mn-screen"],
body:has(#mn-theme:checked) .mbtn[for="mn-theme"] {
  color: var(--sel-ink);
  background: var(--sel);
  box-shadow: var(--bevel-down);
}

.backdrop { display: none; position: fixed; inset: 0; z-index: 55; }
body:has(#mn-intro:checked, #mn-sound:checked, #mn-font:checked,
         #mn-desk:checked,  #mn-theme:checked,
         #mn-screen:checked) .backdrop { display: block; }

/* the brightness switch names where it will take you. Both words sit in the
   same fixed box so the button never changes width when it is pressed. */
.modebtn {
  height: 22px;
  width: 74px;
  padding: 0;
  justify-content: center;
  font-size: var(--f-size);
}
.mode-to-dark  { display: inline; }
.mode-to-light { display: none; }
body:has(#mode-dark:checked) .mode-to-dark  { display: none; }
body:has(#mode-dark:checked) .mode-to-light { display: inline; }


/* ==========================================================================
   04  MENU PANELS
   ========================================================================== */

.mpanel {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  /* Measured against the window, not the viewport. The window is a fixed
     872 tall and the menu bar it hangs from ends 130 down, so 742 is the
     room there actually is. It used to read 100vh - 130px, which was the
     same answer only while the window filled the screen; now that the frame
     scales to fit, a viewport shorter than 872 would have clipped the menu
     to less than the window could show — a menu cut short inside a window
     with visible space below it.

     The reading layout never scrolls; a forty-item menu is a different
     thing, and clipping the last section off the bottom is worse than
     letting the panel scroll the way an OS menu always has. */
  max-height: 742px;
  overflow-y: auto;
  overscroll-behavior: contain;
  z-index: 70;
  width: 300px;
  padding: 3px;
  background: var(--face);
  border: 2px solid var(--ink);
  box-shadow: var(--bevel-up), var(--drop-lg);
  color: var(--ink);
}
/* Panels are sized to what is in them. The Screen menu is the plain width
   and holds four sliders; with the labels and readouts a size up, its track
   was down to about 130px, so it takes the wide width now. */
.mpanel.is-wide { width: 396px; }
.mpanel.is-widest { width: 560px; }

/* three columns, for the forty schemes */
/* The Theme menu is the one that ran small. Thirty-nine names across three
   columns had been squeezed down to --f-sm, the size meant for tags and
   readouts, so the palette names were the smallest text in the interface
   while being the thing you actually read. They take the body size, with the
   padding and swatch height brought up to match. */
/* minmax(0, 1fr), not 1fr. A bare 1fr track keeps an automatic MINIMUM, so
   any name too long for its share widens the column — and because each
   family is its own grid, a family containing Terracotta ended up with
   different column edges from one containing Kiln, and the names did not
   line up down the menu. minmax(0, …) removes that minimum, so all three
   columns are exactly equal in every family and every name starts on the
   same two verticals. The panel is wider to match, so the longest names
   still fit on one line rather than being clipped into alignment. */
.mcols3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px; }
.mcols3 .mopt { font-size: var(--f-size); gap: 5px; padding: 3px 5px; }
.mcols3 .chips { flex: none; }
.mcols3 .chips i { width: 6px; height: 13px; }

/* two columns inside a panel, for lists long enough to overrun the window */
.mcols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px; }
.mcols .mopt { font-size: var(--f-size); gap: 6px; padding: 3px 6px; }

body:has(#mn-intro:checked) #panel-intro,
body:has(#mn-sound:checked) #panel-sound,
body:has(#mn-font:checked)  #panel-font,
body:has(#mn-desk:checked)  #panel-desk,
body:has(#mn-theme:checked) #panel-theme,
body:has(#mn-screen:checked) #panel-screen { display: block; }

.mpanel-h {
  font-size: var(--f-label);
  line-height: 19px;
  letter-spacing: 1px;
  color: var(--ink-dim);
  padding: 3px 8px 4px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 3px;
}
.mopt + .mpanel-h,
.mrow + .mpanel-h { margin-top: 6px; }

/* rows that are real <button> elements rather than labels: strip the
   browser default so they match the rows either side of them */
button.mopt {
  width: 100%;
  font-family: inherit;
  color: inherit;
  text-align: left;
  background: none;
  border: 0;
  -webkit-appearance: none;
  appearance: none;
}
button.mopt { font-size: var(--f-size); }

.mopt {
  gap: 9px;
  padding: 4px 8px;
  line-height: 20px;
  font-size: var(--f-size);
}
/* a switch that lives inside a slider row rather than on its own line */
.mopt.is-inline { flex: none; padding: 2px 8px; }

.mopt .tag {
  margin-left: auto;
  padding-left: 10px;
  font-size: var(--f-sm);
  color: var(--ink-dim);
}
.mopt:hover .tag { color: var(--sel-ink); }

/* THE TICK that marks a live choice. Five 2px squares rather than a glyph or
   a clip-path, so it steps like pixel art and never depends on the font. */
.mopt::before {
  content: "";
  flex: none;
  width: 2px; height: 2px;
  margin-right: 5px;
}

body { --tick: 2px 2px 0 0, 4px 0 0 0, 6px -2px 0 0, 8px -4px 0 0; }

/* THE TICK LIST — generated, see build_lists.py.

   Four separate groups of these had accumulated, added a version at a time as
   the menus grew, and they overlapped: the kit, click and page-turn rows were
   each listed twice over. They also kept falling behind — one group carried a
   note admitting the Buttons, Font and Fit rows had shown no state at all
   until someone noticed, and rules for #fn-2, #kit-chip and the three #hw-*
   options outlived the options themselves.

   CSS cannot ask "is the input this label points at checked" without naming
   both ends, so a list is unavoidable; it just should not be typed. Every
   option gets one, bar the two that do something rather than record a state:
   Reboot, a checkbox used as a trigger, and the file picker. */
/* TICK-LIST-START */
body:has(#isp-1:checked) .mopt[for="isp-1"]::before,
body:has(#isp-2:checked) .mopt[for="isp-2"]::before,
body:has(#isp-3:checked) .mopt[for="isp-3"]::before,
body:has(#bsp-1:checked) .mopt[for="bsp-1"]::before,
body:has(#bsp-2:checked) .mopt[for="bsp-2"]::before,
body:has(#bsp-3:checked) .mopt[for="bsp-3"]::before,
body:has(#snd-boot:checked) .mopt[for="snd-boot"]::before,
body:has(#mute:checked) .mopt[for="mute"]::before,
body:has(#snd-hover:checked) .mopt[for="snd-hover"]::before,
body:has(#snd-click:checked) .mopt[for="snd-click"]::before,
body:has(#snd-page:checked) .mopt[for="snd-page"]::before,
body:has(#kit-mech:checked) .mopt[for="kit-mech"]::before,
body:has(#kit-soft:checked) .mopt[for="kit-soft"]::before,
body:has(#kit-glass:checked) .mopt[for="kit-glass"]::before,
body:has(#kit-metal:checked) .mopt[for="kit-metal"]::before,
body:has(#kit-paper:checked) .mopt[for="kit-paper"]::before,
body:has(#kit-custom:checked) .mopt[for="kit-custom"]::before,
body:has(#hk-mech:checked) .mopt[for="hk-mech"]::before,
body:has(#hk-soft:checked) .mopt[for="hk-soft"]::before,
body:has(#hk-glass:checked) .mopt[for="hk-glass"]::before,
body:has(#hk-metal:checked) .mopt[for="hk-metal"]::before,
body:has(#hk-paper:checked) .mopt[for="hk-paper"]::before,
body:has(#ck-mech:checked) .mopt[for="ck-mech"]::before,
body:has(#ck-soft:checked) .mopt[for="ck-soft"]::before,
body:has(#ck-glass:checked) .mopt[for="ck-glass"]::before,
body:has(#ck-metal:checked) .mopt[for="ck-metal"]::before,
body:has(#ck-paper:checked) .mopt[for="ck-paper"]::before,
body:has(#pk-mech:checked) .mopt[for="pk-mech"]::before,
body:has(#pk-soft:checked) .mopt[for="pk-soft"]::before,
body:has(#pk-glass:checked) .mopt[for="pk-glass"]::before,
body:has(#pk-metal:checked) .mopt[for="pk-metal"]::before,
body:has(#pk-paper:checked) .mopt[for="pk-paper"]::before,
body:has(#pat-1:checked) .mopt[for="pat-1"]::before,
body:has(#pat-2:checked) .mopt[for="pat-2"]::before,
body:has(#pat-3:checked) .mopt[for="pat-3"]::before,
body:has(#pat-4:checked) .mopt[for="pat-4"]::before,
body:has(#pat-5:checked) .mopt[for="pat-5"]::before,
body:has(#pat-6:checked) .mopt[for="pat-6"]::before,
body:has(#pat-7:checked) .mopt[for="pat-7"]::before,
body:has(#pat-8:checked) .mopt[for="pat-8"]::before,
body:has(#pat-9:checked) .mopt[for="pat-9"]::before,
body:has(#pat-10:checked) .mopt[for="pat-10"]::before,
body:has(#pat-11:checked) .mopt[for="pat-11"]::before,
body:has(#pat-12:checked) .mopt[for="pat-12"]::before,
body:has(#pat-13:checked) .mopt[for="pat-13"]::before,
body:has(#pat-14:checked) .mopt[for="pat-14"]::before,
body:has(#bg-0:checked) .mopt[for="bg-0"]::before,
body:has(#fit-cover:checked) .mopt[for="fit-cover"]::before,
body:has(#fit-contain:checked) .mopt[for="fit-contain"]::before,
body:has(#fit-tile:checked) .mopt[for="fit-tile"]::before,
body:has(#th-1:checked) .mopt[for="th-1"]::before,
body:has(#th-2:checked) .mopt[for="th-2"]::before,
body:has(#th-3:checked) .mopt[for="th-3"]::before,
body:has(#th-4:checked) .mopt[for="th-4"]::before,
body:has(#th-5:checked) .mopt[for="th-5"]::before,
body:has(#th-6:checked) .mopt[for="th-6"]::before,
body:has(#th-7:checked) .mopt[for="th-7"]::before,
body:has(#th-8:checked) .mopt[for="th-8"]::before,
body:has(#th-9:checked) .mopt[for="th-9"]::before,
body:has(#th-10:checked) .mopt[for="th-10"]::before,
body:has(#th-11:checked) .mopt[for="th-11"]::before,
body:has(#th-12:checked) .mopt[for="th-12"]::before,
body:has(#th-13:checked) .mopt[for="th-13"]::before,
body:has(#th-14:checked) .mopt[for="th-14"]::before,
body:has(#th-15:checked) .mopt[for="th-15"]::before,
body:has(#th-16:checked) .mopt[for="th-16"]::before,
body:has(#th-17:checked) .mopt[for="th-17"]::before,
body:has(#th-18:checked) .mopt[for="th-18"]::before,
body:has(#th-19:checked) .mopt[for="th-19"]::before,
body:has(#th-20:checked) .mopt[for="th-20"]::before,
body:has(#th-21:checked) .mopt[for="th-21"]::before,
body:has(#th-22:checked) .mopt[for="th-22"]::before,
body:has(#th-23:checked) .mopt[for="th-23"]::before,
body:has(#th-24:checked) .mopt[for="th-24"]::before,
body:has(#th-25:checked) .mopt[for="th-25"]::before,
body:has(#th-26:checked) .mopt[for="th-26"]::before,
body:has(#th-27:checked) .mopt[for="th-27"]::before,
body:has(#th-28:checked) .mopt[for="th-28"]::before,
body:has(#th-29:checked) .mopt[for="th-29"]::before,
body:has(#th-30:checked) .mopt[for="th-30"]::before,
body:has(#th-31:checked) .mopt[for="th-31"]::before,
body:has(#th-32:checked) .mopt[for="th-32"]::before,
body:has(#th-33:checked) .mopt[for="th-33"]::before,
body:has(#th-34:checked) .mopt[for="th-34"]::before,
body:has(#th-35:checked) .mopt[for="th-35"]::before,
body:has(#th-36:checked) .mopt[for="th-36"]::before,
body:has(#th-37:checked) .mopt[for="th-37"]::before,
body:has(#th-38:checked) .mopt[for="th-38"]::before,
body:has(#th-39:checked) .mopt[for="th-39"]::before,
body:has(#con-1:checked) .mopt[for="con-1"]::before,
body:has(#con-2:checked) .mopt[for="con-2"]::before,
body:has(#bs-1:checked) .mopt[for="bs-1"]::before,
body:has(#bs-2:checked) .mopt[for="bs-2"]::before,
body:has(#bs-3:checked) .mopt[for="bs-3"]::before,
body:has(#bs-4:checked) .mopt[for="bs-4"]::before,
body:has(#bs-5:checked) .mopt[for="bs-5"]::before,
body:has(#crt-0:checked) .mopt[for="crt-0"]::before,
body:has(#crt-1:checked) .mopt[for="crt-1"]::before,
body:has(#crt-2:checked) .mopt[for="crt-2"]::before,
body:has(#crv-0:checked) .mopt[for="crv-0"]::before,
body:has(#crv-sq:checked) .mopt[for="crv-sq"]::before,
body:has(#crv-ci:checked) .mopt[for="crv-ci"]::before,
body:has(#show-nav:checked) .mopt[for="show-nav"]::before,
body:has(#show-scroll:checked) .mopt[for="show-scroll"]::before,
body:has(#scroll-all:checked) .mopt[for="scroll-all"]::before,
body:has(#fn-1:checked) .mopt[for="fn-1"]::before,
body:has(#fn-3:checked) .mopt[for="fn-3"]::before,
body:has(#fn-4:checked) .mopt[for="fn-4"]::before,
body:has(#fn-5:checked) .mopt[for="fn-5"]::before,
body:has(#fn-8:checked) .mopt[for="fn-8"]::before,
body:has(#fs-1:checked) .mopt[for="fs-1"]::before,
body:has(#fs-2:checked) .mopt[for="fs-2"]::before
/* TICK-LIST-END */
{
  background: currentColor;
  box-shadow: var(--tick);
}

/* five-chip palette preview */
.chips { flex: none; display: flex; border: 1px solid var(--ink); }
.chips i { display: block; width: 9px; height: 13px; }

/* the file picker, dressed as a menu row */
.mfile { position: absolute; width: 1px; height: 1px; opacity: 0; }
.mrow {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 4px 8px 4px 16px;
  font-size: var(--f-label);
  line-height: 20px;
  color: var(--ink-dim);
}
.mname {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--ink);
}


/* ==========================================================================
   05  SLIDERS AND TWO-COLUMN TABLES
   ========================================================================== */

.mslider .val {
  flex: none;
  min-width: 52px;
  text-align: right;
  font-size: var(--f-label);
  color: var(--ink);
}

/* The two-column jingle table (.mgrid / .mcol / .mcell) stood here: schemes
   on the left, jingles on the right, pick one then the other. The Intro menu
   took that job over with a plain list, and the markup and the #sel-1..5
   radio group went with it, so these rules could no longer match anything.
   .mcols and .mcols3, which the menus do use, are further up and untouched. */


/* ==========================================================================
   06  STATUS BAR
   Left group is fixed width so it sits under the category pane; the meter
   then always occupies the same span, under the reading area.
   ========================================================================== */

.statusbar {
  flex: none;
  height: 32px;
  background: var(--face-alt);
  border-top: 2px solid var(--ink);
}

.sbar {
  height: 100%;
  align-items: stretch;
  gap: 2px;
  padding: 3px;
  font-size: var(--f-sm);
  line-height: 16px;
  letter-spacing: 0.5px;
}

.cell {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 11px;
  color: var(--ink-dim);
  background: var(--face);
  box-shadow: var(--bevel-down);
  white-space: nowrap;
}
.cell b { color: var(--ink); font-weight: 400; }

/* THE METER LINES UP WITH THE READING PANE ABOVE IT, and the widths here are
   solved for that rather than chosen. The workspace is a 256px | 1fr | 256px
   grid with 2px gaps, so measured from inside the window frame the reading
   pane runs from 258 to (width - 258). The meter has to hit both.

     left   3 padding + 60 arrow + 2 + 129 counter + 2 + 60 arrow + 2 = 258
     right  3 padding + 253 copyright + 2                            = 258

   The old numbers were 128 and 256 with 8px of padding on the meter's own
   cell, which the comment claimed was aligned and was not: it started 7px
   inside the pane and ended 11px inside it. Unequal insets on the two sides
   are what made it read as floating rather than fixed — the eye reads the
   asymmetry long before it reads the offset.

   The meter cell is a bare slot: no padding, no face, no bevel. It used to
   carry all three, which put a sunken well inside a sunken well and cost the
   16px that broke the alignment. The track is the visible thing, so the track
   is what gets the space. */
.cell-page  { flex: none; width: 129px; justify-content: center; }
.cell-meter { flex: 1; min-width: 0; padding: 0; background: none; box-shadow: none; }
.cell-copy  { flex: none; width: 253px; justify-content: flex-start; }

/* the meter, cut into blocks. The fill is solid and the gaps are painted
   over the top in the surrounding colour, so both the filled and the empty
   part of the run are divided the same way. */
/* The meter is the shared well from controls.css: same border, same segment
   pitch, same fill as a slider track. A slider is a meter you can grab, so
   they are built from the same parts. */
.meter {
  position: relative;
  flex: 1;
  min-width: 0;
  height: var(--u-track);
  padding: 2px;
  background: color-mix(in srgb, var(--ink) 20%, var(--face));
  border: 2px solid var(--line);
  box-shadow: var(--bevel-down);
}
.meter > i {
  display: block;
  height: 100%;
  background: var(--pop);
}
.meter::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    90deg,
    transparent 0 var(--seg-on),
    var(--face) var(--seg-on) calc(var(--seg-on) + var(--seg-off)));
}

/* Direction marks, drawn as stepped blocks rather than typed. A glyph would
   depend on the font pairing and a rotated rule would land between pixels. */
.arrow::before {
  content: "";
  flex: none;
  width: 4px; height: 7px;
  background: currentColor;
}
.arrow.is-prev::before {
  clip-path: polygon(4px 0, 3px 0, 3px 1px, 2px 1px, 2px 2px, 1px 2px, 1px 3px,
                     0 3px, 0 4px, 1px 4px, 1px 5px, 2px 5px, 2px 6px, 3px 6px,
                     3px 7px, 4px 7px);
}
.arrow.is-next::before {
  clip-path: polygon(0 0, 1px 0, 1px 1px, 2px 1px, 2px 2px, 3px 2px, 3px 3px,
                     4px 3px, 4px 4px, 3px 4px, 3px 5px, 2px 5px, 2px 6px,
                     1px 6px, 1px 7px, 0 7px);
}
.arrow.is-next { flex-direction: row-reverse; }

/* previous / next — behaviour: .btn-drop */
.arrow {
  flex: none;
  width: 60px;
  gap: 7px;
  justify-content: center;
  margin: 1px 0;
  font-size: var(--f-sm);
}

/* AN ARROW THAT LEAVES THE ARTICLE says so before you press it.

   The arrows run through the whole category listing now, not just the pages
   of the open piece, so at either end of an article the next press changes
   document. That is a bigger move than turning a page and it should not look
   identical to one.

   It is marked by holding the value back rather than by adding anything:
   dimmed text and arrowhead, and a face mixed a little way toward the ground
   so the block sits lower against the strip. Nothing is added, no second
   glyph and no colour the scheme does not already contain, so it stays legible
   in all thirty-nine of them and in both contrast settings. Hover and press
   bring it back to full strength, because by then you have committed. */
.arrow.is-cross {
  color: var(--ink-dim);
  background: color-mix(in srgb, var(--face) 82%, var(--ink));
}
.arrow.is-cross:hover,
.arrow.is-cross:active { color: var(--ink); background: var(--face); }


/* Ticks for everything added after the first pass. Every option that can be
   checked needs one, or the menu shows no state at all — which is exactly
   what happened to the Buttons, Font and Fit rows before this block. */


/* ticks for the schemes, the nine body faces and the heading row */


/* the whole navigation strip can be put away — see the Theme menu */
body:not(:has(#show-nav:checked)) .statusbar { display: none; }


/* Every option that can be checked needs a tick, or the menu shows no
   state at all — which is what happened to Navigation bar, Hover, Click
   and the whole Hover kit column. */

/* Rows that are marked from script rather than by a checked input: the
   pinned jingle, and the Randomize toggle in the kit list. Both are buttons,
   so there is no :checked to read and the generated list above cannot cover
   them. */
.jingle.is-on::before,
#kit-random.is-on::before { background: currentColor; box-shadow: var(--tick); }
