/* ==========================================================================
   theme.css -- casino skin for the TSYS console
   --------------------------------------------------------------------------
   Loaded AFTER style.css, which owns the component layout. This file owns the
   *appearance*: theme tokens, table felt textures, card-suit ornaments, slot
   reel animation, and the chip styling on the stat cards.

   Nothing here restyles a component's box model, so removing this file leaves
   a perfectly working plain UI. That is deliberate -- the theme is a skin, not
   a fork.

   All artwork is hand-authored inline SVG. There are no image requests, no
   icon fonts beyond the Material Symbols the app already loaded, and no
   third-party CSS. The suit and reel glyphs are defined once as mask images
   and recoloured by the theme, which is why a theme change costs zero bytes.

   Theme is applied as [data-theme] on <html>. A theme only ever overrides
   custom properties, so adding a fifth theme means copying one block.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Suit artwork
   --------------------------------------------------------------------------
   Exposed as custom properties so a single definition feeds the navbar
   ornament, the slot reels, the table corner marks and the login card. Each is
   a mask, not an image: the visible colour comes from background-color, so the
   same glyph renders red in one place and gold in another with no extra asset.
   viewBox is 0 0 24 24 throughout.
   -------------------------------------------------------------------------- */

/*
  Every glyph is written with absolute commands only, one coordinate group per
  command, no implicit line repetition. That is more verbose than a terse
  hand-tuned path, but it means the shape can be reviewed by eye and verified
  by a script -- a relative "c3.1 3.4 6.6 5.9 6.6 9.6" is unreviewable and is
  how a glyph silently ends up two millimetres off-centre. Run
  `node scripts/check-suits.js` after touching any path here.
*/
:root {
  --suit-spade:   url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2.4C15.5 6.2 18.6 8.6 18.6 12A4.6 4.6 0 0 1 12.4 16.3C12.6 18 12.9 19.5 14.4 20.8L9.6 20.8C11.1 19.5 11.4 18 11.6 16.3A4.6 4.6 0 0 1 5.4 12C5.4 8.6 8.5 6.2 12 2.4Z'/%3E%3C/svg%3E");
  --suit-heart:   url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 20.4C8 17.5 3.25 13.2 3.25 9A4.6 4.6 0 0 1 12 6.7A4.6 4.6 0 0 1 20.75 9C20.75 13.2 16 17.5 12 20.4Z'/%3E%3C/svg%3E");
  --suit-diamond: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2.2L21.4 12L12 21.8L2.6 12Z'/%3E%3C/svg%3E");
  --suit-club:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 4.2A4.2 4.2 0 1 1 12 12.6A4.2 4.2 0 1 1 12 4.2ZM8.88 9.6A4.2 4.2 0 1 1 8.88 18A4.2 4.2 0 1 1 8.88 9.6ZM15.12 9.6A4.2 4.2 0 1 1 15.12 18A4.2 4.2 0 1 1 15.12 9.6ZM11.3 14.5C11.4 16.8 12.3 18.6 14.1 20.2L9.9 20.2C11.7 18.6 12.6 16.8 12.7 14.5Z'/%3E%3C/svg%3E");

  /* Shared by every theme; each block below may override. */
  --suit-red: #e11d48;
  --suit-black: #0b1120;
  --gold: #f0c674;
  --gold-2: #b8873b;
  --gold-ink: #3a2a08;
  --felt-1: #0b1120;
  --felt-2: #101a30;
  --accent: #3b82f6;
  --accent-2: #7c3aed;
  --grain-opacity: 0.5;
  --decal-opacity: 0.16;
  --reel-speed: 7s;
}

/* --------------------------------------------------------------------------
   2. Themes
   --------------------------------------------------------------------------
   Four looks over one component set. "vegas" is the default and is also the
   fallback, so a missing or misspelled data-theme still renders sanely instead
   of an unstyled page.
   -------------------------------------------------------------------------- */

/* -- Vegas Night: deep indigo felt, neon rim, gold leaf. The default. ------ */
html[data-theme="vegas"] {
  --color-bg: #080d1a;
  --color-bg-2: #0d1425;
  --color-card: #141d33;
  --color-card-2: #1b2743;
  --color-border: #2b3a5e;
  --color-text: #e8eefb;
  --color-text-2: #93a4c8;
  --color-primary: #3b82f6;
  --color-primary-hover: #60a5fa;
  --accent: #3b82f6;
  --accent-2: #a855f7;
  --felt-1: #080d1a;
  --felt-2: #0d1425;
  --gold: #f5cf7d;
  --gold-2: #c1913f;
  --suit-red: #f43f5e;
  --suit-black: #dbe6ff;
  --grain-opacity: 0.55;
  --decal-opacity: 0.18;
  /* Semantic pair. Left unset, --color-danger stayed the Tailwind default
     #ef4444 in every theme, so a delete confirmation on the champagne and
     emerald themes was a different colour from everything around it. Every
     theme now completes the set. */
  --color-warning: #d97706;
  --color-warning-hover: #f59e0b;
  --color-danger: #ef4444;
  --color-danger-hover: #f87171;
  /* Shape: the reference look. Soft radii, a visible border, real elevation.
     The other three move away from this in different directions. */
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;
  --border-w: 1px;
  --border-w-strong: 1px;
  --shadow-md: 0 4px 20px rgba(0,0,0,.35);
  --shadow-lg: 0 8px 40px rgba(0,0,0,.45);
  --track-headings: -0.011em;
}

