/*
 * Throwing Bones — tool-specific styles (pile/card grid). Requires
 * ../../shared/css/tokens.css and ../../shared/css/tool-shell.css loaded
 * first. Source: build_docs/design_handoff_witch_tools/redesign/tools.html.
 */

/* Stage B restoration (Throwing Bones master spec SS1.1/SS4.2): canonical
   card aspect ratio recovered from the original (dead but intact)
   generateDecks.php - $cardWidth=400,$cardHeight=515 - corroborated
   directly by newcards/.new itself being a 400x515 PNG. That 400:515
   ratio (~0.777, a portrait card closer to a standard playing card than
   tarot) replaces the redesign's square-ish grid-cell-driven card box.
   Height keeps the redesign's already-tuned 250px; width is derived from
   the recovered ratio rather than estimated. .pile gets an explicit
   matching width instead of stretching to the grid column, so every card
   is the same canonical size regardless of column width (pile footprint
   variation belongs to placement/rotation, not card scale - SS6). */
:root { --card-h: 250px; --card-w: calc(var(--card-h) * 400 / 515); }

/* Stage C tonal pass (SS14): "SAME semantic category mapping; RELATED
   tonal grammar; NOT necessarily identical rendered colour in every
   tool/material." These are descendants of the 8 existing --type-*-bg
   tokens (shared/css/tokens.css, same ones leaves.js's classColor() keys
   off) - the semantic hex values themselves are untouched, and Reading
   Leaves keeps using them directly with its own treatment, per spec.
   Scoped here (not centralized into tokens.css) since this pigment-like
   treatment is Throwing-Bones-specific.
   Each is the category's own --type-*-bg blended toward a shared warm
   neutral - ink-mid for the six dark/mid categories (desaturates/warms
   without much lightening, since most of their paired fg colours are
   light and a lightening blend would have hurt contrast), ochre-field
   for the two already-light categories (testing/damages, whose fg is
   dark - blending those toward ink-mid would have darkened them into
   their own dark fg instead). Verified numerically (not eyeballed): every
   category's WCAG contrast against its existing --type-*-fg is equal to
   or better than before this pass, except healing, which was already
   below 4.5:1 before this change (2.94) and is improved, not worsened
   (3.42) - a pre-existing deficiency in that one semantic pair, flagged
   rather than silently fixed by redesigning a protected semantic colour. */
:root {
  --type-legal-material: #2B2017;
  --type-magic-material: #1F4353;
  --type-supernatural-material: #276577;
  --type-healing-material: #32839E;
  --type-testing-material: #DEAD53;
  --type-maleficium-material: #7E4824;
  --type-death-material: #3F2F21;
  --type-damages-material: #C7955F;
}
.card[data-type="legal"] { border-color: var(--type-legal-material); }
.card[data-type="legal"] .cband { background: var(--type-legal-material); }
.card[data-type="magic"] { border-color: var(--type-magic-material); }
.card[data-type="magic"] .cband { background: var(--type-magic-material); }
.card[data-type="supernatural"] { border-color: var(--type-supernatural-material); }
.card[data-type="supernatural"] .cband { background: var(--type-supernatural-material); }
.card[data-type="healing"] { border-color: var(--type-healing-material); }
.card[data-type="healing"] .cband { background: var(--type-healing-material); }
.card[data-type="testing"] { border-color: var(--type-testing-material); }
.card[data-type="testing"] .cband { background: var(--type-testing-material); }
.card[data-type="maleficium"] { border-color: var(--type-maleficium-material); }
.card[data-type="maleficium"] .cband { background: var(--type-maleficium-material); }
.card[data-type="death"] { border-color: var(--type-death-material); }
.card[data-type="death"] .cband { background: var(--type-death-material); }
.card[data-type="damages"] { border-color: var(--type-damages-material); }
.card[data-type="damages"] .cband { background: var(--type-damages-material); }
/* Stage F (SS12.1/12.4) investigated live: the spec wants roughly 10-14
   piles in a typical desktop first viewport AND zero card-to-card
   overlap between different piles. At this library's authored spread
   (several templates legitimately reach 100-130px past their own
   canonical card box in some direction - SS5's per-family variety, not
   a bug) those two targets conflict under a single uniform grid gap.
   Tried tightening gap 160x120 to 95x60 to chase the density target -
   confirmed live (screenshot) it produces real, visible card-over-card
   overlap between adjacent piles and between scroll rows, a correctness
   regression worse than an under-filled first viewport. Reverted to the
   gap already proven collision-free live. A true fix (giving each pile
   only as much territory as ITS OWN chosen template needs, instead of a
   single uniform worst-case gap) is real scope beyond a CSS tweak - not
   attempted here; left for a dedicated pass if tighter density is
   wanted badly enough to justify it. */
