/* ═══════════════════════════════════════════════════════════════════════════
   part_card.css — the glass part card, shared.
   ═══════════════════════════════════════════════════════════════════════════
   Moved out of repairs.css (2026-09-10) so the active repairs board can draw a
   ticket in exactly this shape without loading the whole repairs sheet. Two
   pages link it: repairs/index.php (the parts grid) right after repairs.css,
   and repairs/active_repairs.php (the Cards view) right after
   activerepairs.css. Both keep the cascade order the rules had before.

   The base .product-card rule is repeated here from repairs.css so the card
   stands on its own on the board; on the parts page it simply restates
   itself. The tint classes at the end are the board's: a ticket's tint
   carries its pay status the way a part's carries its stock. */

/* ---------- Product Card (base) ---------- */
.product-card {
    background: var(--glass-bg);
    backdrop-filter: blur(18px) saturate(160%);
    -webkit-backdrop-filter: blur(18px) saturate(160%);
    border: 1px solid var(--glass-border);
    border-radius: 20px;
    padding: 0;
    transition: all 0.3s ease;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    position: relative;
    overflow: hidden;
    box-shadow: 0 8px 32px var(--glass-shadow), inset 0 1px 0 rgba(255,255,255,0.6);
}
.product-card::before { display: none; }
.product-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 12px 35px var(--glass-shadow), inset 0 1px 0 rgba(255,255,255,0.7);
}

/* ── Base rules the card leans on, restated for the board ──────────────────
   repairs.css keeps its own copies for the rest of that page; here they let
   the ticket cards paint the title and the tile pills the same way. */