/* -- Royal Poker: green baize, cream and brass, quiet and classic. -------- */
html[data-theme="poker"] {
  --color-bg: #04140c;
  --color-bg-2: #07200f;
  --color-card: #0c2c17;
  --color-card-2: #123c1f;
  --color-border: #1f5c31;
  --color-text: #eef7ee;
  --color-text-2: #93b89b;
  --color-primary: #22a355;
  --color-primary-hover: #34d06a;
  --color-success: #22a355;
  --color-success-hover: #34d06a;
  --accent: #22a355;
  --accent-2: #0f7a4d;
  --felt-1: #04140c;
  --felt-2: #082313;
  --gold: #e8c583;
  --gold-2: #a8813c;
  --suit-red: #ef4444;
  --suit-black: #0a1a0f;
  --grain-opacity: 0.7;
  --decal-opacity: 0.16;
  --color-warning: #d97706;
  --color-warning-hover: #f59e0b;
  --color-danger: #dc2626;
  --color-danger-hover: #ef4444;
  /* Shape: flat and close to square. Baize and brass do not float; nothing on
     this theme casts a shadow, which is the main thing that separates it from
     vegas at a glance. */
  --radius-sm: 3px;
  --radius-md: 4px;
  --radius-lg: 6px;
  --border-w: 1px;
  --border-w-strong: 2px;
  --shadow-md: none;
  --shadow-lg: 0 1px 0 rgba(0,0,0,.4);
  --track-headings: 0.01em;
}

/* -- Slot Machine: arcade plum and magenta, louder, for a busy floor. ------ */
html[data-theme="slots"] {
  --color-bg: #150726;
  --color-bg-2: #1e0b34;
  --color-card: #26103f;
  --color-card-2: #331654;
  --color-border: #4c2273;
  --color-text: #f4e9ff;
  --color-text-2: #b495d6;
  --color-primary: #d946ef;
  --color-primary-hover: #e879f9;
  --color-success: #14b8a6;
  --color-success-hover: #2dd4bf;
  --accent: #d946ef;
  --accent-2: #f59e0b;
  --felt-1: #150726;
  --felt-2: #1f0a3a;
  --gold: #ffd166;
  --gold-2: #d99a1f;
  --suit-red: #fb7185;
  --suit-black: #ffe9a8;
  --grain-opacity: 0.45;
  --decal-opacity: 0.15;
  --reel-speed: 4.5s;
  --color-warning: #f59e0b;
  --color-warning-hover: #fbbf24;
  --color-danger: #f43f5e;
  --color-danger-hover: #fb7185;
  /* Shape: heavy and pill-soft. The largest radii in the set and a hard offset
     shadow instead of a blur -- a lit arcade cabinet, not a card floating in
     the dark. */
  --radius-sm: 10px;
  --radius-md: 16px;
  --radius-lg: 24px;
  --border-w: 2px;
  --border-w-strong: 2px;
  --shadow-md: 0 4px 0 rgba(0,0,0,.45);
  --shadow-lg: 0 8px 0 rgba(0,0,0,.5);
  --track-headings: 0;
}

/* -- High Roller: black and champagne, minimal and expensive. ------------- */
html[data-theme="royale"] {
  --color-bg: #070707;
  --color-bg-2: #101010;
  --color-card: #161616;
  --color-card-2: #1f1f1f;
  --color-border: #35322c;
  --color-text: #f2ede3;
  --color-text-2: #9d968a;
  --color-primary: #c8a86a;
  --color-primary-hover: #e0c48c;
  --color-success: #8fae7e;
  --color-success-hover: #a9c795;
  /* Deliberately NOT the champagne gold used for the brand and the primary
     accent. Gold was tried here first and it made the stats donut read as one
     solid gold ring, because that donut paints the unchecked share in
     --color-warning and a warning that matches the brand colour carries no
     information. A burnt amber stays distinct from the gold at every size. */
  --color-warning: #c2703d;
  --color-warning-hover: #d98a54;
  --accent: #c8a86a;
  --accent-2: #8a7444;
  --felt-1: #070707;
  --felt-2: #131313;
  --gold: #e8d5a9;
  --gold-2: #a8915c;
  --suit-red: #c0563f;
  --suit-black: #efe7d6;
  --grain-opacity: 0.6;
  --decal-opacity: 0.13;
  --shadow-md: 0 4px 20px rgba(0,0,0,.6);
  --shadow-lg: 0 8px 44px rgba(0,0,0,.75);
  --color-danger: #a8443a;
  --color-danger-hover: #c2564a;
  /* Shape: sharp and hairlined. Square corners, hairline rules, wide heading
     tracking, and no elevation at all. Expensive things do not have shadows;
     they have very fine borders. This is the theme that looks least like the
     other three. */
  --radius-sm: 0px;
  --radius-md: 2px;
  --radius-lg: 2px;
  --border-w: 1px;
  --border-w-strong: 1px;
  --shadow-md: none;
  --shadow-lg: none;
  --track-headings: 0.14em;
}

