/* ==========================================================================
   DESKTOP PATTERNS — the tiled texture behind the window
   --------------------------------------------------------------------------
   Fourteen rows: twelve patterns, plus None and a slot for an image of your
   own. Every one of the twelve is drawn with gradients and takes its colour
   from the live scheme — there are no pattern image files left. Only the
   custom slot loads a file, and that one comes from the visitor's machine.

     ground = var(--desk)      set by themes.css
     mark   = var(--desk-dot)  set by themes.css
     tile   = var(--pat-size)  set by the slider in the Desktop menu

   EVERY pattern is expressed as a fraction or multiple of --pat-size rather
   than in fixed pixels, so one slider rescales the whole set and each tile
   keeps its proportions. The slider runs 1..8 and the script maps it to the
   multipliers 2, 4, 6, 8, 10, 16, 20 and 32, which multiply an 8px cell. A
   boot always picks the last of them: at 32x a pattern reads as a texture
   rather than a mesh.

   Each pattern is defined ONCE as a pair of variables:
     --patN   the layer stack        --patNs   the tile size

   A pattern applies on click. Pointing at a row highlights it, nothing more.
   Diagonals are allowed here; they are not allowed in the interface.
   ========================================================================== */

body {
  /* --pat-x is the multiplier from the slider: 2, 4, 6, 8, 10, 16, 20 or 32.
     Each pattern has a natural 8px cell, so the tile is 8 x the multiplier
     and every mark inside it scales with the cell rather than staying 1px
     while the gaps grow. That is the difference between a pattern getting
     BIGGER and one just getting sparser. */
  --pat-x: 4;
  --pat-size: calc(8px * var(--pat-x) / 2);
  --pat-half: calc(var(--pat-size) / 2);
  --pat-qtr:  calc(var(--pat-size) / 4);
  --pat-mark: max(1px, calc(var(--pat-x) * 0.5px));
  --pat-dot:  max(2px, calc(var(--pat-x) * 0.9px));

  /* File tiles are drawn on a 16px grid and must scale by whole multiples of
     16, or the art lands on fractional pixels and resamples unevenly. */
  --pat-tile: calc(16px * var(--pat-x) / 2);

  /* 1 — NONE */
  --pat1:  none;
  --pat1s: auto;

  /* 2 — 50% GRAY. Dither checker. */
  --pat2:  repeating-conic-gradient(var(--desk-dot) 0% 25%, transparent 0% 50%);
  --pat2s: var(--pat-size) var(--pat-size);

  /* 3 — DOTS. One round mark per cell. */
  --pat3:  radial-gradient(circle at var(--pat-qtr) var(--pat-qtr),
             var(--desk-dot) calc(var(--pat-dot) / 2), transparent calc(var(--pat-dot) / 2 + 1px));
  --pat3s: var(--pat-size) var(--pat-size);

  /* 4 — SQUARES. A square reads heavier than a circle of the same width, so
     the mark is cut to two thirds to sit alongside Dots at the same weight.
     The conic sweep starts at the corner of the mark and fills one quadrant,
     which is exactly a square. */
  --pat-sq:  max(2px, calc(var(--pat-dot) * 0.46));
  --pat4:  conic-gradient(from 90deg at var(--pat-sq) var(--pat-sq),
             var(--desk-dot) 0 25%, transparent 0);
  --pat4s: var(--pat-size) var(--pat-size);

  /* 5 — TRIANGLES. A 135deg gradient cut off early leaves a wedge in the
     corner of each cell rather than splitting the whole cell in half. */
  --pat5:  linear-gradient(135deg, var(--desk-dot) 0 18%, transparent 0);
  --pat5s: var(--pat-size) var(--pat-size);

  /* 6 — TILE. Plain grid. */
  --pat6:  linear-gradient(90deg, var(--desk-dot) var(--pat-mark), transparent 0),
           linear-gradient(180deg, var(--desk-dot) var(--pat-mark), transparent 0);
  --pat6s: var(--pat-size) var(--pat-size);

  /* 7 — WAFFLE. Grid, with a lighter grid at a quarter of the pitch. */
  --pat7:  linear-gradient(90deg, var(--desk-dot) var(--pat-mark), transparent 0),
           linear-gradient(180deg, var(--desk-dot) var(--pat-mark), transparent 0),
           linear-gradient(90deg,  color-mix(in srgb, var(--desk-dot) 45%, transparent) var(--pat-mark), transparent 0),
           linear-gradient(180deg, color-mix(in srgb, var(--desk-dot) 45%, transparent) var(--pat-mark), transparent 0);
  --pat7s: var(--pat-size) var(--pat-size),
           var(--pat-size) var(--pat-size),
           var(--pat-qtr) var(--pat-qtr),
           var(--pat-qtr) var(--pat-qtr);

  /* 8 — FLANEL. A plaid: strong lines on the cell, light lines offset to the
     middle of it, so the two weights sit between one another instead of on
     top of one another. Strong meets strong at the corners, light meets
     light at the centres. */
  --pat8:  linear-gradient(90deg,  var(--desk-dot) var(--pat-mark), transparent 0),
           linear-gradient(180deg, var(--desk-dot) var(--pat-mark), transparent 0),
           linear-gradient(90deg,  color-mix(in srgb, var(--desk-dot) 38%, transparent) var(--pat-mark), transparent 0),
           linear-gradient(180deg, color-mix(in srgb, var(--desk-dot) 38%, transparent) var(--pat-mark), transparent 0);
  --pat8s: var(--pat-size) var(--pat-size),
           var(--pat-size) var(--pat-size),
           var(--pat-size) var(--pat-size),
           var(--pat-size) var(--pat-size);
  --pat8p: 0 0, 0 0, var(--pat-half) 0, 0 var(--pat-half);

  /* 9 — HORIZONTAL. One line per half cell. */
  --pat9:  linear-gradient(180deg, var(--desk-dot) var(--pat-mark), transparent 0);
  --pat9s: var(--pat-size) var(--pat-half);

  /* 10 — VERTICAL. The same, stood up. */
  --pat10:  linear-gradient(90deg, var(--desk-dot) var(--pat-mark), transparent 0);
  --pat10s: var(--pat-half) var(--pat-size);

  /* 11 — DIAGONAL. */
  --pat11:  repeating-linear-gradient(45deg, var(--desk-dot) 0 calc(var(--pat-mark) * 2),
             transparent calc(var(--pat-mark) * 2) var(--pat-half));
  --pat11s: auto;

  /* 12 — WEAVE. Crosshatch. */
  --pat12:  repeating-linear-gradient(45deg,  var(--desk-dot) 0 var(--pat-mark),
             transparent var(--pat-mark) var(--pat-half)),
           repeating-linear-gradient(-45deg, var(--desk-dot) 0 var(--pat-mark),
             transparent var(--pat-mark) var(--pat-half));
  --pat12s: auto;

  /* 13 — STARFIELD. Drawn rather than loaded, so it follows the scheme; an
     SVG tile carries fixed colours and cannot. Five marks across a cell of
     three, at sizes an octave apart. Spreading them over a larger cell than
     the other patterns use is what makes it read as sparse rather than as a
     texture, and staggering the sizes stops the repeat being obvious. */
  /* Eleven stars over a cell of five, at offsets that share no common
     spacing, in four sizes. The giveaway with a tiled starfield is rhythm:
     evenly spread marks read as a grid however random each one looks. These
     are clustered in two loose drifts with a gap between them, which is what
     stops the eye finding the repeat. */
  --pat-sky: calc(var(--pat-size) * 5);
  --pat13:  radial-gradient(circle at 7% 11%,  var(--desk-dot) calc(var(--pat-dot) / 2),   transparent 0),
            radial-gradient(circle at 19% 4%,  var(--desk-dot) calc(var(--pat-dot) / 4.5), transparent 0),
            radial-gradient(circle at 13% 27%, var(--desk-dot) calc(var(--pat-dot) / 3.5), transparent 0),
            radial-gradient(circle at 31% 19%, var(--desk-dot) calc(var(--pat-dot) / 5),   transparent 0),
            radial-gradient(circle at 44% 38%, var(--desk-dot) calc(var(--pat-dot) / 2.5), transparent 0),
            radial-gradient(circle at 61% 9%,  var(--desk-dot) calc(var(--pat-dot) / 4),   transparent 0),
            radial-gradient(circle at 73% 52%, var(--desk-dot) calc(var(--pat-dot) / 2),   transparent 0),
            radial-gradient(circle at 88% 33%, var(--desk-dot) calc(var(--pat-dot) / 5),   transparent 0),
            radial-gradient(circle at 57% 71%, var(--desk-dot) calc(var(--pat-dot) / 3.5), transparent 0),
            radial-gradient(circle at 26% 63%, var(--desk-dot) calc(var(--pat-dot) / 4.5), transparent 0),
            radial-gradient(circle at 81% 86%, var(--desk-dot) calc(var(--pat-dot) / 3),   transparent 0);
  --pat13s: var(--pat-sky) var(--pat-sky);

  /* 14 — CUSTOM TILE. See images/patterns/README.txt. */
  --pat14:  url("../images/patterns/custom.svg");
  --pat14s: var(--pat-tile) var(--pat-tile);

  --p-active:  var(--pat2);
  --ps-active: var(--pat2s);
  --pp-active: 0 0;
}