.grid { margin-top: 44px; display: grid; grid-template-columns: repeat(auto-fill, var(--card-w)); gap: 160px 120px; justify-content: space-between; }
.pile { position: relative; width: var(--card-w); height: var(--card-h); cursor: pointer; overflow: visible; }
/* Safari flicker fix (2026-10-12): was .pile:hover { z-index: 10; }.
   Hover toggled whether a pile was a stacking context. Un-hovered, a
   READ card (z 999) escaped above the drawer backdrop (z 19); hovering
   it capped it at 10, under the backdrop, which took the pointer, which
   un-hovered the pile - a light/dark loop. Every pile is now a constant
   stacking context at the same level, so nothing re-layers on hover. */
.pile { z-index: 10; }
/* User request, 2026-10-12: the card whose drawer is open stays
   undimmed. JS (setReadingCard) lifts its pile above the backdrop (19)
   and level with the drawer (20, later in the DOM, so still on top) for
   as long as the drawer is open - set on READ, not hover, so the flicker
   loop above cannot recur. The pile's other cards and badges get
   brightness(.65), the same 35% black the backdrop applies, and stay
   click-through so a click there still lands on the backdrop. */
.pile-reading { z-index: 20; pointer-events: none; }
.pile-reading .card-reading { pointer-events: auto; }
.pile-reading > .count-badge, .pile-reading > .kept-badge { transition: filter 320ms; }
.pile-dim > .card:not(.card-reading) { filter: drop-shadow(0 3px 6px rgba(0,0,0,.25)) brightness(.65); }
.pile-dim > .card-rummaged:not(.card-reading) { filter: drop-shadow(0 7px 12px rgba(0,0,0,.3)) brightness(.65); }
.pile-dim > .count-badge, .pile-dim > .kept-badge { filter: brightness(.65); }
/* Stage K (SS11): keyboard/AT equivalent to press-and-hold - a real,
   reachable <button>, invisible until it actually has focus (SS17
   "restrained" - mouse users already get the hold gesture, this is the
   keyboard/AT path specifically, not a second mouse affordance). */
.pile-read-btn {
  position: absolute; left: 0; bottom: -26px; z-index: 1001;
  background: none; border: none; font-size: 11px; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase; color: var(--ink-mid);
  cursor: pointer; padding: 4px 2px; opacity: 0; pointer-events: none;
}
.pile-read-btn:focus { opacity: 1; pointer-events: auto; outline: 2px solid var(--ochre); outline-offset: 2px; }
/* Restrained progressive acknowledgement of a hold in progress (SS11) -
   a slight, gradual lift on the whole pile, not a flashy progress ring
   or spinner (SS29 "no decorative occult/UI theatrics"). */
.pile-holding { transition: filter 550ms linear; filter: brightness(1.06) saturate(1.1); }
.pile-empty { grid-column: 1/-1; color: var(--ink-mid); font-size: 14px; padding: 60px 0; text-align: center; }
/* Final Implementation Package SS14/QA: "the brown card-count badge
   belongs to a dedicated pile-level UI layer above card z-index
   competition... rummaged cards must never cover it." z-index 5 (bug
   fix, 2026-10-07) was below the rummage/cast z-index of 999 a card can
   reach - confirmed live, a rummaged card rendered on top of the badge.
   1000 is above every z-index a .card itself ever reaches (max 999,
   applyRummageVisual); still below .pile-read-btn's 1001, which does
   not overlap it spatially so the ordering between those two does not
   matter. */