/* --------------------------------------------------------------------------
   3. The felt
   --------------------------------------------------------------------------
   Two layers: a broad radial vignette for depth, and a fine diagonal weave.
   The weave is two repeating-linear-gradients a pixel apart -- no image, so it
   costs nothing and stays sharp on any DPR.
   -------------------------------------------------------------------------- */

html, body {
  background-color: var(--felt-1);
  background-image:
    radial-gradient(1200px 780px at 8% -12%, color-mix(in srgb, var(--accent) 26%, transparent), transparent 62%),
    radial-gradient(1000px 700px at 108% 112%, color-mix(in srgb, var(--accent-2) 24%, transparent), transparent 60%),
    linear-gradient(135deg, var(--felt-1) 0%, var(--felt-2) 100%);
  background-attachment: fixed;
}

/* The weave sits on a pseudo-element so it can sit above the gradient but
   below every component, and so it can be dropped entirely at low opacity. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: var(--grain-opacity);
  background-image:
    repeating-linear-gradient(45deg,  rgba(255,255,255,.014) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(-45deg, rgba(0,0,0,.03) 0 1px, transparent 1px 3px);
}

/* Everything real needs to clear the fixed background layers.

   .mobile-wrap is deliberately NOT in that list. It is already position:fixed,
   so adding it there would reset it to relative and collapse the full-bleed
   scanner view on the phone. It only ever needed the stacking order: with
   z-index:auto a positioned box is painted in tree order among the z-index:0
   boxes in the root stacking context, and it is a later sibling of body::after,
   so the decal layer below would have been painted straight over the phone UI.
   Before the decals existed this did not matter, because body::before is an
   earlier sibling and the weave was harmlessly hidden on mobile. */
.navbar, main.container, .auth-wrap, .toast-container { position: relative; z-index: 1; }
.mobile-wrap { z-index: 1; }

/* --------------------------------------------------------------------------
   3b. Felt decals
   --------------------------------------------------------------------------
   Card outlines and suit watermarks printed into the felt, so a large flat
   expanse of near-black has something in it. Two things are going on and it is
   worth being clear about which is which, because they are drawn differently on
   purpose:

     --decal-cards  line art. Two card outlines with the double border and the
                    corner index a real card has, stroked rather than filled, on
                    a smaller tile so the pattern reads as fine print.
     --decal-suits  silhouettes. Big filled suits, on a much larger tile, at a
                    lower alpha. Filling them keeps a shape this large from
                    turning into a thicket of lines.

   Both are masks, so the visible colour is the element's own background and a
   theme change still costs zero bytes -- there is no second palette to keep in
   sync with --gold and --accent.

   They are deliberately NOT named --suit-*. That prefix means "a deck face on a
   24x24 box" and check-suits.js enforces exactly that; these are multi-element
   tiles on their own viewBoxes. The checker validates them too, just under the
   rules that suit tiles actually need.

   Both tiles keep every mark clear of the tile edge. A mark that touched an edge
   would be cut in half, and the two halves would no longer meet across the
   repeat seam, which reads as a rendering bug rather than as a pattern.
   -------------------------------------------------------------------------- */