.product-card .product-name {
    color: var(--text-primary, #1e293b);
    font-weight: 700;
    font-size: 14px;
    text-align: center;
    margin-bottom: 12px;
    letter-spacing: 0.3px;
    width: 100%;
    min-height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.product-name {
    color: var(--primary);
    font-weight: 700;
    font-size: 13px;
    margin-bottom: 5px;
}
.part-grade {
    position: absolute;
    top: 10px;
    left: 12px;
    z-index: 3;
    padding: 3px 9px;
    border-radius: 999px;
    font-family: var(--font-ui, inherit);
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 0.04em;
    color: #fff;
    white-space: nowrap;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.22);
    pointer-events: none;   /* never intercept the click that adds the part */
}


/* ═══════════════════════════════════════════════════════════════════════════
   GLASS PART CARDS — the tint carries stock
   ═══════════════════════════════════════════════════════════════════════════
   The same construction as the checkout's phone cards: white at the top fading
   into a tint at the foot. There the tint is the phone's colour; parts have no
   colour, so it carries the one thing worth knowing at a glance instead —
   whether the part is on the shelf.

     card-instock  slate blue   · on the shelf, sell it now
     card-restock  red          · none here, check inventory first

   The stock badge stays. The tint is for reading a grid at arm's length; the
   badge is for the exact count once you have picked one out.

   colour-mix keeps both tints at the same lightness, so a red card is no
   heavier on the page than a blue one — the colour should say which, not shout.
   ═══════════════════════════════════════════════════════════════════════════ */
/* The exact values phone_color_hex() returns for a blue phone and a red one, so
   a part card and a phone card tint identically. A desaturated slate was tried
   first and read as almost no tint at all next to the real thing — the mix drops
   to 22% of whatever arrives, so starting from a muted colour leaves nothing. */
.product-card.card-instock { --card-tint: #3060a0; }
.product-card.card-restock { --card-tint: #c02b22; }

.product-card.card-instock,
.product-card.card-restock,
.product-card.card-paid,
.product-card.card-deposit,
.product-card.card-pop {
    border: 1px solid rgba(255, 255, 255, .85);
    border-radius: 26px;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background:
      linear-gradient(180deg,
        rgba(255, 255, 255, .96) 0%,
        rgba(255, 255, 255, .80) 42%,
        color-mix(in srgb, var(--card-tint) 22%, #ffffff) 100%);
    box-shadow: 0 14px 34px rgba(30, 50, 90, .12),
                inset 0 1px 0 rgba(255, 255, 255, .9);
}
/* No colour-mix (older Safari): a plain white card rather than a broken one. */
@supports not (background: color-mix(in srgb, red 50%, blue)) {
    .product-card.card-instock,
    .product-card.card-restock,
    .product-card.card-paid,
    .product-card.card-deposit,
    .product-card.card-pop {
        background: linear-gradient(180deg, #ffffff 0%, #eef2f7 100%);
    }
}
.product-card.card-instock:hover,
.product-card.card-restock:hover,
.product-card.card-paid:hover,
.product-card.card-deposit:hover,
.product-card.card-pop:hover {
    transform: translateY(-3px);
    box-shadow: 0 20px 44px rgba(30, 50, 90, .17),
                inset 0 1px 0 rgba(255, 255, 255, .9);
}


/* The inner bands were solid fills, which cut the card into blocks and hid the
   gradient behind them. Hairlines instead, so the glass runs the full height. */
.product-card.card-instock .card-bottom,
.product-card.card-restock .card-bottom,
.product-card.card-instock .card-bottom-footer,
.product-card.card-restock .card-bottom-footer {
    background: none;
    border-top: 1px solid rgba(30, 50, 90, .09);
}
.product-card.card-instock .card-top,
.product-card.card-restock .card-top {
    background: none;
}

/* ── The part photo: a tile that sits ON the card ───────────────────────────
   It was sunk into the surface — a well with the picture at the bottom of it.
   That treatment exists for photos of PEOPLE and products on the dashboard,
   where a socket reads as a frame. Here the card itself is already a pane of
   glass, and cutting a hole in it put the one thing you are trying to identify
   at the furthest point from the eye.

   Raised instead: a white tile floating just above the gradient, with its own
   soft shadow and a hairline to keep the edge crisp where the card behind it
   is pale. Parts are small dark objects photographed on white, so a white tile
   is also the background they were shot against — the picture ends where the
   tile does rather than fading into a tint. */
.product-card.card-instock .part-photo,
.product-card.card-restock .part-photo,
.product-card.card-paid .part-photo,
.product-card.card-deposit .part-photo,
.product-card.card-pop .part-photo {
    background: #ffffff;
    border-radius: 16px;
    padding: 10px;
    box-shadow: 0 6px 16px rgba(30, 50, 90, .14),
                0 0 0 1px rgba(30, 50, 90, .05),
                inset 0 1px 0 rgba(255, 255, 255, 1);
    transition: box-shadow .18s ease, transform .18s ease;
}
/* Lifts a little further with the card, so the two move as one object. */
.product-card.card-instock:hover .part-photo,
.product-card.card-restock:hover .part-photo,
.product-card.card-paid:hover .part-photo,
.product-card.card-deposit:hover .part-photo,
.product-card.card-pop:hover .part-photo {
    transform: translateY(-2px);
    box-shadow: 0 10px 24px rgba(30, 50, 90, .18),
                0 0 0 1px rgba(30, 50, 90, .05),
                inset 0 1px 0 rgba(255, 255, 255, 1);
}

/* ╔═══════════════════════════════════════════════════════════════════════════
   ║ TRIAL — part photos blended, no white tile
   ║
   ║ TO REVERT: delete this whole block (down to the matching ╚ line) and the
   ║ matching block in neumorphic.css. The tile rules above are untouched and
   ║ take over again on their own — nothing else has to be put back.
   ║
   ║ mix-blend-mode: multiply makes white pixels render as whatever is behind
   ║ them, so the photo's white studio background disappears into the card. No
   ║ file is altered: it works on all 199 photos, jpg and webp included, and on
   ║ anything uploaded later.
   ║
   ║ What it costs: white INSIDE the part goes translucent too — a white
   ║ connector, a printed label, a glare highlight. Most parts are dark objects
   ║ shot on white, so it rarely shows, but a pale part can look hollowed out.
   ╚═══════════════════════════════════════════════════════════════════════════ */
.product-card.card-instock .part-photo,
.product-card.card-restock .part-photo {
    background: transparent;
    padding: 0;
    border-radius: 0;
    box-shadow: none;
    mix-blend-mode: multiply;
}
.product-card.card-instock:hover .part-photo,
.product-card.card-restock:hover .part-photo {
    transform: translateY(-2px);
    box-shadow: none;
}

/* Multiply only works against a LIGHT backdrop — on a dark card it turns the
   photo to mud. The dark themes keep the tile. */
body.dark-mode .product-card.card-instock .part-photo,
body.dark-mode .product-card.card-restock .part-photo,
body.embossed-dark .product-card.card-instock .part-photo,
body.embossed-dark .product-card.card-restock .part-photo,
body.dark-mode .product-card.card-paid .part-photo,
body.dark-mode .product-card.card-deposit .part-photo,
body.dark-mode .product-card.card-pop .part-photo {
    background: #ffffff;
    padding: 10px;
    border-radius: 16px;
    box-shadow: 0 6px 16px rgba(30, 50, 90, .14),
                0 0 0 1px rgba(30, 50, 90, .05);
    mix-blend-mode: normal;
}

/* ═══════════════════════════════════════════════════════════════════════════
   PART CARDS, IN THE CHECKOUT'S ANATOMY
   ═══════════════════════════════════════════════════════════════════════════
   Head · photo and two figures · four tiles · a foot ending in Add. The same
   shape as the phone cards on the main checkout, so the two grids read as one
   product rather than two apps that happen to share a palette.

   Three per row, not five. The card is horizontal now and needs the width; at
   five across the name wrapped and the tiles crushed to nothing.

   The stock gradients above are untouched — .card-instock and .card-restock
   still paint the body, so the tint keeps meaning stock and nothing here
   competes with it.
   ═══════════════════════════════════════════════════════════════════════════ */
.products-grid { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 1500px) { .products-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 900px)  { .products-grid { grid-template-columns: 1fr; } }

.product-card.gsm-card {
    display: flex;
    flex-direction: column;
    padding: 0;
    overflow: hidden;
    font-family: var(--font-ui);
    color: #2c2f34;
}

/* ── Head ───────────────────────────────────────────────────────────────── */
.gsm-card .gsm-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 16px 20px 8px;
    background: none;
}
/* ── Grade and source, in their tiles ───────────────────────────────────────
   Both marks were absolutely positioned into the card's corners. Inside a tile
   they have to sit in the flow like any other tile content, so the positioning
   is unwound here rather than in their own rules — those still apply on the
   MobileSentrix listing cards, which keep the corner layout. */
.gsm-card .gsm-tile-mark .part-grade,
.gsm-card .gsm-tile-mark .part-prov {
    position: static;
    top: auto;
    left: auto;
    right: auto;
    max-width: 100%;
    /* One height for both, and centred within it. A word-pill and a logo-pill
       are naturally different heights, and with the captions pinned to the
       bottom of the tile that difference pushed the grade a row above the
       source. A shared box makes them line up whatever is inside. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* No caption beneath these two any more, so the mark centres on the tile
       rather than sitting above a label. The height still matches the other
       tiles' content so the row keeps one baseline. */
    height: 28px;
}
.gsm-card .gsm-tile-mark {
    justify-content: center;
    gap: 4px;
}
/* The plain-text fallbacks take the same height, so a card with no badge still
   lines up with the ones beside it. */
.gsm-card .gsm-tile-mark > b {
    display: inline-flex;
    align-items: center;
    height: 28px;
}
/* The provider pill carries a logo; the grade pill carries a word. Both need to
   survive a narrow tile without spilling. */
.gsm-card .gsm-tile-mark .source-pill {
    max-width: 100%;
    overflow: hidden;
}
.gsm-card .gsm-tile-mark .source-pill img { max-height: 21px; width: auto; }
.gsm-card .gsm-tile-mark .part-grade {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The part name, in the display treatment the figures use — Oswald, the grey
   gradient, and the drop-shadow raise. It is the card's headline and now the
   only thing in the head, so it carries the weight the code used to share.

   NO text-shadow: the fill is a gradient, and a text shadow paints in the
   foreground — it would sit on top of the fill and halo the letters. */
.gsm-card .gsm-title {
    font-family: var(--font-ui);
    /* 17px, down from 22 (Luis, 2026-09-24): at 22 the name was the loudest
       thing on the card, louder than the price it sits above. */
    font-size: 17px;
    font-weight: 600;
    line-height: 1.12;
    letter-spacing: .2px;
    color: #6b7280;
    text-shadow: none;
    /* Two lines, then ellipsis. Part names run long — "Proximity Light Sensor
       Flex Cable" — and a single clipped line loses the words that identify it. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    /* ENGRAVED, not raised: the white highlight goes DOWN-RIGHT and the dark
       offset UP-LEFT — the reverse of the price beside it. Light collecting on
       the lower lip of a groove is what the eye reads as cut in.

       drop-shadow rather than text-shadow, same as before: the fill is a
       gradient, and a text shadow paints in the foreground where it would sit
       on top of the fill instead of behind the letters. The offsets flip; the
       tool cannot. */
    filter: drop-shadow(1px 1px 1px rgba(255, 255, 255, .95))
            drop-shadow(-1px -1px 1.5px rgba(30, 50, 90, .26));
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
    .gsm-card .gsm-title {
        /* Built around #6b7280 — the same grey as the 30-DAY warranty badge —
           so the cardx27s neutral tone is one colour used twice rather than two
           greys that nearly match. Darker above it, lighter below, the same
           direction the figures fade.

           Softer than the price on purpose: a two-line title at 22px covers far
           more area than a number does, and the dark ramp made the name as
           heavy as the figure being quoted. */
        background: linear-gradient(175deg, #5c626f 0%, #6b7280 42%, #9aa1af 100%);
        -webkit-background-clip: text;
        background-clip: text;
        -webkit-text-fill-color: transparent;
        background-size: 100% 100%;
        background-repeat: no-repeat;
    }
}
/* Flat on the repair checkout's part cards (Luis, 2026-10-13), the same as the
   names in the parts table: no engraving. The Active Repairs cards share this
   title and keep it. */
.product-card.gsm-card .gsm-title { filter: none; }

/* The code, in the fourth tile. Monospace and tabular, because it is an
   identifier you compare against a scanner rather than a phrase you read —
   and monospace is also what keeps twelve digits from ranging in width as the
   numbers change.

   Smaller than the other tiles' values: those are words of two or three
   characters, this is twelve, and at Oswald 15px it would ellipsis away the
   half that distinguishes one part from another. */
/* No caption on this one. The barcode mark says what the number is more
   directly than the word SKU would, so the icon carries the label's job and the
   two sit together as one unit.

   Centred, because with nothing pinned to the bottom of the tile the pair would
   otherwise ride at the top while its neighbours' captions hold theirs down. */
.gsm-card .gsm-tile-sku {
    justify-content: center;
    gap: 5px;
}
/* Large enough to read as a mark rather than a bullet — it is doing the
   labelling now. */
.gsm-card .gsm-tile-sku svg {
    width: 30px;
    height: 30px;
    margin-bottom: 0;
    stroke-width: 1.6;
}
.gsm-card .gsm-tile-sku b {
    font-family: var(--font-num);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: -.3px;
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum' 1;
}
.gsm-card .gsm-imei-head {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    flex: 0 0 auto;
    font-family: var(--font-num);
    font-size: 12.5px;
    font-weight: 700;
    color: #7c8794;
    white-space: nowrap;
    text-shadow: 0 1px 0 rgba(255, 255, 255, .95),
                 0 -1px 1px rgba(30, 50, 90, .22);
}
.gsm-card .gsm-imei-head svg {
    width: 14px; height: 14px; flex: 0 0 auto;
    fill: none; stroke: #a8b1bd; stroke-width: 2; stroke-linecap: round;
}

/* ── Photo and the two figures ──────────────────────────────────────────── */
.gsm-card .gsm-mid {
    display: grid;
    /* Wider than it was: the second figure moving to the foot freed the room,
       and the photo is what identifies the part faster than its name does. */
    grid-template-columns: 165px 1fr;
    gap: 16px;
    padding: 10px 16px 16px;
    align-items: center;
}
.gsm-card .gsm-imgwrap { display: flex; align-items: center; justify-content: center; }
/* Overrides .product-card img, which was sized for the old vertical card. */
.gsm-card .gsm-imgwrap img {
    width: auto !important;
    height: auto !important;
    max-width: 100%;
    max-height: 155px;
    object-fit: contain;
}

.gsm-card .gsm-heroes { display: flex; gap: 8px; }
.gsm-card .gsm-hero {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    text-align: center;
}
.gsm-card .gsm-hero svg {
    width: 19px; height: 19px; margin-bottom: 3px;
    fill: none; stroke: #3a3d42; stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round;
}
/* The same grey gradient the checkout's price and battery figures use, so a
   figure reads as a figure on either page. drop-shadow, never text-shadow —
   the fill is a gradient and a text shadow would paint on top of it. */
.gsm-card .gsm-hero b {
    font-family: var(--font-ui);
    /* The headline figure on the card, so it carries the size. The stock ring
       beside it is deliberately smaller — that one is context, this is the
       number being asked for. */
    font-size: 42px;
    font-weight: 600;
    letter-spacing: .3px;
    color: #2c6bb8;
    filter: drop-shadow(-1px -1px 1px rgba(255, 255, 255, .95))
            drop-shadow(1px 1px 1.5px rgba(30, 50, 90, .34));
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
    /* Blue, where the card title is grey. The price is the number being asked
       for; making it the only coloured figure on the card is what separates it
       from the reference values around it. */
    .gsm-card .gsm-hero b {
        background: linear-gradient(175deg, #1d4d8f 0%, #2c6bb8 45%, #5b93d4 100%);
        -webkit-background-clip: text;
        background-clip: text;
        -webkit-text-fill-color: transparent;
        background-size: 100% 100%;
        background-repeat: no-repeat;
    }
}
.gsm-card .gsm-hero span {
    font-size: 10px; font-weight: 700; letter-spacing: .5px; color: #8a8f97;
}

/* ── Tiles ──────────────────────────────────────────────────────────────── */
/* Vertical dividers only. The horizontal rules above and below this row cut
   the card into three stacked blocks — the gradient runs the full height and
   the bands were fighting it. The seams between tiles stay, because those
   separate four values that would otherwise run together. */
.gsm-card .gsm-tiles {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    background: none;
}
.gsm-card .gsm-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 11px 6px;
    text-align: center;
    border-left: 1px solid rgba(30, 50, 90, .07);
}
.gsm-card .gsm-tile:first-child { border-left: 0; }
.gsm-card .gsm-tile svg {
    width: 19px; height: 19px; margin-bottom: 2px;
    fill: none; stroke: #3a3d42; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
}
.gsm-card .gsm-tile b {
    font-family: var(--font-ui);
    font-size: 15px; font-weight: 600; color: #12161d; letter-spacing: .2px;
    /* Source names and grades are words, not figures, and some are long. */
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gsm-card .gsm-tile span { font-size: 10px; color: #8a8f97; margin-top: auto; }

/* ── Foot ───────────────────────────────────────────────────────────────── */
.gsm-card .gsm-foot {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 16px 16px;
    background: none;
}
.gsm-card .gsm-tab {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-width: 0;
    font-size: 11px;
    font-weight: 600;
    color: #7c8794;
    white-space: nowrap;
    overflow: hidden;
}
.gsm-card .gsm-tab svg {
    width: 13px; height: 13px; flex: 0 0 auto;
    fill: none; stroke: #a8b1bd; stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round;
}
.gsm-card .gsm-tab span { overflow: hidden; text-overflow: ellipsis; }
.gsm-card .gsm-tab em { color: #b6bac1; font-style: italic; font-size: 10px; }

/* No Add button. The whole card has always been the click target on this page —
   the corner pills even carry pointer-events:none so they cannot intercept it —
   so a button was a second way to do the one thing a click already does.

   The MS cost sits UNDER the price, in the slot the "PRICE" caption held. That
   caption was labelling the obvious: a large figure with a dollar sign in front
   of it does not need a word confirming it is a price. The cost does need
   saying, and directly beneath the number it relates to is where it belongs —
   the two are read together or not at all.

   Chip, not plain text: it keeps the cost visibly a different KIND of value
   from the price above it, which matters when one is quoted to a customer and
   the other never is. */
.gsm-card .gsm-cost-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-top: 4px;
    padding: 3px 11px 3px 9px;
    border-radius: 999px;
    background: rgba(30, 50, 90, .06);
    color: #5c6673 !important;
    font-family: var(--font-num);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: -.2px;
}
/* The sliders mark it carried in the foot. Kept so the chip is recognisable as
   the same value that used to sit down there. */
.gsm-card .gsm-cost-chip svg {
    width: 12px;
    height: 12px;
    flex: 0 0 auto;
    fill: none;
    stroke: #8a93a0;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* One figure in the hero row now, so it centres on the space the two shared. */
.gsm-card .gsm-heroes { justify-content: center; }

/* The stock badge is carried hidden for any script that still reads it; the
   IN STOCK tile is what the eye uses now. */
.gsm-card .stock-badge[hidden] { display: none !important; }

/* ── Why the two marks sat at different heights ─────────────────────────────
   Not the tiles. The provider logos carry EXPLICIT heights per artwork —
   .src-ico-prov 12px, .src-ico-house 15px, .src-ico-maya 17px — set back when
   these lived in a small corner pill and each vendor's file needed its own
   size to look even. An explicit height beats a max-height, so the 21px ceiling
   set for the tiles did nothing, and the source pill stayed roughly 16-21px of
   visible chrome against the grade pill's fuller box. Equal containers, unequal
   contents: the two read as sitting at different heights because their painted
   surfaces genuinely were.

   Fixed by sizing the artwork for THIS context rather than the corner one, and
   by giving both pills the same painted height so their tops and bottoms line
   up rather than just their boxes. */
.gsm-card .gsm-tile-mark .part-prov .src-ico,
.gsm-card .gsm-tile-mark .part-prov .src-ico-house,
.gsm-card .gsm-tile-mark .part-prov .src-ico-prov,
.gsm-card .gsm-tile-mark .part-prov .src-ico-maya {
    height: 19px;
    max-height: none;
    max-width: 104px;
}

/* One painted height for both marks, so the red pill and the pale pill occupy
   the same band. Padding on the sides only — the height does the vertical. */
.gsm-card .gsm-tile-mark .part-grade,
.gsm-card .gsm-tile-mark .part-prov {
    height: 30px;
    padding-top: 0;
    padding-bottom: 0;
    box-sizing: border-box;
}
.gsm-card .gsm-tile-mark .part-grade { padding-left: 14px; padding-right: 14px; }
.gsm-card .gsm-tile-mark .part-prov  { padding-left: 12px; padding-right: 12px; }

/* ── Stock badge, beside the price ──────────────────────────────────────────
   A dashed ring with the count in it, sitting before the price on the same row.

   Dashed rather than solid on purpose: a solid ring reads as a status lamp —
   something the system is asserting — where this is a running count that will
   be different tomorrow. The broken line says "measured", not "declared".

   Bordered rather than filled so it cannot compete with the price beside it.
   That is the figure being asked for; this is context. */
/* Bottom-aligned, not centred. Both columns end in a caption — IN STOCK under
   the ring, the MS chip under the price — and aligning the bottoms is what puts
   those two on one line. Centring aligned the middles instead, which left the
   captions at different heights because the ring and the price are not the same
   size. */
.gsm-card .gsm-heroes {
    align-items: flex-end;
    gap: 18px;
}

/* The ring and its label, as a column mirroring the price and its chip. */
.gsm-card .gsm-stock {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
}
.gsm-card .gsm-stock-dot {
    /* Smaller: with the label lifted out, the ring only has to hold a number,
       and at 74px it was competing with the price for size. */
    width: 56px;
    height: 56px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border: 2px dashed rgba(30, 50, 90, .28);
    background: rgba(255, 255, 255, .45);
}
.gsm-card .gsm-stock-dot b {
    font-family: var(--font-ui);
    font-size: 23px;
    font-weight: 600;
    line-height: 1;
    color: #3d4653;
    /* Solid, so text-shadow is the right tool — no gradient fill to paint over,
       unlike the price beside it. */
    text-shadow: -1px -1px 1px rgba(255, 255, 255, .95),
                  1px  1px 1.5px rgba(30, 50, 90, .30);
}
/* The same chip as the MS cost, without the icon — same fill, same radius, same
   type, same 4px lift off the figure above it. Two captions on one line that
   were only nearly alike read as a mistake; identical, they read as a pair.

   Letter-spacing is the one difference, and it is the text's doing rather than
   a choice: IN STOCK is two short words in caps where MS-44 is a code, and caps
   need the air or they set solid. */
.gsm-card .gsm-stock-label {
    display: inline-flex;
    align-items: center;
    margin-top: 4px;
    padding: 3px 11px;
    border-radius: 999px;
    background: rgba(30, 50, 90, .06);
    color: #5c6673;
    font-family: var(--font-num);
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: .3px;
    line-height: 1.45;
}

/* The ring takes the card's own stock colour, so it agrees with the tint behind
   it rather than introducing a third opinion about the same fact. */
.product-card.card-instock .gsm-stock-dot { border-color: rgba(48, 96, 160, .45); }
.product-card.card-restock .gsm-stock-dot { border-color: rgba(192, 43, 34, .45); }
.product-card.card-restock .gsm-stock-dot b { color: #a8322a; }

/* ── Warranty badge ─────────────────────────────────────────────────────────
   The same pill as the MS grade badge — it inherits .part-grade for shape and
   type, and only the colour differs, which comes inline per tier. Sharing the
   component is the point: grade and warranty are both "a fact about this part,
   asserted by whoever supplied it", and they should look like siblings.

   Slightly wider padding than the grade pill: "LIFETIME" is a longer word than
   "XO7" and a tier that wraps or ellipses defeats the badge. */
.gsm-card .gsm-tile-mark .part-warranty {
    position: static;
    height: 30px;
    padding-left: 13px;
    padding-right: 13px;
    letter-spacing: .3px;
}

/* This tile keeps its caption where Grade and Source lost theirs. "XO7" and a
   vendor logo say what they are; "LIFETIME" on its own could be almost
   anything. */
.gsm-card .gsm-tile-mark > span {
    margin-top: auto;
}

/* ── Foot: the warranty condition ───────────────────────────────────────────
   One line now, centred. It replaced the model and the fit-time tabs — the
   model is already on the card twice over (it is what you filtered by to get
   here), and the fit time was demo copy standing in for nothing.

   This is a CONDITION, not a fact about the part like the tiles above it, so it
   reads as a single centred sentence rather than sitting in a column of its
   own. Quieter than the tiles too: it is the small print, and it should look
   like small print until someone needs it. */
.gsm-card .gsm-foot { justify-content: center; }
.gsm-card .gsm-tab-terms {
    flex: 0 1 auto;
    justify-content: center;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .1px;
    color: #98a1ad;
    /* The sentence runs long on a narrow card; let it wrap rather than clip,
       since a warranty term cut in half is worse than one on two lines. */
    white-space: normal;
    text-align: center;
    line-height: 1.35;
}
.gsm-card .gsm-tab-terms span { overflow: visible; text-overflow: clip; }
.gsm-card .gsm-tab-terms svg { stroke: #b3bcc8; }

/* ── Tickets on the active repairs board (Cards view) ──────────────────────
   Same card, and the tint carries the pay status instead of the stock:
     card-paid     green  · nothing owed
     card-deposit  amber  · a deposit is down, a balance is owed
     card-pop      red    · pay on pickup, the whole amount is owed */
.product-card.card-paid    { --card-tint: #059669; }
.product-card.card-deposit { --card-tint: #d97706; }
.product-card.card-pop     { --card-tint: #c02b22; }
.product-card.card-paid .gsm-stock-dot    { border-color: rgba(5, 150, 105, .45); }
.product-card.card-deposit .gsm-stock-dot { border-color: rgba(217, 119, 6, .45); }
.product-card.card-pop .gsm-stock-dot     { border-color: rgba(192, 43, 34, .45); }

/* ── Warehouse cards in the inventory card's dress (Luis, 2026-09-30) ──────
   The same white-to-tint glass as card-instock / card-restock, in the gold the
   warehouse listings already wear, so "order this" reads apart from "on the
   shelf" and "out" at a glance. The dashed gold edge from repairs.css stays. */
.product-card.card-wh { --card-tint: #ca8a04; }
.product-card.gsm-card.card-wh {
    border-radius: 26px;
    backdrop-filter: none; -webkit-backdrop-filter: none;
    background:
      linear-gradient(180deg,
        rgba(255, 255, 255, .96) 0%,
        rgba(255, 255, 255, .80) 42%,
        color-mix(in srgb, var(--card-tint) 22%, #ffffff) 100%);
    box-shadow: 0 14px 34px rgba(30, 50, 90, .12), inset 0 1px 0 rgba(255, 255, 255, .9);
}
.product-card.card-wh .gsm-stock-dot { border-color: rgba(202, 138, 4, .55); }
.product-card.card-wh .gsm-stock-dot b { color: #a16207; }
.product-card.card-wh .gsm-hero b.wh-price { font-size: 26px; color: #a16207; }
/* "Check price" at the size of a real price, over the older small grey rule. */
.product-card.gsm-card.card-wh .gsm-hero b.product-price.wh-price {
    font-size: 30px !important; font-weight: 800 !important; color: #a16207 !important;
    letter-spacing: -.02em; line-height: 1.1; opacity: 1 !important;
}
