/* ============================================================================
   POWERMINE TABLE POLISH — the shared look for every card/table cabinet.

   WHY THIS FILE EXISTS
   The 2026-07-02 polish pass (../POLISH_REVIEW.md) gave blackjack, hold'em and
   roulette real depth — felt grain, a lit wood rail, cards with weight. It was
   written inline, three times, so the other six card/table games stayed flat and
   the suite reads as two different products. This file is that same system
   extracted once so every table can wear it and Sebastian's lobby has a single
   surface to align the ecosystem to.

   HOW A GAME OPTS IN  (two edits, no per-game CSS)
     1. <link rel="stylesheet" href="../shared/table-polish.css">
     2. add the utility class alongside the game's own class:
          table surface  ->  class="felt-oval pm-felt"      (or .felt/.board/…)
          card element   ->  class="card pm-card"           (usually one JS line)
          chip/token     ->  class="chip pm-chip"
          lit button     ->  class="rent-btn pm-btn-lit"
   The game keeps its own layout, sizing and palette; this only adds material.

   PALETTE
   Every recipe reads the game's own variables and falls back if unset, so a
   cabinet keeps its identity — blackjack teal, roulette wine, spades green:
     --felt / --feltrail   cloth colors      --gold   accent for focus + trim
   Override --wood-hi/mid/lo for a different rail timber.

   HARD LINE: presentation only. Nothing here reads or writes game state, and no
   rule in ../MODEL_RULES.md is expressible in CSS — this file must never carry
   a number that describes what a player gets back.
   ============================================================================ */

:root{
  /* Fractal-noise tile (feTurbulence, desaturated) used for cloth grain and card
     linen. Base64 so it needs no second request and survives file:// opening. */
  --noise:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxNDAnIGhlaWdodD0nMTQwJz48ZmlsdGVyIGlkPSduJz48ZmVUdXJidWxlbmNlIHR5cGU9J2ZyYWN0YWxOb2lzZScgYmFzZUZyZXF1ZW5jeT0nMC44JyBudW1PY3RhdmVzPScyJyBzdGl0Y2hUaWxlcz0nc3RpdGNoJyByZXN1bHQ9J3QnLz48ZmVDb2xvck1hdHJpeCBpbj0ndCcgdHlwZT0nc2F0dXJhdGUnIHZhbHVlcz0nMCcvPjwvZmlsdGVyPjxyZWN0IHdpZHRoPScxMDAlJyBoZWlnaHQ9JzEwMCUnIGZpbHRlcj0ndXJsKCNuKScvPjwvc3ZnPg==");

  /* Rail timber. DECLARED, not a fallback: cabinets reference var(--wood-hi) directly
     in their own rules (roulette's striped board builds its rail from these). Undeclare
     them and that background shorthand goes invalid and computes to `none` — the whole
     board loses its cloth. Nothing overrides these today; the per-cabinet knobs below
     are the ones that must stay fallbacks. */
  --wood-hi:#7a5228; --wood-mid:#4a2f18; --wood-lo:#1c0f08;
}

/* THE PER-CABINET KNOBS ARE INLINE FALLBACKS, NOT :root DECLARATIONS — deliberately.
   This file loads AFTER each cabinet's inline <style> so the pm-* rules win at equal
   specificity. A custom property declared here would win the same way, and silently
   beat the cabinet's own tuning: hold'em set --felt-mid to its green #061208 and got
   a grey wash instead, because :root here landed later. var(--x, default) inverts
   that — the game's value wins when it sets one, the default applies when it doesn't.
     --felt-mid / --felt-edge   where the cloth falls off at 78% and the rim
     --pm-mark-size / -top / -color / -opacity   table watermark */