:root {
  /* Three cards at three different sizes, none of them on a shared row or
     column, so the repeat lands as a scatter. Two cards on a diagonal was
     tried first and read as a grid: with only two marks per tile the eye finds
     the row and the column immediately and the pattern looks like wallpaper.
     The three sizes also keep it from reading as one repeated object.

     The corner index is held below the outline rather than above it. At the
     same alpha as the stroke it measured brighter than either the card edge or
     a suit silhouette, which is backwards: the pips are 16px marks and the suits
     are 130px, so the small one has to sit quieter or it becomes the thing the
     eye lands on. */
  --decal-cards: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 320'%3E%3Cpath d='M 29 26L 83 26A 7 7 0 0 1 90 33L 90 115A 7 7 0 0 1 83 122L 29 122A 7 7 0 0 1 22 115L 22 33A 7 7 0 0 1 29 26Z' fill='none' stroke='%23000' stroke-width='2.5'/%3E%3Cpath d='M 32 32L 80 32A 4 4 0 0 1 84 36L 84 112A 4 4 0 0 1 80 116L 32 116A 4 4 0 0 1 28 112L 28 36A 4 4 0 0 1 32 32Z' fill='none' stroke='%23000' stroke-width='2.5'/%3E%3Cpath d='M 40 41L 48 49L 40 57L 32 49Z' fill='%23000' fill-opacity='.45'/%3E%3Cpath d='M 204 140L 248 140A 6 6 0 0 1 254 146L 254 213A 6 6 0 0 1 248 219L 204 219A 6 6 0 0 1 198 213L 198 146A 6 6 0 0 1 204 140Z' fill='none' stroke='%23000' stroke-width='2'/%3E%3Cpath d='M 206 145L 246 145A 3 3 0 0 1 249 148L 249 211A 3 3 0 0 1 246 214L 206 214A 3 3 0 0 1 203 211L 203 148A 3 3 0 0 1 206 145Z' fill='none' stroke='%23000' stroke-width='2'/%3E%3Cpath d='M 213 164.7C 210 162.8 207 159.8 207 157A 3.2 3.2 0 0 1 213 157A 3.2 3.2 0 0 1 219 157C 219 159.8 216 162.8 213 164.7Z' fill='%23000' fill-opacity='.45'/%3E%3Cpath d='M 80 222L 116 222A 6 6 0 0 1 122 228L 122 283A 6 6 0 0 1 116 289L 80 289A 6 6 0 0 1 74 283L 74 228A 6 6 0 0 1 80 222Z' fill='none' stroke='%23000' stroke-width='1.8'/%3E%3Cpath d='M 82 227L 114 227A 3 3 0 0 1 117 230L 117 281A 3 3 0 0 1 114 284L 82 284A 3 3 0 0 1 79 281L 79 230A 3 3 0 0 1 82 227Z' fill='none' stroke='%23000' stroke-width='1.8'/%3E%3Cpath d='M 90 235L 96 241L 90 247L 84 241Z' fill='%23000' fill-opacity='.45'/%3E%3C/svg%3E");

  /* Heart, club and diamond, placed so no two come near each other and none of
     them crosses the tile edge. The club's three lobes and its stem are one
     path and they union under the default nonzero fill rule, which is what makes
     it read as a single club rather than as four overlapping shapes.

     The card outlines above are stroked at full strength while these are held
     back, which is backwards from how it looks on the page but is what keeps
     the suits reading as the larger, softer shape. Measured on a rendered
     1280px screenshot, as the luminance this adds to the felt underneath: card
     stroke +9 to +13, suit +6 to +7, across all four themes. The card interior
     and the bare felt around it both measure +0, which is the check that
     matters most here -- a stroked card that had quietly filled solid would
     light up its whole interior.

     The 0.42 was tuned against the 700px tile, where the shapes were big enough
     to carry a heavier fill. Dropping that tile to 520px shrank them, and a fill
     that had been right at the old size reads as a distinct shape at the new
     one, so the alpha came down with it to 0.26. The two numbers are a pair:
     raise the tile without lowering this and the suits turn back into smudges. */
  --decal-suits: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 520 520'%3E%3Cg fill='%23000' fill-opacity='.26'%3E%3Cpath d='M 150 230C 131 217 95 190 95 140A 27.5 27.5 0 0 1 150 140A 27.5 27.5 0 0 1 205 140C 205 190 169 217 150 230Z'/%3E%3Cpath d='M 352.5 129A 21 21 0 1 1 310.5 129A 21 21 0 1 1 352.5 129ZM 336.9 156A 21 21 0 1 1 294.9 156A 21 21 0 1 1 336.9 156ZM 368.1 156A 21 21 0 1 1 326.1 156A 21 21 0 1 1 368.1 156ZM 328 159.5C 328.5 171 333 180 342 188L 321 188C 330 180 334.5 171 335 159.5Z'/%3E%3Cpath d='M 380 270L 424 330L 380 390L 336 330Z'/%3E%3C/g%3E%3C/svg%3E");
}

/* The whole decal layer is gated on mask support. The artwork is drawn with
   mask-image, and the colour comes from this element's own background, so
   without a mask the element is not a pattern at all -- it is a translucent
   gradient across the whole viewport. Failing to plain felt beats that. */