body { background-position: var(--pp-active, 0 0); background-origin: border-box; }


/* --------------------------------------------------------------------------
   MAPPING — the checked radio drives the desktop
   -------------------------------------------------------------------------- */
body:has(#pat-1:checked)  { --p-active: var(--pat1);  --ps-active: var(--pat1s); }
body:has(#pat-2:checked)  { --p-active: var(--pat2);  --ps-active: var(--pat2s); }
body:has(#pat-3:checked)  { --p-active: var(--pat3);  --ps-active: var(--pat3s); }
body:has(#pat-4:checked)  { --p-active: var(--pat4);  --ps-active: var(--pat4s); }
body:has(#pat-5:checked)  { --p-active: var(--pat5);  --ps-active: var(--pat5s); }
body:has(#pat-6:checked)  { --p-active: var(--pat6);  --ps-active: var(--pat6s); }
body:has(#pat-7:checked)  { --p-active: var(--pat7);  --ps-active: var(--pat7s); }
body:has(#pat-8:checked)  { --p-active: var(--pat8);  --ps-active: var(--pat8s);  --pp-active: var(--pat8p); }
body:has(#pat-9:checked)  { --p-active: var(--pat9);  --ps-active: var(--pat9s); }
body:has(#pat-10:checked)  { --p-active: var(--pat10);  --ps-active: var(--pat10s); }
body:has(#pat-11:checked)  { --p-active: var(--pat11);  --ps-active: var(--pat11s); }
body:has(#pat-12:checked)  { --p-active: var(--pat12);  --ps-active: var(--pat12s); }
body:has(#pat-13:checked)  { --p-active: var(--pat13);  --ps-active: var(--pat13s); }
body:has(#pat-14:checked)  { --p-active: var(--pat14);  --ps-active: var(--pat14s); }


/* --------------------------------------------------------------------------
   SWATCHES
   Each row carries a small drawing rather than a live sample of its own
   pattern. A live sample has to pick a size, and there is no size at which
   all fourteen read: at 2x half of them are grey mush and at 32x every one
   of them is a single flat block. A fixed glyph is only an anchor — it says
   which row you are on, and the desktop behind the menu shows what the
   pattern actually does.
   -------------------------------------------------------------------------- */

svg.pv {
  flex: none;
  width: 30px;
  height: 18px;
  padding: 1px;
  color: var(--ink-dim);
  background: color-mix(in srgb, var(--ink) 10%, var(--face));
  border: 1px solid var(--line);
  shape-rendering: crispEdges;
}
svg.pv rect { fill: currentColor; }

/* Weave and Diagonal are drawn as strokes rather than stepped blocks. A
   criss-cross made of single pixels reads as speckle at this size; it has to
   be actual crossing lines to be recognisable, so these two opt out of
   crispEdges and are allowed to antialias. */
svg.pv.is-line { shape-rendering: auto; }
svg.pv .a rect { fill: var(--pop); }
.mopt:hover svg.pv { color: var(--sel-ink); background: color-mix(in srgb, var(--sel) 70%, var(--face)); }

/* the ground row in the Desktop menu */
.swrow .sw-1 { background: var(--dark); }
.swrow .sw-2 { background: var(--deep); }
.swrow .sw-3 { background: var(--mid); }
.swrow .sw-4 { background: var(--light); }
.swrow .sw-5 { background: var(--pop); }

/* the Default row shows the ground it would actually produce, dimness and all */
/* The Default row previews the ground Default would produce for the live
   palette. It used to show var(--desk), which is whatever is on screen — so
   picking a colour made the Default swatch show that colour too, and there
   was no way to see what you would be going back to. */
.mopt[for="bg-0"] .bgv {
  flex: none; width: 30px; height: 18px;
  border: 1px solid var(--ink);
  background: color-mix(in srgb, var(--dark) calc(100% - var(--bg-darkness)), #000000);
}


/* --------------------------------------------------------------------------
   TICKS
   -------------------------------------------------------------------------- */
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 {
  background: currentColor;
  box-shadow: var(--tick);
}