.count-badge {
  position: absolute; top: -14px; right: -14px; background: var(--rust); color: #fff;
  font-size: 11px; font-weight: 700; border-radius: 50%; width: 25px; height: 25px;
  display: flex; align-items: center; justify-content: center; z-index: 1000; box-shadow: 0 2px 5px rgba(0,0,0,.35);
}
/* Stage H (SS16.2): "visual treatment should be restrained" - plain
   text on the existing --ochre token (no new colour, no icon), opposite
   corner from .count-badge so the two never collide. */
.kept-badge {
  position: absolute; top: -14px; left: -14px; background: var(--ochre); color: var(--ink);
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  border-radius: 10px; padding: 2px 8px; z-index: 5; box-shadow: 0 2px 5px rgba(0,0,0,.3);
}
/* Stage B restoration (SS4.1): .card previously declared no background at
   all - only .cart/.cband/.cbody children painted over it, so any gap
   (image not yet loaded, or simply the layout gaps between those
   children) let a card buried behind it show through. Cards are opaque
   physical objects (SS2.3) - var(--paper) gives every card a real opaque
   backing regardless of what its children have painted yet. Fixing the
   source rather than adding !important anywhere. */
.card {
  position: absolute; inset: 0; width: 100%; height: 100%; overflow: hidden; border-radius: 6px; border: 2px solid var(--brown-field);
  cursor: pointer; background: var(--paper);
  display: flex; flex-direction: column;
  transition: transform 260ms cubic-bezier(.2,.8,.2,1), filter 260ms;
  transform: var(--rest); transform-origin: 50% 50%; filter: drop-shadow(0 3px 6px rgba(0,0,0,.25));
}
/* Stage G (SS9 "shadow state"): a card still mid-cast reads as briefly
   airborne - a softer, larger-thrown shadow than its resting one. The
   transition back down to the normal filter above (already in .card's
   own transition list) is what gives the "settle," timed by bones.js's
   own per-card transition-duration/delay (castInCards) rather than this
   rule's own - so it travels and settles together with the transform. */
.card-casting { filter: drop-shadow(0 16px 24px rgba(0,0,0,.32)); }
/* Stage D: the old hover rule replaced the authored --rest transform
   with a separately-computed --hover fan-out destination, which made
   sense for the old procedural fan but would un-author a real
   composition on hover. A small uniform lift layered ON TOP of the
   authored position instead (SS7's real per-card pull-forward is a
   Stage E click interaction, not a hover effect).
   Bug fix (user report, 2026-10-05): this was keyed off .pile:hover,
   but .pile's own box is just one canonical card's footprint (194x250)
   - the CARDS extend up to ~85px beyond it via their authored offsets,
   confirmed live. Near where two piles' extended card footprints
   overlap (more likely once the drawer is open and the grid reflows
   narrower), the mouse could be visually over a card while outside the
   .pile box that actually owns the :hover state, or straddle the
   boundary between two piles - each toggle restarted the 260ms
   transition on a different element, producing a fast flicker.
   Keyed off the individual .card itself instead - each card's hover
   state now matches its own real rendered (transformed) hit area
   exactly, which is also more correct for SS7.1's "the visible card
   surface is the primary interactive surface" than a parent-box
   hover region ever was. */
.card:hover { transform: var(--rest) scale(1.018); filter: drop-shadow(0 10px 16px rgba(0,0,0,.28)); }
/* Stage E (SS8.2/8.3): rummaged cards keep a persistent, slightly
   separated shadow (not the fleeting hover lift) - "the card remains
   visibly displaced after inspection... a person looking at the pile
   should immediately be able to tell which card(s) have been
   rummaged." If it was an under-card, its band/title become
   permanently legible too, not just on hover - a closed drawer should
   not require re-hovering to see what was disturbed.
   Bug fix (user report, 2026-10-05): .card-rummaged alone (one class,
   specificity 0,1,0) was losing to .pile:hover .card's shadow (three
   selectors, 0,3,0) whenever the pile was hovered - confirmed live via
   getComputedStyle, the rummage shadow never actually showed while
   hovering, which is almost always true right after the click that
   caused it. The explicit .card:hover.card-rummaged pairing below
   matches that specificity so the rummage shadow wins in both states. */