@supports (mask-image: linear-gradient(#000, #000)) or (-webkit-mask-image: linear-gradient(#000, #000)) {
  body::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;

    /* A flat colour would sit more deadly on the felt than the gradient above it,
       so the ink is a slow diagonal run from gold into the theme's accent. Both
       mid stops deliberately keep a large share of the gold: a plain mix with an
       accent this dark would let whole tiles of the pattern go invisible where
       the gradient happened to be dark, and the repeat would read as a hole in
       the felt rather than as texture. */
    background-image: linear-gradient(155deg,
      var(--gold) 0%,
      color-mix(in srgb, var(--gold) 62%, var(--accent-2)) 48%,
      color-mix(in srgb, var(--gold) 45%, var(--accent)) 100%);
    background-repeat: no-repeat;
    opacity: var(--decal-opacity);

    -webkit-mask-image: var(--decal-suits), var(--decal-cards);
            mask-image: var(--decal-suits), var(--decal-cards);
    -webkit-mask-repeat: repeat;
            mask-repeat: repeat;

    /* The two tiles are offset from each other on purpose. A shared size would
       make the combined pattern repeat on one visible lattice.

       520px for the suits, not larger. Sizing was settled by screenshotting the
       real login page rather than by reasoning about it, because the failure mode
       is not "too faint" or "too busy" -- a filled suit that is too large stops
       reading as a suit at all. At 700px the heart renders about 150px across and
       a low-alpha fill that size is just a soft grey mass, so the eye reads a
       smudge on the felt. Shrinking the tile is the fix that keeps the shape
       legible: the same artwork at 520px is about 110px, which is big enough to
       give the background scale and small enough to still be recognisably a heart,
       a club and a diamond.

       The 0.26 on --decal-suits is the other half of that, and it is deliberately
       lower than the cards' strokes. A filled shape needs less alpha than a
       hairline to read at the same weight, so holding it back is what keeps the
       delicate card outlines the thing the eye lands on rather than the suits. */
    -webkit-mask-size: 520px 520px, 350px 350px;
            mask-size: 520px 520px, 350px 350px;
    -webkit-mask-position: 140px 60px, 0 0;
            mask-position: 140px 60px, 0 0;
  }

  /* A 375px phone cannot show a 520px tile, so the suits would be sparse there and
     the phone would keep more of the plain black the desktop no longer has.
     Shrinking both tiles keeps the pattern present at every size, and scaling a
     watermark costs nothing visually. The two numbers hold the same ratio to the
     desktop pair above, so the pattern keeps its balance on a phone instead of
     coming out denser than it does on a monitor. */
  @media (max-width: 640px) {
    body::after {
      -webkit-mask-size: 310px 310px, 210px 210px;
              mask-size: 310px 310px, 210px 210px;
    }
  }

  /* The phone scanner is deliberately exempt. It is a camera view: the wrap is
     an opaque #000 that already hides the decals, and keeping them off is a
     decision rather than an accident. Scoped to this page with :has() instead
     of a max-width query, so a narrow desktop window on the login page still
     gets the pattern. If :has() is unsupported the rule is ignored and the
     opaque wrap does the job, exactly as before. */
  html:has(.mobile-wrap) body::after { display: none; }
}

/* --------------------------------------------------------------------------
   4. Gold treatment
   -------------------------------------------------------------------------- */

.gold-text {
  background: linear-gradient(100deg, var(--gold-2) 0%, var(--gold) 38%, #fff8e6 50%, var(--gold) 62%, var(--gold-2) 100%);
  background-size: 260% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  animation: goldSweep 7s linear infinite;
}
@keyframes goldSweep {
  from { background-position: 200% 0; }
  to   { background-position: -60% 0; }
}

/* A hairline of gold along the top of a surface, the way a felt table is
   trimmed. Uses the accent so it follows the theme. */
.card, .auth-card, .stat-card, .device-card, .modal {
  position: relative;
  overflow: hidden;
}
.card::after, .auth-card::after, .stat-card::after, .device-card::after {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--gold) 60%, transparent), transparent);
  opacity: .5;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   5. Card-suit ornament
   --------------------------------------------------------------------------
   A rank-and-suit strip that reads as the corner of a playing card. Used on
   the brand, and as a repeating watermark in the empty-state panels.
   -------------------------------------------------------------------------- */

.suits {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  line-height: 0;
}
.suits > i {
  width: 13px;
  height: 13px;
  display: block;
  background-color: var(--suit-red);
  -webkit-mask: var(--suit-heart) center / contain no-repeat;
  mask: var(--suit-heart) center / contain no-repeat;
}
.suits > i.spade {
  background-color: var(--suit-black);
  -webkit-mask: var(--suit-spade) center / contain no-repeat;
  mask: var(--suit-spade) center / contain no-repeat;
}
.suits > i.diamond {
  background-color: var(--suit-red);
  -webkit-mask: var(--suit-diamond) center / contain no-repeat;
  mask: var(--suit-diamond) center / contain no-repeat;
}
.suits > i.club {
  background-color: var(--suit-black);
  -webkit-mask: var(--suit-club) center / contain no-repeat;
  mask: var(--suit-club) center / contain no-repeat;
}
.suits.lg > i { width: 20px; height: 20px; }
.suits.sm > i { width: 10px; height: 10px; gap: 4px; }

