/* The dot field — the interactive ground the Studio stands on, and now the recommendation
   surfaces too.

   WHY THIS IS A FILE AND NOT A COPY. Four frontends here share no module system, and every time
   something has been duplicated across them it has drifted: the three shape choosers, the
   website/app showcase tables, the five-versus-three male builds that silently deleted a
   customer's answer. The owner asked for this exact background in three more places. Copying it
   three times would guarantee that in a year there are four slightly different rooms.

   Served from /static/, which every surface can reach.

   WHICH SURFACES ACTUALLY USE IT, because this paragraph used to say "the PWA links it" and the
   PWA did not, and said the Studio was served from this directory as though that meant it used
   this file — it does not:

     · static_v2/styling.html   links it.
     · app/screens/design.js    mounts it for the Studio screen only, and removes it on teardown.
                                Scoped, because a fixed ground behind the whole app would replace
                                the paper every other screen is composed on.
     · api/static/studio.html   STILL CARRIES ITS OWN COPY, inline, plus its own driver. That is
                                exactly the duplication this file was extracted to end, and it is
                                the one surface the extraction was done FOR — `Dotfield.pause()`
                                exists because of its Fabric drag. Migrating it means routing its
                                `_fxBusy` flag through pause()/resume() and re-testing the drag,
                                which is a change to a working tool and wants its own pass.

   A comment that names a surface which does not use it is worse than no comment: it is the reason
   somebody edits this file expecting the Studio to change.

   WHAT IT IS. A fine dim dot grid over a soft ground, with a spotlight that follows the cursor and
   lights the dots it passes: a dim base layer, plus a lit layer masked to a circle at --mx/--my.
   ONE GROUND, no longer theme-aware: the dark cut went with the night theme, and there is no
   data-theme attribute left for a second block to match. The spotlight is made of WHITE LIGHT
   rather than of a hue, so nothing here competes with the clothes — which is now true of the whole
   interface and not just of this file.

   The element must be `<div class="dotfield" aria-hidden="true"></div>` as the first child of
   <body>, and dotfield.js drives it. It is `position:fixed` and `pointer-events:none`, so it sits
   behind everything and catches nothing. Give your own content `position:relative` (or any
   z-index above 0) or it will be painted underneath. */

@property --mx{ syntax:'<length-percentage>'; inherits:true; initial-value:50%; }
@property --my{ syntax:'<length-percentage>'; inherits:true; initial-value:42%; }
/* THE GROUND AND THE DIM GRID ARE UNCONDITIONAL. They are static, they are what this field is
   for, and they cost one paint. */
.dotfield{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden;
  --mx:50%;--my:50%;--dot-gap:11px;
  background:var(--dot-bg);
  box-shadow:inset 0 0 200px 64px var(--dot-vig);}
.dotfield::before{content:"";position:absolute;inset:-2px;
  background-image:radial-gradient(var(--dot-dim) 0.6px, transparent 1.05px);
  background-size:var(--dot-gap) var(--dot-gap);}

/* THE SPOTLIGHT IS THE PART THAT NEEDS A CURSOR, AND IT ONLY EXISTS WHERE THERE IS ONE.
   Everything in this block is driven by --mx/--my, which dotfield.js writes from pointermove and
   deliberately does NOT track for a coarse pointer. On a touch device that left the pair frozen
   at their initial 50%/50% forever — measured on a phone, at every width — so the "interactive
   spotlight" was a static blob welded to the middle of the screen, and the browser was still
   carrying: a 360px radial gradient layer, a second full-viewport dot grid in ::after, a 150px
   radial mask over it, and two registered @property transitions poised to interpolate values
   that never change.
   (pointer: fine) IS ABOUT INPUT CAPABILITY, not about theme, so it does not collide with the
   house rule against styling on prefers-color-scheme. A touch device now paints the ground and
   one dot grid and nothing else; a desktop is completely unchanged. */
@media (pointer: fine) {
  .dotfield{
    transition:--mx .3s var(--ease), --my .3s var(--ease);
    background:
      radial-gradient(circle 360px at var(--mx) var(--my), var(--dot-halo), transparent 60%),
      var(--dot-bg);}
  .dotfield::after{content:"";position:absolute;inset:-2px;
    background-size:var(--dot-gap) var(--dot-gap);
    background-image:radial-gradient(var(--dot-lit) 0.6px, transparent 1.05px);
    /* a clean, well-defined spotlight: dots fully lit in the core, smooth single falloff */
    -webkit-mask-image:radial-gradient(circle 150px at var(--mx) var(--my), #000 0%, #000 22%, transparent 62%);
            mask-image:radial-gradient(circle 150px at var(--mx) var(--my), #000 0%, #000 22%, transparent 62%);}
}
/* THIS FIELD IS THE STAGE GROUND — fixed behind the whole app, and the stage in front of it is
   transparent, so whatever colour lives here is the colour of the ROOM THE GARMENT STANDS IN.

   THE ROOM IS ACHROMATIC, LIKE THE WHOLE PRODUCT NOW — but it is still NOT the page's #FFFFFF,
   and that is the one decision in this file that is not a palette decision. Two separate reasons,
   and they have been confused before:

     · WHY IT CARRIES NO HUE. It never should have. The house went warm for a week — #FAF7F2 paper,
       a clay accent — and the Studio's CHROME went with it; this ground was deliberately cut LESS
       warm (#F7F5F2 → #EFEAE2) because a sand room shifts the perceived colour of the garment in
       front of it and garment colour is product truth. The owner then rejected the colour outright,
       so that argument is simply the house rule now and the ground joins it: white paper, black
       ink, no hue anywhere except --warn and --danger.

     · WHY IT IS NOT WHITE. This is a WORKING GROUND, not a page ground, and the two are different
       jobs. The garment, the artwork the customer places on it and the 3D mesh are all judged
       against this field, and a large part of the catalogue is white, ivory or powder — a white
       tee on a white stage has no silhouette, and the customer cannot see the shape of the thing
       they are buying. #FCFCFC → #F0F0F0 is 1.14:1 below the chrome: enough to read as a room the
       garment stands IN rather than a hole cut in the page, and far too little to bias a dye.
       A LATER PASS THAT "FINISHES THE WHITE CONVERSION" BY PUSHING THIS TO #FFFFFF WILL DELETE THE
       SILHOUETTE OF EVERY PALE GARMENT IN THE CATALOGUE. Do not.

   The dots are cut from --ink #111111 so the grid belongs to the same ink as the type, and the
   spotlight is still WHITE LIGHT, not a hue.

   THE TWO html[data-theme=…] BLOCKS THAT USED TO STAND HERE COLLAPSED INTO THIS ONE. There is no
   theme attribute left anywhere in the product for a second block to match, so a dark cut here
   would be a rule that can never fire — and the near-black ground it painted is exactly the black
   plate the owner removed.

   KEEP THIS BLOCK BYTE-IDENTICAL TO THE INLINE COPY IN api/static/studio.html, which still carries
   its own (see the header note above). Changing only one of the pair is the exact drift this file
   was extracted to end. */
.dotfield{
  --dot-bg:radial-gradient(120% 92% at 50% 32%,#FCFCFC,#F0F0F0 82%);
  --dot-dim:rgba(17,17,17,.16); --dot-lit:rgba(17,17,17,.5);
  --dot-halo:rgba(255,255,255,.55); --dot-pool:rgba(255,255,255,.5); --dot-vig:rgba(17,17,17,.06);}