.card-rummaged { filter: drop-shadow(0 7px 12px rgba(0,0,0,.3)); }
.card:hover.card-rummaged { filter: drop-shadow(0 7px 12px rgba(0,0,0,.3)); }
.card:focus-visible { outline: 3px solid var(--ochre); outline-offset: 2px; }
/* User request, 2026-10-08: every card's own label/caption should be
   visible always - not just the top card, not only on hover. Previously
   .card-under's cband/cbody defaulted to opacity:0, only revealed via
   :hover or once persisted-rummaged (card-rummaged.card-under). Both of
   those rules are gone now that the base state is already visible. */
.card .cband { position: absolute; left: 0; right: 0; top: 0; padding: 5px 8px; font-size: 12.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; background: var(--type-magic-material); }
.card .cart { flex: 1 1 0; min-height: 0; height: 0; position: relative; overflow: hidden; }
/* 2026-10-08: all newcards/*.png are full-bleed, but most still carry
   their own drawn rounded-card border/frame right at the image edge
   (not a canvas margin, part of the artwork) -- cover alone still shows
   a sliver of it. scale() zooms past that frame so only the inner
   illustration shows; checked against the thickest-bordered files. */
.card .cart img { display: block; width: 100%; height: 100%; object-fit: cover; transform: scale(1.55); }
.card .cbody { position: absolute; left: 0; right: 0; bottom: 0; padding: 10px 8px 8px; background: linear-gradient(to top, var(--ochre-field) 0%, var(--ochre-field) 55%, transparent 100%); }
.card .cdate { font-size: 13px; font-weight: 700; color: var(--ink); }
.card .cexcerpt { font-size: 13.5px; line-height: 1.32; color: var(--ink-soft); margin-top: 3px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

/* Stage H (SS17 "restrained... compatible with existing Witching
   controls"): plain inline text matching the controls row's own type
   scale, no new chrome.
   Stage I regression fix: a selected card's title can be a full
   sentence (an event excerpt, not a short label) - nowrap on it was
   overflowing .controls-extra (which does not itself wrap) by as much
   as 18px of real horizontal scroll, confirmed live. Truncated to one
   line with an ellipsis instead - the full title is already visible in
   the open drawer, this is just a restrained "what you selected" tag. */
.cast-selected { font-size: 13px; color: var(--ink-mid); max-width: 260px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cast-why { font-size: 13px; color: var(--ink-mid); max-width: 320px; }
/* castBar itself (index.html's extraControlHtml) is otherwise
   display:contents so its buttons sit directly in .controls-extra's own
   row - but .controls-extra (shared/css/tool-shell.css, not edited here)
   does not wrap, so a full cast bar (Cast + Selected + Keep + Cast
   around this + Why these + explanation + Clear) could still overflow
   it on anything narrower than a wide desktop. Switching #castBar to a
   real wrapping flex container keeps all of that wrapping onto its own
   extra line(s) instead of forcing the page wider. */
#castBar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; max-width: 100%; }

/* Skeleton loading state (user request, 2026-09-24) — shown immediately
   while the entries + deck-card fetches are in flight. */
.pile-skeleton { height: 250px; border-radius: 6px; background: var(--brown-field); opacity: .35; animation: pile-skeleton-pulse 1.1s ease-in-out infinite alternate; }
.pile-skeleton:nth-child(3n+1) { animation-delay: .1s; }
.pile-skeleton:nth-child(3n+2) { animation-delay: .25s; }
@keyframes pile-skeleton-pulse { from { opacity: .2; } to { opacity: .45; } }