/* The brand lockup: suits above the wordmark, gold wordmark beneath. */
.navbar .brand .lockup { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.navbar .brand .lockup .suits { opacity: .95; }
.navbar .brand .lockup .word {
  font-weight: 800;
  letter-spacing: .3px;
  font-size: 14.5px;
  white-space: nowrap;
}

/* On the login and connect cards the lockup sits beside a logo tile, so it is
   centred rather than left-aligned like the navbar one. */
.auth-brand .lockup { display: flex; flex-direction: column; align-items: center; gap: 3px; }
.auth-brand .lockup h1 { font-size: 19px; }

/* Gold leaf on the logo tile, so the brand reads as one object. */
.auth-brand .logo,
.navbar .brand .logo {
  background: linear-gradient(140deg, var(--gold), var(--gold-2));
  color: var(--gold-ink);
  box-shadow:
    0 4px 16px color-mix(in srgb, var(--gold) 26%, transparent),
    inset 0 1px 0 rgba(255, 255, 255, .4);
}

/* Corner marks on the big panels, the way a table is marked at each corner. */
.panel-felt { position: relative; }
.panel-felt > .corner {
  position: absolute;
  width: 42px;
  height: 42px;
  opacity: .07;
  pointer-events: none;
  background-color: var(--gold);
  -webkit-mask: var(--suit-club) center / contain no-repeat;
  mask: var(--suit-club) center / contain no-repeat;
}
.panel-felt > .corner.tl { top: 8px;    left: 8px; }
.panel-felt > .corner.tr { top: 8px;    right: 8px; }
.panel-felt > .corner.bl { bottom: 8px; left: 8px; }
.panel-felt > .corner.br { bottom: 8px; right: 8px; }

/* --------------------------------------------------------------------------
   6. Slot reels
   --------------------------------------------------------------------------
   A three-reel strip. Each .reel holds five stacked glyphs and cycles
   vertically; all three share a duration but are offset by a negative delay,
   which is what makes them read as three independent reels stopping at
   different times rather than one block moving in lockstep.
   -------------------------------------------------------------------------- */

.reels {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  border-radius: 10px;
  background: linear-gradient(180deg, var(--color-card-2), var(--color-bg-2));
  border: 1px solid color-mix(in srgb, var(--gold) 55%, var(--color-border));
  box-shadow: inset 0 2px 8px rgba(0,0,0,.6), 0 0 12px color-mix(in srgb, var(--gold) 14%, transparent);
  overflow: hidden;
  flex: none;
}
.reel {
  width: 19px;
  height: 26px;
  overflow: hidden;
  position: relative;
  border-radius: 5px;
  background: linear-gradient(180deg, rgba(0,0,0,.5), rgba(255,255,255,.07) 50%, rgba(0,0,0,.5));
}
.reel > .strip {
  display: block;
  animation: reelSpin var(--reel-speed) linear infinite;
}
.reel:nth-child(1) > .strip { animation-delay: -0.9s; }
.reel:nth-child(2) > .strip { animation-delay: -3.4s; }
.reel:nth-child(3) > .strip { animation-delay: -5.6s; }
.reel > .strip > i {
  display: block;
  width: 19px;
  height: 26px;
  background-color: var(--gold);
  -webkit-mask: var(--suit-heart) center / 16px 16px no-repeat;
  mask: var(--suit-heart) center / 16px 16px no-repeat;
}
.reel:nth-child(2) > .strip > i { -webkit-mask-image: var(--suit-diamond); mask-image: var(--suit-diamond); }
.reel:nth-child(3) > .strip > i { -webkit-mask-image: var(--suit-club); mask-image: var(--suit-club); }
/* Alternate the red suits so the strip is not one flat colour. */
.reel > .strip > i:nth-child(odd)  { background-color: var(--gold); }
.reel > .strip > i:nth-child(3n+2) { background-color: var(--suit-red); }
@keyframes reelSpin {
  from { transform: translateY(0); }
  to   { transform: translateY(-130px); } /* 5 x 26px */
}
/* A blur while spinning is what sells the motion. */
.reel.spinning > .strip { animation-duration: .45s; }

/* --------------------------------------------------------------------------
   7. Chip styling for the stat cards
   --------------------------------------------------------------------------
   A poker chip reads as a chip because of the notches cut into its rim, not
   because of its face. Those notches belong in a thin band hugging the top
   and bottom edges.

   A repeating-conic-gradient was the obvious way to fake the alternating rim
   segments and it is the wrong tool: a conic gradient radiates from the
   element's centre, so it fills the whole card as a starburst rather than
   edging it. A repeating-linear-gradient confined to a 3px strip gives the
   notches without the mess.
   -------------------------------------------------------------------------- */

.stat-card {
  background: var(--color-card);
  border-color: color-mix(in srgb, var(--gold) 30%, var(--color-border));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gold) 16%, transparent);
}

/* The notch band. Two background layers, one per edge, each sized to 3px. */
.stat-card::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: .5;
  background:
    repeating-linear-gradient(90deg,
      color-mix(in srgb, var(--gold) 70%, transparent) 0 5px,
      transparent 5px 11px) left top    / 100% 3px no-repeat,
    repeating-linear-gradient(90deg,
      color-mix(in srgb, var(--gold) 70%, transparent) 0 5px,
      transparent 5px 11px) left bottom / 100% 3px no-repeat;
}

