/* Shared UI, extending the site's existing tokens and Bootstrap controls. */
/* Mobile-only helper cells (line-break spacer, "Altri prezzi" toggle):
   hidden by default so they never leak into the desktop table as phantom
   unlabelled columns; the mobile media query below turns them back on. */
.game-field-break, .game-field-expand-toggle { display: none; }
.app-main { min-width: 0; }
.app-main h1 { font-size: 1.5rem; overflow-wrap: anywhere; }
.page-header { margin-bottom: 1.25rem; }
.page-header p { color: var(--ink-soft); margin: .35rem 0 0; line-height: 1.6; }
.page-header > *, .app-main .d-flex > *, .search-suggestions .suggestion-item > * { min-width: 0; }
.app-main img { max-width: 100%; }
.app-main .btn, .eshop-link, .btn-eshop-hero { white-space: normal; overflow-wrap: anywhere; }
.filter-bar, .game-list-sort { padding: .85rem; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.filter-bar.form-check { padding-left: 2rem; }
.game-list-sort { display: none; gap: .5rem; margin-bottom: .75rem; color: var(--ink-soft); font-size: .85rem; }
.list-state, .empty-state { padding: 2rem 1rem !important; text-align: center; color: var(--ink-soft); }
.list-state { white-space: normal !important; }
.pill { max-width: 100%; white-space: normal; overflow-wrap: anywhere; }
.pill-store, .pill-console { background: #eef2ff; color: #3730a3; }
.badge-italian { font-size: .75rem; }
.title-main, .game-title-cell { font-weight: 600; max-width: none; overflow-wrap: anywhere; }
.game-cover-thumb { width: 56px; height: 56px; object-fit: cover; border-radius: .5rem; }
.title-japanese { color: var(--ink-soft); font-size: .8rem; margin-top: .25rem; }
.badge-historic-low { display: inline-block; padding: .22rem .6rem; border-radius: 999px; font-size: .75rem; font-weight: 700; background: #e6f6ea; color: #1e7a34; }
/* STEP 7: neutral by default — "Stesso prezzo dell'Italia"/"più dell'Italia"
   are facts, not bad news, so they must not default to the same green as an
   actual saving. Only the "meno dell'Italia" case (a genuine saving) is
   green — see §11: color is never the only signal (the wording already
   says which case it is either way). */
.savings-hint { color: var(--ink-soft); font-size: .8rem; }
.savings-hint-positive { color: #1e7a34; }
.lowest-price-summary { display: flex; flex-direction: column; gap: .15rem; }
.lowest-price-store { font-weight: 700; }
.table-responsive-card { min-width: 0; max-width: 100%; }
.table-responsive-card .table { width: 100% !important; table-layout: fixed; font-size: .85rem; }
.table-responsive-card .table thead th { color: var(--ink-soft); font-weight: 600; font-size: .73rem; text-transform: uppercase; letter-spacing: .03em; border-bottom: 2px solid var(--border); white-space: normal; overflow-wrap: anywhere; }
.table-responsive-card .table td { vertical-align: middle; padding: .75rem .4rem; overflow-wrap: anywhere; }
.game-field-value { min-width: 0; }
.game-field-price { text-align: right; font-variant-numeric: tabular-nums; }
.price-stack { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; font-variant-numeric: tabular-nums; }
.price-previous { color: var(--ink-soft); font-size: .8rem; }
.game-field-title { font-weight: 600; }
.game-title-meta { display: flex; flex-wrap: wrap; gap: .25rem; margin-top: .35rem; }
.game-field-cover img { width: 56px; height: 56px; object-fit: cover; border-radius: .5rem; }
.game-inline-cover { width: 56px; height: 56px; object-fit: cover; border-radius: .5rem; flex: 0 0 auto; }
.game-title-row { display: flex; gap: .5rem; align-items: flex-start; min-width: 0; }
.game-title-row .game-title-text { min-width: 0; }
.game-field-label { display: none; }
.game-list th:has(.visually-hidden) { width: 44px; }
/* STEP 12: recognizable but discreet — the store pill/price themselves are
   the link (see GameUI.eshopLink / ui.store_link), not a boxed CTA. */
.btn-eshop-hero { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: .4rem .6rem; border: 1px solid #d5e0fb; border-radius: .5rem; background: #eef2ff; color: var(--accent); font-size: .8rem; font-weight: 600; text-decoration: none; }
.eshop-link { display: inline-flex; align-items: center; gap: .3rem; padding: .15rem 0; color: inherit; text-decoration: none; }
.eshop-link:hover, .eshop-link:focus-visible { text-decoration: underline; }
.eshop-link .pill { text-decoration: none; }
.eshop-link-arrow { color: var(--accent); font-weight: 700; }
.wishlist-star { min-width: 44px; min-height: 44px; flex-shrink: 0; }
.dataTables_wrapper .pagination { flex-wrap: wrap; gap: .15rem; }
.dataTables_wrapper .dataTables_filter label { max-width: 100%; white-space: normal; }
.dataTables_wrapper .dataTables_filter input { max-width: 100%; }
/* No hamburger button to leave room for anymore (nav links wrap to their
   own row below the brand instead) — just keep the brand from overflowing
   on very narrow screens. */
.app-navbar .navbar-brand { min-width: 0; max-width: 100%; white-space: normal; overflow-wrap: anywhere; font-size: 1rem; }
@media (min-width: 1024px) {
  /* STEP 4: preserve the stable DataTables column schema while creating a
     clearer desktop reading order inside it. Mobile remains governed solely
     by the existing max-width block below. */
  .game-list tbody td { padding-top: .62rem; padding-bottom: .62rem; }
  .game-list th[data-field="position"] { width: 58px !important; }
  .game-list th:has(+ th) { padding-right: 1rem !important; }
  .game-list th { width: auto; }
  .game-list th[data-field="title"] { width: 20%; }
  .game-list th[data-field="cover"] { width: 84px; font-size: .65rem; }
  .game-list th[data-field="favorite"] { width: 52px !important; }
  /* Mobile-only affordance: the store is already named by its own column
     header on desktop, so the inline pill added there would be redundant. */
  .price-store-inline { display: none; }
  /* STEP 14: rank/cover/favorite centered, price already right-aligned via
     .game-field-price above — game-field-* classes are assigned by
     GameList.enhance() at every breakpoint, not just inside the mobile rules
     below, so this reuses the same markup rather than a desktop-only class. */
  .game-list td.game-field-position, .game-list td.game-field-cover, .game-list td.game-field-favorite { text-align: center; }
  .table-responsive-card .game-title-cell > a, .table-responsive-card .title-main > a {
    color: var(--ink); font-size: .94rem; font-weight: 700; line-height: 1.3;
  }
  .table-responsive-card .game-title-cell > a:hover, .table-responsive-card .game-title-cell > a:focus-visible,
  .table-responsive-card .title-main > a:hover, .table-responsive-card .title-main > a:focus-visible { color: var(--accent); }
  .table-responsive-card .game-title-meta, .table-responsive-card .localized-title, .table-responsive-card .title-japanese {
    color: var(--ink-soft); font-size: .76rem; font-weight: 400;
  }
  .game-list td.game-hierarchy-quality .game-field-value { font-weight: 600; }
  .game-list td.game-hierarchy-price-eur .game-field-value,
  .table-responsive-card .price-eur-primary {
    color: var(--ink); font-size: .98rem; font-weight: 700;
  }
  .table-responsive-card .price-stack > .price-eur-primary { order: -2; }
  .game-list td.game-hierarchy-price-native .game-field-value,
  .table-responsive-card .price-native-secondary {
    color: var(--ink-soft); font-size: .78rem; font-weight: 500;
  }
  .game-list td.game-hierarchy-store .game-field-value,
  .game-list td.game-hierarchy-metadata .game-field-value { color: var(--ink-soft); font-size: .8rem; }
  .game-list td.game-hierarchy-convenience .savings-hint { font-weight: 600; }
}
@media (max-width: 1023.98px) {
  /* STEP 6: label + select on one row when there's room instead of always
     stacking (the >=1024px rule keeps this hidden entirely — desktop uses
     column-header sorting instead) — same control, less height between the
     filters and the first result on Nintendo/Classifica/Top Games/Confronto. */
  .game-list-sort { display: flex; flex-wrap: wrap; align-items: center; padding: .6rem .85rem; margin-bottom: .5rem; }
  .mobile-hide { display: none !important; }
  .table-responsive-card { background: transparent; border: 0; box-shadow: none; padding: 0; }
  .table-responsive-card .table, .game-list tbody { display: block; width: 100% !important; }
  .table-responsive-card thead { display: none; }
  /* STEP 2: a server-rendered table that JavaScript has not turned into
     cards (scripts off, or before DOMContentLoaded) stays a plain table —
     header visible, natural column widths, horizontal scroll inside the
     card instead of squeezed cells or page overflow. Once GameList adds
     .game-list (every table, at DOMContentLoaded) the card rules apply. */
  .table-responsive-card:has(> .table:not(.game-list)) { overflow-x: auto; }
  .table-responsive-card .table:not(.game-list) { display: table; table-layout: auto; }
  .table-responsive-card .table:not(.game-list) thead { display: table-header-group; }
  .table-responsive-card .table:not(.game-list) :is(th, td, td *) { overflow-wrap: normal; }

  /* Compact mobile row: a single flex line-set per game, not a stacked
     label/value card. Row 1 (cover, title, favorite) always fits on the
     first line; remaining "compact" fields wrap onto their own line(s) as
     small inline chips. Fields with no explicit mobile role are omitted
     entirely on mobile (still present, and sortable/searchable, in the
     desktop table) rather than stacked as a tall label/value list. */
  .game-list tbody > tr {
    display: flex; flex-wrap: wrap; align-items: flex-start;
    column-gap: .5rem; row-gap: .2rem;
    padding: .45rem .6rem; margin: 0 0 .4rem;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: var(--shadow);
  }
  /* Real root cause (verified with CSS.getMatchedStylesForNode, not just
     specificity on paper): it's NOT Bootstrap core's .table-striped/
     .table-hover (those only set custom properties, and their own
     box-shadow painter — .table > :not(caption) > * > * — is low
     specificity, 0-1-1). The actual offender is DataTables' OWN Bootstrap5
     integration CSS (dataTables.bootstrap5.min.css):
       table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * {
         box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.05);
       }
     That selector chains table.dataTable + .table-striped + tbody + tr +
     :nth-of-type — specificity 0-3-3, which beat every earlier attempt at
     a plain (or even tbody>tr>td-qualified) reset here. The 5% grey inset
     is subtle on a single-line row (barely visible on the Home results
     table) but reads as an obvious grey rectangle on taller multi-line
     cells (store-compare rows, "Altri prezzi" toggle) on /top100,
     /nintendo, /confronto, /classifica — same cause everywhere, just more
     visible where cells are taller. !important is the reliable fix instead
     of chasing DataTables' own selector (fragile across versions/variants
     like 2n+1 vs odd) — same approach as the sibling :hover rule below,
     which already needed it for the same reason.
  */
  .table-responsive-card .game-list > tbody > tr > td {
    display: block; padding: 0; border: 0; background: transparent !important;
    box-shadow: none !important; text-align: left; min-width: 0;
  }
  .table-responsive-card .game-list > tbody > tr:hover > td { background: transparent !important; box-shadow: none !important; }
  .table-responsive-card .game-list td.list-state { flex: 1 1 100%; }

  /* Field captions ("Store", "Sconto %"...) stay screen-reader-only on
     mobile: the pills/values they label are already self-descriptive
     (e.g. "Italia", "-30%"), so a visible caption would only add height. */
  .game-list .game-field-label {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
  }

  .game-list td.game-field-position { order: 0; flex: 0 0 auto; align-self: center; font-weight: 700; color: var(--ink-soft); font-size: .8rem; min-width: 1.4rem; text-align: center; }
  .game-list td.game-field-cover { order: 1; flex: 0 0 auto; }
  .game-list td.game-field-title { order: 2; flex: 1 1 120px; min-width: 0; align-self: center; }
  .game-list td.game-field-favorite { order: 3; flex: 0 0 auto; margin-left: auto; align-self: flex-start; }

  /* Zero-height spacer inserted (in JS) right before the first compact/action
     field: flex-basis 100% forces every field after it onto a new flex line,
     so a narrow field (e.g. a store pill) can never sneak onto row 1 next to
     the title/favorite just because there happens to be room left. */
  .game-list td.game-field-break { display: block; order: 4; flex-basis: 100%; height: 0; padding: 0 !important; margin: 0; border: 0; }

  /* Anything not explicitly marked compact/position/action is omitted from
     the mobile view by default (publisher, genere, prezzo precedente,
     scadenza, voto RAWG secondario, ecc.) — opt-in, not opt-out. */
  /* Specificity must beat ".table-responsive-card .game-list > tbody > tr >
     td" (0-2-3) above, which sets display:block on every cell — otherwise
     that rule wins ties on the classes component and "hidden" fields render
     as a stray flex item (order:0, so it floats before cover/title) instead
     of actually disappearing. */
  .table-responsive-card .game-list > tbody > tr > td.game-field-hide-mobile { display: none; }

  .game-list td.game-field-compact { order: 5; flex: 0 1 auto; display: flex; align-items: center; }
  .game-list td.game-field-compact .game-field-value { display: flex; align-items: center; flex-wrap: wrap; gap: .3rem; font-size: .92rem; }
  .game-list td.game-field-compact .game-field-value .pill { font-size: .75rem; padding: .15rem .5rem; }
  .game-list td.game-field-compact.game-field-price { text-align: left; }

  /* STEP 5: the EUR-primary/native-secondary hierarchy STEP 4 gave the
     desktop table (see the min-width:1024px block above) had no mobile
     counterpart — the compact row rendered both figures the same size/
     weight, in source order, so a foreign native price (e.g. "$49.99")
     could visually outrank its own EUR conversion. Mirrors both mechanisms
     that already share these class names: the price-stack inline markup
     (nintendo/top100/confronto, GameUI.price/ui.price) and the Home/
     Classifica's separate Prezzo/Prezzo(€) columns (game-hierarchy-*). */
  .game-list td.game-hierarchy-price-eur .game-field-value,
  .game-list .price-eur-primary {
    color: var(--ink); font-weight: 700; font-size: .92rem;
  }
  .game-list td.game-hierarchy-price-native .game-field-value,
  .game-list .price-native-secondary {
    color: var(--ink-soft); font-weight: 500; font-size: .8rem;
  }
  /* Native price becomes the last compact chip (after store/EUR/sconto,
     all still order:5) instead of leading, same relative demotion as
     desktop's order:-2 promotion of the EUR figure below. */
  .game-list td.game-hierarchy-price-native { order: 6; }
  .game-list .price-stack > .price-eur-primary { order: -2; }

  /* Multi-region price columns (Italia/USA/Giappone): only the cheapest
     store for that title stays visible on mobile by default, marked at
     render time with a .cheapest-badge — the others are reachable via the
     "Altri prezzi" toggle below, not just dropped. */
  .game-list td.game-field-store-compare:not(:has(.cheapest-badge)) { display: none; }
  .game-list tbody > tr.game-row-expanded td.game-field-store-compare { display: flex; }

  /* STEP 7: on /confronto the "Prezzo più basso" summary (hierarchy
     convenience, still the default order:5) is the answer a mobile card
     should lead with — the visible store's own raw price line and the
     "Altri prezzi" toggle move to order:6 so they follow it instead of
     tying with it on DOM order. No effect on Nintendo/Top Games (no
     convenience column there): quality already rendered first by DOM
     order at the old tie, so the visual result is unchanged. */
  .game-list td.game-field-store-compare { order: 6; }
  .game-list td.game-field-expand-toggle { display: flex; align-items: center; order: 6; }
  /* STEP 5: measured ~32px tall with near-zero horizontal padding — under
     the ~44px touch target target. Height/padding only; label, icon,
     expand behaviour and aria-expanded are unchanged. */
  .expand-toggle-btn {
    display: inline-flex; align-items: center; gap: .25rem;
    min-height: 44px; padding: .4rem .5rem; border: 0; background: none;
    color: var(--accent); font-size: .78rem; font-weight: 600;
  }

  .game-list td.game-field-action { order: 6; margin-left: auto; }

  /* Plain key/value tables with no <thead> (e.g. admin diagnostics) can't
     opt fields into the compact row, so they keep the previous full-width
     label/value layout instead of being hidden. */
  .game-list td.game-field-stacked { order: 5; flex-basis: 100%; display: flex; gap: .5rem; }
  .game-list .game-field-stacked .game-field-label {
    position: static; width: auto; height: auto; margin: 0; padding: 0;
    overflow: visible; clip: auto; white-space: normal; flex: 0 0 45%;
    color: var(--ink-soft); font-size: .78rem; align-self: center;
  }

  .game-cover-thumb, .game-inline-cover { width: 48px; height: 48px; }
  .title-main, .game-title-cell {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
    overflow: hidden; font-size: .9rem; line-height: 1.25;
  }
  /* Secondary title lines (publisher/genere, titolo giapponese/tradotto):
     not part of the mobile priority hierarchy. */
  .title-japanese, .localized-title { display: none; }
  .game-title-meta { margin-top: .1rem; gap: .3rem; }
  .game-title-meta .pill { font-size: .68rem; padding: .1rem .45rem; }

  .btn-eshop-hero { min-width: 44px; min-height: 44px; padding: .3rem; }
  /* Inline eShop link (store pill / price) grows a bit of vertical padding
     for touch, while staying visually a link, not a button. */
  .eshop-link { padding: .3rem 0; }

  .title-discount-badge { display: none; }
  .app-main .form-select, .app-main .btn, .app-main input:not([type=checkbox]) { min-height: 44px; }
  .form-check-label { padding: .4rem 0; }
  /* STEP 5: measured ~36px tall (padding .4rem + line-height). Height only,
     scoped to mobile — .app-navbar-inner's flex-wrap already breaks the
     links onto their own row(s) by width, so a taller row does not change
     how many links share a row or the number of rows. */
  .app-navbar .nav-link { min-height: 44px; }
  /* STEP 6: trims the gap above the first real content block (filters,
     search banner...) on every page — desktop's 1.25rem (STEP 4-validated
     density) is untouched. */
  .page-header { margin-bottom: .75rem; }
  .dataTables_wrapper .row > * { min-width: 0; }
  .dataTables_wrapper .dataTables_filter input { width: 100%; margin: .25rem 0 0 !important; }
  .dataTables_wrapper .dataTables_filter label { display: block; }
}