/* ---- FOCUS: premium UIs show where the keyboard is. Costs mouse users nothing. */
button:focus-visible,
[role="button"]:focus-visible,
.pm-chip:focus-visible{
  outline:2px solid var(--gold,#ffcf3a);
  outline-offset:2px;
  border-radius:inherit;
}

/* ---- FELT: cloth + grain + lit wood rail + ambient-occlusion seam.
   The rail is drawn with the layered-background technique (padding-box cloth over
   border-box timber) so it follows any border-radius the game already set — an
   ordinary border can't hold a gradient without square corners. */
.pm-felt{
  isolation:isolate;
  border:clamp(10px,1.6vmin,20px) solid transparent;
  /* GROUND/CHROME pass -- 2026-07-22, third attempt. Both earlier attempts were
     wrong in opposite directions and each was verified in the browser:

       7/21, image under OVERLAY on a near-black radial (rgba(0,0,0,.72)-.9 across
       78-100%): overlay is 2*backdrop*source below 0.5, so a near-black backdrop
       crushes the result to black no matter what the photo contains. Flat black void.

       7/22 first fix, image under NORMAL: an opaque no-alpha webp at center/cover
       paints as itself and hides the gradient entirely -- so --felt stopped doing
       anything. Blackjack's teal (#0a2c2e) rendered kelly green, the vignette was
       gone, and the ground stopped being recessive: on screen the cloth was the
       loudest object on the table, louder than the cards and the seat portraits.

     LUMINOSITY is the mode that does what this actually needs. It takes LIGHTNESS
     from the source (the photo -- the weave, the nap, the fall of light) and HUE +
     SATURATION from the backdrop (the cabinet's own --felt). Blackjack gets teal
     cloth, spades gets green cloth, and both get the same real fabric. That is the
     multi-style rule from 6c72e0e expressed in one blend mode: shared craft, each
     world's own color.

     Two constraints this mode imposes, both deliberate below:
       - the gradient must carry REAL, OPAQUE color. Luminosity reads hue+sat from
         it, so a transparent or desaturated backdrop yields grey cloth. Every stop
         is opaque, and the mid/edge fallbacks are now DERIVED from --felt with
         color-mix rather than hardcoded green: keno's cloth is blue-slate
         (--felt:#0c1a2a) and a fixed green falloff would have run its table from
         blue at the centre to green at the rim. A cabinet that sets its own
         --felt-mid / --felt-edge (blackjack, hold'em) still wins -- these are
         fallbacks, never :root declarations, per the note above.
       - the photo must stay mid-toned. Depth comes from the box-shadow insets,
         which sit on top of the whole background stack and are unaffected by blend
         mode -- that is what puts the vignette back and pushes the ground down. */
  background:
    url("assets/table-ground/poker-felt-cloth.webp") center/cover no-repeat padding-box,
    radial-gradient(120% 100% at 50% 40%,var(--felt,#0c2a1e) 0%,var(--felt-mid,color-mix(in srgb,var(--felt,#0c2a1e) 62%,#000)) 78%,var(--felt-edge,color-mix(in srgb,var(--felt,#0c2a1e) 34%,#000)) 100%) padding-box,
    repeating-linear-gradient(124deg,rgba(0,0,0,.22) 0 2px,transparent 2px 8px) border-box,
    url("assets/table-ground/rail-chrome-frame.webp") center/cover no-repeat border-box,
    linear-gradient(150deg,var(--wood-hi,#7a5228) 0%,var(--wood-mid,#4a2f18) 30%,var(--wood-lo,#1c0f08) 52%,var(--wood-mid,#4a2f18) 76%,var(--wood-hi,#7a5228) 100%) border-box;
  /* Rail keeps luminosity too, for the same reason and one more: the source is a
     macro photograph with a lens flare in one corner, and `cover` stretches that
     flare across whichever edge it lands on. Under screen it read as a bright smear
     on the bottom rail of the roulette board only -- lit from nowhere, and asymmetric
     against a frame that is lit from the top left everywhere else. Luminosity keeps
     the grain and the stud highlights while the timber gradient underneath keeps
     ownership of the color and the light direction. */
  background-blend-mode:luminosity,normal,normal,luminosity,normal;
  box-shadow:
    0 0 0 2px var(--wood-lo,#1c0f08),
    inset 0 0 0 2px rgba(255,205,140,.14),
    inset 0 0 70px rgba(0,0,0,.68),
    inset 0 0 26px 8px rgba(0,0,0,.5),
    0 20px 50px rgba(0,0,0,.7);
}
/* Grain and watermark sit BEHIND the game's own children (z-index:-1 inside the
   felt's own stacking context), so no card, chip or seat can be covered by them. */
.pm-felt::before{
  content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:-1;
  background-image:var(--noise);background-size:150px 150px;
  opacity:.05;mix-blend-mode:overlay;
}
.pm-felt::after{
  content:var(--pm-mark,'GAMBINO');position:absolute;top:var(--pm-mark-top,50%);left:50%;
  transform:translate(-50%,-50%);
  font-size:var(--pm-mark-size,clamp(28px,7.5vmin,100px));font-weight:900;letter-spacing:.14em;
  color:var(--pm-mark-color,#dbfbf6);opacity:var(--pm-mark-opacity,.014);
  pointer-events:none;z-index:-1;white-space:nowrap;
}
/* A game whose surface is a plain rectangle (solitaire's board, keno's wrap) still
   gets cloth and grain, but takes NO border: those layouts position their children
   with absolute pixel math against the surface's own box, so consuming even 6px of
   content width pushes the far column out past the frame. The rail is drawn purely
   with inset shadow here — material without moving anything. */
/* This rule REPLACES the background shorthand above, so it needs the cloth wired
   into it separately -- the 7/22 ground pass edited .pm-felt only and left keno and
   solitaire on gradient alone, still carrying the old near-black stops. Verified in
   the browser: keno's board was a flat black void sitting next to a fully clothed
   blackjack table, which is the two-different-products split this whole file exists
   to close. Same photo, same luminosity treatment, same opaque-color requirement as
   above -- only the rail differs, because a flat surface draws its rail with inset
   shadow instead of a border. */
.pm-felt.pm-flat{
  border:0;
  background:
    url("assets/table-ground/poker-felt-cloth.webp") center/cover no-repeat,
    radial-gradient(120% 100% at 50% 40%,var(--felt,#0c2a1e) 0%,var(--felt-mid,color-mix(in srgb,var(--felt,#0c2a1e) 62%,#000)) 78%,var(--felt-edge,color-mix(in srgb,var(--felt,#0c2a1e) 34%,#000)) 100%);
  background-blend-mode:luminosity,normal;
  box-shadow:
    inset 0 0 0 2px rgba(255,205,140,.1),
    inset 0 0 0 6px var(--wood-mid,#4a2f18),
    inset 0 0 0 8px var(--wood-lo,#1c0f08),
    inset 0 0 60px rgba(0,0,0,.6),
    0 12px 34px rgba(0,0,0,.6);
}

/* ---- CARDS: layered physical shadow, linen tooth, raking top gloss, serif pips.
   A real card is not a white rectangle — it has thickness and it catches light. */
.pm-card{
  position:relative;
  isolation:isolate;
  border-radius:10%/7%;
  overflow:hidden;
  box-shadow:
    0 2px 3px rgba(0,0,0,.4),
    0 6px 14px rgba(0,0,0,.5),
    inset 0 0 0 1px rgba(0,0,0,.25);
}
.pm-card::before{
  content:'';position:absolute;inset:0;pointer-events:none;z-index:2;
  background-image:var(--noise);background-size:44px 44px;
  opacity:.06;mix-blend-mode:multiply;
}
.pm-card::after{
  content:'';position:absolute;inset:0;pointer-events:none;z-index:2;
  background:linear-gradient(155deg,rgba(255,255,255,.32) 0%,rgba(255,255,255,0) 34%);
}
/* Rank/suit glyphs: a slab/serif face is what card stock actually uses. Works on
   both SVG-drawn faces and text-drawn faces. */
.pm-card svg text,
.pm-card .cr,.pm-card .cs{font-family:Georgia,'Times New Roman',serif}

/* ---- CHIPS: a disc with edge spots and a stacked shadow, not a filled circle. */
.pm-chip{
  position:relative;
  border-radius:50%;
  background:
    radial-gradient(circle at 35% 28%,rgba(255,255,255,.55),transparent 45%),
    repeating-conic-gradient(from 0deg,rgba(255,255,255,.8) 0deg 16deg,transparent 16deg 36deg),
    radial-gradient(circle at 50% 50%,var(--chip-face,#1e5a56) 55%,var(--chip-edge,#0e332f) 100%);
  border:3px solid #fff;
  box-shadow:0 3px 0 rgba(0,0,0,.5),0 5px 10px rgba(0,0,0,.4);
}

/* ---- BUTTONS: one light direction — highlight on top, shadow beneath, and a
   real travel on press so the control feels mechanical. */
.pm-btn-lit{
  box-shadow:0 4px 0 rgba(0,0,0,.4),inset 0 1px 0 rgba(255,255,255,.22);
}
.pm-btn-lit:active{
  transform:translateY(2px);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.1);
}

/* ---- MOTION: honor the OS setting. The polish is material, not movement. */
@media (prefers-reduced-motion:reduce){
  .pm-btn-lit:active{transform:none}
}


/* ---- DISCLOSURE: "entropy is not credits" ------------------------------------
   Brandon's ruling 2026-07-21. The entropy readout STAYS (it's the player's
   feedback that their play is doing something), and it already sits on the
   opposite side of the money bar from Credits. This makes the separation
   explicit rather than positional, because position alone is not a disclosure.
   Presentation only — it states what the number ISN'T, never what it's worth. */
.pm-not-credits{
  display:block;
  font-size:.82em;
  letter-spacing:.04em;
  font-weight:700;
  opacity:.75;
  text-transform:none;
}