/*
  Gold chip face for the icon tile.

  The variant selectors have to be listed explicitly: style.css sets
  .stat-card.total .icon and friends, which is one class more specific than a
  bare .stat-card .icon, so a single generic rule here would silently lose and
  the tiles would stay blue/green/orange in every theme.
*/
.stat-card.total .icon,
.stat-card.checked .icon,
.stat-card.unchecked .icon {
  background: radial-gradient(circle at 32% 28%, var(--gold), var(--gold-2));
  color: var(--gold-ink);
  box-shadow: 0 2px 0 color-mix(in srgb, var(--gold-2) 70%, black), inset 0 0 0 2px rgba(0,0,0,.28);
}

/* --------------------------------------------------------------------------
   8. The table
   --------------------------------------------------------------------------
   The ticket list is the thing an operator stares at all shift, so it gets the
   most deliberate treatment: a darker inset well, and a dealt-card entrance.
   -------------------------------------------------------------------------- */

.table-wrap {
  background: linear-gradient(180deg, rgba(0,0,0,.22), rgba(0,0,0,.34));
  border-radius: var(--radius-md);
  box-shadow: inset 0 2px 14px rgba(0,0,0,.45);
}
table.data thead th {
  background: linear-gradient(180deg, var(--color-card-2), var(--color-card));
  color: var(--gold);
  font-weight: 700;
  letter-spacing: .4px;
  text-transform: uppercase;
  font-size: 11.5px;
}
table.data thead th:first-child { border-top-left-radius: var(--radius-md); }
table.data thead th:last-child  { border-top-right-radius: var(--radius-md); }
table.data tbody tr:nth-child(even) { background: rgba(255,255,255,.014); }
table.data tbody tr:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }

/* The serial column is a row number, so set it as one. */
table.data td.serial {
  font-variant-numeric: tabular-nums;
  color: var(--gold);
  font-weight: 700;
  text-align: right;
}

/* A new or just-scanned ticket arrives like a card sliding onto the table.
   desktop.js re-renders the row, so the class is applied to the fresh node. */
@keyframes dealIn {
  from { opacity: 0; transform: translateY(-10px) rotateX(28deg); box-shadow: 0 18px 26px rgba(0,0,0,.5); }
  to   { opacity: 1; transform: none; box-shadow: none; }
}
tr.row-dealt { animation: dealIn 420ms cubic-bezier(.2,.8,.25,1) both; transform-origin: 50% 0; }
tr.row-flash td { animation: rowFlash 1.1s ease-out; }
@keyframes rowFlash {
  0%   { background-color: color-mix(in srgb, var(--color-success) 40%, transparent); }
  100% { background-color: transparent; }
}

/* The empty states get a faint suit watermark instead of flat grey. */
.feed-empty, .drop-zone { position: relative; }

/* --------------------------------------------------------------------------
   8b. Progress track and legend
   --------------------------------------------------------------------------
   style.css hardcodes these to the default green/amber, so on a theme whose
   success and warning colours are muted they stop matching the donut they
   are supposed to describe -- a bright green swatch next to a sage green arc.
   Point them at the same tokens the donut uses.
   -------------------------------------------------------------------------- */

.progress-track .checked {
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--color-success) 70%, black), var(--color-success));
}
.progress-track .unchecked {
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--color-warning) 70%, black), var(--color-warning));
}
.progress-legend .sw.checked   { background: var(--color-success); }
.progress-legend .sw.unchecked { background: var(--color-warning); }

/* --------------------------------------------------------------------------
   9. Theme switcher
   -------------------------------------------------------------------------- */

.theme-switch {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 9px;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  background: var(--color-bg-2);
}
.theme-switch > .material-symbols-outlined { font-size: 17px; color: var(--gold); }
.theme-switch select {
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: 0;
  color: var(--color-text);
  font: inherit;
  font-size: 12.5px;
  padding: 2px 16px 2px 2px;
  cursor: pointer;
  /* Chevron drawn as a gradient so no extra element or icon font is needed. */
  background-image:
    linear-gradient(45deg, transparent 50%, var(--color-text-2) 50%),
    linear-gradient(135deg, var(--color-text-2) 50%, transparent 50%);
  background-position: right 5px center, right 1px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.theme-switch select:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; border-radius: 4px; }
.theme-switch select option { background: var(--color-card); color: var(--color-text); }

/* The login page has no navbar, so it carries its own switcher. It is pinned
   to the bottom of the viewport rather than added to .auth-wrap's flow: that
   wrapper is a centred row flexbox holding the two step cards, so a third
   child would sit beside the card instead of under it. */
.auth-theme-row {
  position: fixed;
  left: 50%;
  bottom: max(20px, env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  display: flex;
  justify-content: center;
  z-index: 2;
}

/* --------------------------------------------------------------------------
   10. Stacking order

   This file loads LAST, which is why it wins every tie against style.css and
   storefront.css. That is also why the old z-index values scattered across those
   files never had the effect they looked like they had, and why the layout was
   genuinely broken rather than merely untidy:

     style.css declared   .navbar { z-index: 30 }
     style.css declared   .toast-container { z-index: 200 }
     this file's rule 9c  .navbar, main.container, .auth-wrap,
                          .toast-container { position: relative; z-index: 1 }

   Same specificity, later file, so every one of those collapsed to 1. The
   navbar and main.container then sat at the SAME level, and because
   main.container is the later sibling in the DOM, page content painted straight
   over the sticky header. The tabs, the theme switcher, the phone-pairing button
   and the account menu were all unclickable wherever content happened to sit
   under them. The account dropdown was worse than invisible in appearance and
   entirely absent in practice: it is a DOM child of the navbar, so it was
   trapped inside the navbar's stacking context at level 1 and painted over by
   the container.

   The scale below replaces the ad-hoc numbers with one ordered set. Two rules
   matter most:

     - page content is BELOW the header, so a sticky header is always clickable
     - toasts and banners are ABOVE the header, so a notification is never
       buried under the navigation it is reporting about

   Levels are spaced by 10 so a value can be slotted between two neighbours
   without renumbering. 9c above is left in place because it also fixes the
   body::before / body::after paint order the felt decals depend on; this block
   only re-states the elements that were wrong.
   -------------------------------------------------------------------------- */

:root {
  --z-content:   10;   /* page content, scrolling under the header          */
  /*
 * The pinned action bar on a phone: the Buy button, fixed to the bottom of the
 * viewport so it sits inside thumb reach instead of mid-page.
 *
 * Above page content, and below everything that must be reachable over it - the
 * scrim at 50, which is what dims the bar while the sign-in sheet is open, and
 * the header at 60. Hardcoding a number here would have been the next stacking
 * bug in this project; the scale exists so there is one place to get it wrong.
 */
  --z-action:    30;   /* pinned bottom action bar, phone only              */
  --z-scrim:     50;   /* dimmer behind the account panel                   */
  --z-header:    60;   /* sticky navbar / topbar -- must be clickable       */
  --z-panel:     70;   /* account dropdowns, children of the header         */
  --z-mobile:    80;   /* the phone scanner takes over the whole viewport   */
  --z-backdrop: 100;   /* modal dimmer                                     */
  --z-modal:    110;   /* the dialog itself                                */
  --z-overlay:  120;   /* blocking overlay during pairing                   */
  --z-banner:   140;   /* success banner                                   */
  --z-toast:    160;   /* transient notifications, topmost of all          */
}

/* Content is below the header. Without this the sticky header is painted over
   by whatever scrolls beneath it. */
main.container,
.storefront,
.auth-wrap {
  position: relative;
  z-index: var(--z-content);
}

/* The header itself. sticky + a z-index creates a stacking context, so its
   z-index is what governs everything inside it -- which is why the dropdown
   below cannot simply be given a huge number. */
.navbar,
.topbar {
  z-index: var(--z-header);
}

/* Dropdowns inherit the header's context, so this only has to place them within
   it. They are positioned already, so no position is set here. */
.nav-account .account-panel,
.topbar-inner .account-panel {
  z-index: var(--z-panel);
}

.account-scrim { z-index: var(--z-scrim); }

.backdrop          { z-index: var(--z-backdrop); }
.modal             { z-index: var(--z-modal); }
.connecting-overlay{ z-index: var(--z-overlay); }
.success-banner    { z-index: var(--z-banner); }
.toast-container   { z-index: var(--z-toast); }
.mobile-wrap       { z-index: var(--z-mobile); }

/* The floating theme picker on the sign-in screen sits above page content but
   below any dialog, so a modal is never trapped behind it. */
.auth-theme-row { z-index: var(--z-content); }

/* --------------------------------------------------------------------------
   11. Motion safety
   --------------------------------------------------------------------------
   Everything above animates. Someone with vestibular sensitivity should get
   the same page with none of it, so one gate kills all of it rather than
   scattering prefers-reduced-motion through each block.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .gold-text,
  .reel > .strip {
    animation: none !important;
  }
  /* A stopped reel would show glyph 1 forever, which looks like a bug rather
     than a choice -- so pin each reel to a different resting symbol. */
  .reel > .strip { transform: translateY(-26px); }
  .reel:nth-child(2) > .strip { transform: translateY(-52px); }
  .reel:nth-child(3) > .strip { transform: translateY(-78px); }
  tr.row-dealt, tr.row-flash td { animation: none !important; }
  .card, .modal, .stat-card, .auth-card, .device-card { transition: none !important; }
}

