:root {
    /* The page's own ground. It was a near-white wash (#f8f9fb -> #f7f8fb), which left the
       white panels with nothing to stand on - and on a portal that is white panel above white
       panel, the page read as one sheet of paper. Asked for on the member portal first
       (/Member/*, 2026-10-04), then for the whole site the same day, so the two values live
       here, once, and every page stands on them. The pair the portal was tuned with. */
    --eg-bg: #e6ecf6;
    --eg-bg-soft: #d7e1f2;
    --eg-ink: #22324a;
    --eg-subtle: #66748f;
    --eg-line: rgba(148, 163, 184, 0.28);
    --eg-border: #cbd5e1;
    --eg-glass-top: rgba(255, 255, 255, 0.92);
    --eg-glass-bottom: rgba(244, 250, 255, 0.84);
    --eg-glass-edge: rgba(255, 255, 255, 0.74);
    --eg-shadow: 0 18px 40px -28px rgba(31, 41, 55, 0.52);
    /* Hover is a real lift and has to read as one. The old value (0 18px 40px
       -28px .46) sat within a few pixels of the resting shadow and was even
       slightly lighter, so a hovered card looked exactly like a resting one.
       The lift is now a wider, deeper drop plus a faint contact band under the
       bottom edge — the pair a raised surface casts. */
    --eg-shadow-hover: 0 30px 60px -18px rgba(15, 23, 42, 0.5), 0 8px 18px -10px rgba(15, 23, 42, 0.18);

    /* The bubble layer. A popup opens over a near-white card, and the card's own
       --eg-shadow is far too soft to separate it — the bubble read as part of the
       card. This one pairs a close contact edge with a softer drop, which is what
       puts it visibly above the surface. */
    --eg-pop-shadow: 0 2px 6px -1px rgba(15, 23, 42, 0.18), 0 16px 34px -14px rgba(15, 23, 42, 0.38);
    --eg-primary-a: #3f5e8c;
    --eg-primary-b: #567fbd;
    --eg-secondary-a: #575a68;
    --eg-secondary-b: #7a7f92;

    /* Elementa accent ramp, tokens.json > color.accent. Used as the travelling
       highlight in the "bidding in progress" countdown border. */
    --eg-accent-300: #7cc8fb;

    /* Elementa color.light["--hover-bg"] — the fill an interactive surface takes
       under the pointer (menu option, table row). */
    --hover-bg: #f1f5f9;

    /* Elementa color.accent["600"] — the information tone the toast uses. */
    --accent-600: #0073c7;

    /* Elementa zIndex["--z-top"] — tooltip and toast layer. */
    --z-top: 9999;

    /* Elementa scrollbar tokens (tokens.json > scrollbar / color.light). */
    --scrollbar-size: 4px;
    --scrollbar-size-active: 8px;
    --scrollbar-thumb: rgba(100, 116, 139, 0.35);
    --scrollbar-thumb-active: rgba(100, 116, 139, 0.6);

    /* Elementa's single radius (tokens.json > radius): cards, inputs, buttons,
       badges and popovers all take 4px; only .glass-card gets 12px. */
    --radius: 4px;

    /* Elementa's structural fill (tokens.json > color.light): the quiet inset
       surface the sheet gives a readonly input, a dropzone or a sticky head. */
    --surface-100: #f1f5f9;

    /* The app's main type size: 14px, not Bootstrap's 16px. This is the token Bootstrap's own
       `body` rule reads (`body { font-size: var(--bs-body-font-size) }`), so every element
       that inherits its size - the default text of the whole app - drops at once.

       Deliberately NOT `html { font-size: 14px }`: the root font size is what every spacing
       utility in this app is written in (`p-3`, `gap-2`, `mb-2`, `p-4` are all rem), so
       lowering it would have re-spaced every page by 12.5% to shrink some text. This way the
       text drops and the layout does not - `.p-3` kept its 16px and the header's 8px gap
       stayed 8px.

       What does NOT follow is anything that states a size of its own, which is most of this
       app: `.f11`, the page sheets' pixel sizes, and Bootstrap's components (`.btn` and
       `.form-control` are `1rem`), all of which stay where they are. The app's own body-text
       utility (`.f14` / `.f15`) was brought down to 14px separately, so it matches. Verified
       page by page: 8 text runs changed on the lot page (the description lines and the LOT /
       ring chips) and the 7 info rows below them, 16 on the profile page, 1 on the message
       page, none on the home page or /Auctions - and no element overflowed on any of them. */
    --bs-body-font-size: 0.875rem;
}

html,
body {
    min-height: 100%;
}

body {
    color: var(--eg-ink);
    font-family: "Noto Sans Thai", "Open Sans", Arial, sans-serif;
    background-color: var(--eg-bg);
    /* The two corner tints move with the ground: on the deeper base the old pale violets and
       blues (198,180,255 / 165,205,255) read as glowing patches rather than tints, so they are
       the deeper pair the member portal was tuned with. */
    background-image:
        radial-gradient(42rem 26rem at 92% -5%, rgba(150, 124, 214, 0.40) 0%, rgba(150, 124, 214, 0) 68%),
        radial-gradient(44rem 28rem at 30% 108%, rgba(112, 158, 228, 0.40) 0%, rgba(112, 158, 228, 0) 68%),
        linear-gradient(180deg, var(--eg-bg) 0%, var(--eg-bg-soft) 100%);
    background-repeat: no-repeat;
    background-attachment: fixed;
}

.bg-kob1 {
    background-color: transparent !important;
    background-image: none !important;
}

/* The scrollbar is part of the layout, on every page. A page long enough to need it reserves
   its gutter and a short one does not, which shifts everything 3px sideways - the member
   pages' cards, built to one width (.col-lg-7 of the same container), ended up at different
   left edges depending on how much content was on the page. `stable` keeps the gutter whether
   or not the page needs it, so every page is centred on the same axis. */
:root {
    scrollbar-gutter: stable;
}

.main {
    background: transparent;
    padding-bottom: 28px;
}

a {
    color: #2e5b98;
    text-decoration: none;
}

a:hover {
    color: #234572;
    text-decoration: none;
}

.tec-f2 {
    font-family: "Noto Sans Thai", "Open Sans", Arial, sans-serif;
    letter-spacing: 0.01em;
}

/* The app's own "body text" utility, and it is the app's base size now: 14px, the same as
   --bs-body-font-size. It was 15px, one notch above a 16px base - but the base dropped to 14
   and a utility that sits ABOVE the text around it stopped reading as body text: the lot
   page's Fancier / Country / Ring No. rows were the only 15px left on the page.

   `.f15` is kept as an alias rather than renamed: the same class is written into markup by two
   views and by the pigeon card that pigeon-card.js builds, and the copy of that script inside
   bundle.js / bundle.min.js is only rewritten by a build - a browser still holding the old
   bundle would ask for a class this sheet does not define. (Those rows are 13px anyway, by the
   `.pigeon-img-panel ~ .p-3 .f15` override further down, so the card is unaffected.) */
.f14,
.f15 {
    font-size: 14px !important;
}

.f11 {
    font-size: 11px !important;
}

.text-grey,
.text-color-grey,
.text-color-grey-100 {
    color: #61708a !important;
}

.text-light {
    color: #f8fbff !important;
}

.text-light-200 {
    color: #d9e6ff !important;
}

/* Card title size. The legacy sheet defined this and the Elementa sheet never
   did, so the card title fell through to Bootstrap's h4 (1.5rem = 24px). 18px
   is the size the card was designed at, and it is the same 18px the panel heads
   use, so every title in the app reads at one size. */
.h-h4 {
    font-size: 18px;
    font-weight: 500;
}

#header {
    position: sticky;
    top: 0;
    z-index: 1030;
}

/* The header used to close with a full-width 1px hairline (`--eg-line`) pinned to its bottom
   edge. Removed on request: the header's own glass - the translucent fill plus the 10px blur -
   is what separates it from the page now, and a line on top of that only drew attention to
   where the header ends. It was also the one element here that needed the left/right box trick
   (100vw counts the scrollbar and forces a horizontal one), and dropping the rule drops that
   too. */

#header .header-body {
    backdrop-filter: blur(10px);
    background: rgba(250, 252, 255, 0.8);
    border-bottom: 0;
}

/* The header's own row closes with a line: the sheet's one hard-coded grey (#c9c9c9, darker than
   the --eg-line hairline the header used to carry), asked for by name on 2026-10-04. It adds its
   1px to the header's height, so the sticky header is 73px rather than 72. */
#header .header-top {
    background: transparent;
    border-bottom: 1px solid #c9c9c9;
}

#header .header-row {
    min-height: 72px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
}

#header .header-column {
    display: flex;
    align-items: center;
}

#header .header-column:last-of-type {
    flex: 1 1 auto;
    justify-content: flex-end;
    min-width: 0;
}

.tec-brand {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    white-space: nowrap;
    color: #22324a;
}

.tec-brand:hover {
    color: #1a2740;
}

.tec-brand-logo {
    width: 44px;
    height: auto;
    display: block;
}

#header .header-nav-main nav {
    display: block;
}

#mainNav {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: center;
    gap: 2px;
    margin: 0;
    padding: 0;
    list-style: none;
}

#mainNav > li {
    list-style: none;
}

/* The header nav reads as one system: every item leads with the same family of
   glyph the rest of the sheet uses. inline-flex rather than block so the icon
   and the label share one baseline and the pill keeps its padding.

   Scoped to the TOP-LEVEL items only (> li >): the member links are no longer
   pills but rows inside the avatar's menu, and they must not pick this up. */
#mainNav > li > .dropdown-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border-radius: 10px;
    color: #385378;
    font-family: "Noto Sans Thai", "Open Sans", Arial, sans-serif;
    font-size: 15px;
    font-weight: 500;
    /* The padding is NOT even, by a measured amount. The label's ink sits 1.75px above the
       middle of the pill - the same offset every icon+text row in this app has, because Noto
       Sans Thai's descent is large (Thai needs the room for marks that hang below the
       baseline) and capitals do not fill it. A plain row hides that; a pill with a background
       does not, so half of the difference is moved from the bottom padding to the top: 9.5 -
       22.5 (line box) - 6.5 keeps the pill at its 38.5px, and so keeps the header's height. */
    padding: 9.5px 12px 6.5px;
}

#mainNav > li > .dropdown-item .material-symbols-outlined {
    font-size: 18px;
    line-height: 1;
    flex: none;
    /* follows the label, so the hover colour takes both together */
    color: inherit;
    /* Hand-set rather than derived from the label: the glyph is bigger than the label's own
       15px, and it is lifted 3.5px from where `align-items: center` would put it. The label's
       capitals centre 1.75px above its own line box (see the pill's padding note above), so
       this lands the glyph 1.75px above the letters' middle - measured with the baseline probe
       and the canvas ink scan, not guessed. */
    transform: translateY(-3.5px);
}

#mainNav > li > .dropdown-item:hover {
    background: rgba(63, 94, 140, 0.1);
    color: #22324a;
}

/* The page banner that used to name the section is gone, so the nav item has to
   carry that job. The current page keeps the theme's blue on a light tint of the
   same blue, in the same pill geometry, and the icon follows the label through
   the `color: inherit` above. The :hover pair is here on purpose: hovering the
   active item must not repaint it as an ordinary one. */
#mainNav > li > .dropdown-item.is-active,
#mainNav > li > .dropdown-item.is-active:hover {
    background: rgba(0, 115, 199, 0.1);
    color: var(--accent-600);
    font-weight: 600;
}

/* --- Account menu ---------------------------------------------------------
   A signed-in visitor does not need three more pills competing with the site
   navigation: Profile, My Auctions and Logout fold into one avatar, and the
   avatar is the only thing added to the row. */
#mainNav > li > .member-avatar {
    padding: 6px 10px;
}

#mainNav > li > .member-avatar .material-symbols-outlined {
    font-size: 26px;
    color: #3f5e8c;
}

#mainNav > li > .member-avatar:hover .material-symbols-outlined {
    color: #22324a;
}

/* Bootstrap puts a caret after a .dropdown-toggle; an avatar is the whole
   button, so the caret goes. */
#mainNav > li > .member-avatar::after {
    display: none;
}

/* The panel follows the sheet's own popup recipe: white, --eg-border edge,
   --radius corners, the theme shadow. Its offset from the avatar is left to
   Bootstrap — the panel is positioned with an inline transform, so a margin
   here would not apply. */
#mainNav .dropdown-menu {
    min-width: 190px;
    padding: 6px;
    border: 1px solid var(--eg-border);
    border-radius: var(--radius);
    background: #fff;
    box-shadow: var(--eg-shadow);
}

/* The avatar's menu is the one nav dropdown that carries more than links: its head names the
   account, so it needs the room for an address. 190px fits "Profile" and "Logout" and nothing
   else - the bell's panel overrides the width further down the sheet. */
#mainNav .member-menu .dropdown-menu {
    min-width: 240px;
}

/* The head sits on the items' own 10px gutter and its rule spans the panel, so it reads as the
   menu's own heading rather than a fourth item. The avatar is the trigger's own glyph, a step
   larger and on a disc: the trigger is a pill in a row of pills, and repeating that pill here
   would read as a button. */
#mainNav .member-menu-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px 9px;
    margin-bottom: 6px;
    border-bottom: 1px solid var(--eg-border);
}

#mainNav .member-menu-avatar {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 9999px;
    background: var(--surface-100);
    color: #64748b;
    font-size: 22px;
    line-height: 1;
}

/* `min-width: 0` so the address - which has no space to break at - wraps inside the panel
   instead of widening it. */
#mainNav .member-menu-id {
    display: block;
    min-width: 0;
}

#mainNav .member-menu-name {
    display: block;
    font-family: "Noto Sans Thai", "Open Sans", Arial, sans-serif;
    font-size: 13px;
    font-weight: 600;
    color: var(--eg-ink);
    overflow-wrap: anywhere;
}

#mainNav .member-menu-email {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    font-weight: 400;
    color: #64748b;
    /* Neither a name nor an address has a space to break at when it is long: both wrap instead
       of pushing the panel wider or being clipped. */
    overflow-wrap: anywhere;
}

#mainNav .dropdown-menu .dropdown-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    border-radius: var(--radius);
    color: #385378;
    font-family: "Noto Sans Thai", "Open Sans", Arial, sans-serif;
    font-size: 13px;
    font-weight: 500;
}

#mainNav .dropdown-menu .dropdown-item .material-symbols-outlined {
    font-size: 17px;
    line-height: 1;
    flex: none;
    color: #64748b;
}

#mainNav .dropdown-menu .dropdown-item:hover,
#mainNav .dropdown-menu .dropdown-item:focus {
    background: var(--hover-bg);
    color: #22324a;
}

#mainNav .dropdown-menu .dropdown-item:hover .material-symbols-outlined,
#mainNav .dropdown-menu .dropdown-item:focus .material-symbols-outlined {
    color: inherit;
}

/* --- Notification bell ----------------------------------------------------
   The same pill as the avatar, with the unread count on its top-right corner.
   The number is rendered by the server and refreshed by the push, so the badge
   is never a guess the client made. */
#mainNav > li > .notify-bell {
    position: relative;
    /* 10px, not 6px: the badge hangs 6px past the glyph and must clear the label. */
    gap: 10px;
    padding: 6px 10px;
}

#mainNav > li > .notify-bell .material-symbols-outlined {
    font-size: 24px;
    color: #3f5e8c;
}

#mainNav > li > .notify-bell:hover .material-symbols-outlined {
    color: #22324a;
}

.notify-badge {
    position: absolute;
    top: 2px;
    right: 2px;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: 9999px;
    background: #dc2626;
    color: #fff;
    font-family: "Noto Sans Thai", "Open Sans", Arial, sans-serif;
    font-size: 11px;
    font-weight: 600;
    line-height: 17px;
    text-align: center;
}

/* --- Search ----------------------------------------------------------------
   The magnifier that opens /Search: the item search, offered to guests as well as
   members (so it lives outside the isLogin half of the nav). It is the bell's pill
   with a magnifier in it and no badge, which is why the two share their numbers. */
#mainNav > li > .nav-search {
    gap: 10px;
    padding: 6px 10px;
}

#mainNav > li > .nav-search .material-symbols-outlined {
    font-size: 24px;
    color: #3f5e8c;
}

/* The current page keeps the theme's accent here too: the label is hidden in the bar, so the
   glyph is the whole item and it has to say "you are here" on its own. The base
   `.dropdown-item.is-active` rule above already supplies the tint; this only stops the hover
   colour from repainting the icon as an ordinary one. */
#mainNav > li > .nav-search:hover .material-symbols-outlined {
    color: #22324a;
}

#mainNav > li > .nav-search.is-active .material-symbols-outlined,
#mainNav > li > .nav-search.is-active:hover .material-symbols-outlined {
    color: var(--accent-600);
}

/* The glyph and its badge share a box, because that box is what the badge hangs
   off: the ☰ menu row is as wide as the panel, so a badge pinned to the row
   floated to the far edge, and one pinned to the bare glyph collided with the
   "Notifications" label that follows it. right/top -6 puts the badge's corner on
   the glyph's corner whatever the count's width. */
#mainNav > li > .notify-bell .nav-ico {
    position: relative;
    display: inline-flex;
    flex: none;
}

#mainNav > li > .notify-bell .notify-badge {
    left: auto;
    right: -6px;
    top: -6px;
}

/* A username can be wider than the panel, and the open panel is deliberately not
   clipped, so the label truncates instead of spilling past the edge. */
#mainNav .nav-label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The [hidden] attribute loses to any display the component sets, so it has to
   be said out loud. */
.notify-badge[hidden] {
    display: none !important;
}

.header-btn-collapse-nav {
    width: 42px;
    height: 42px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border-radius: var(--radius);
    border: 1px solid rgba(255, 255, 255, 0.74);
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.9), rgba(242, 248, 255, 0.82));
    backdrop-filter: blur(10px) saturate(125%);
    color: #37557d;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8), 0 10px 26px -22px rgba(31, 41, 55, 0.8);
    transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease, filter 0.2s ease;
}

.header-btn-collapse-nav:hover {
    color: #1f3c66;
    filter: brightness(1.03);
}

.header-btn-collapse-nav:focus,
.header-btn-collapse-nav:focus-visible,
.header-btn-collapse-nav:active,
.header-btn-collapse-nav[aria-expanded="true"] {
    border-color: rgba(255, 255, 255, 0.82) !important;
    color: #24456f !important;
    outline: 0 !important;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.88), 0 12px 28px -22px rgba(56, 110, 176, 0.72) !important;
}

.header-btn-collapse-nav:active,
.header-btn-collapse-nav[aria-expanded="true"] {
    transform: translateY(1px);
}

.card,
.box-a,
.glass-card {
    border-radius: 14px;
}

/* Section border — global: every content section/card shows a real edge */
.card,
.box-shadow-10,
.box-shadow-4 {
    border: 1px solid var(--eg-border);
}

.glass-card,
.box-a {
    position: relative;
    border: 1px solid var(--eg-border) !important;
    background: linear-gradient(180deg, var(--eg-glass-top), var(--eg-glass-bottom)) !important;
    backdrop-filter: blur(10px) saturate(125%);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.74), var(--eg-shadow) !important;
    overflow: visible;
    transition: box-shadow 0.2s ease;

    /* A stacking context of the card's own, so the glaze below stays inside it. */
    isolation: isolate;
}

.glass-card::before,
.box-a::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    background: linear-gradient(145deg, rgba(255, 255, 255, 0.54), rgba(255, 255, 255, 0) 42%);
    opacity: 0.8;

    /* The glaze is decorative - a highlight lying ON the card's surface - but as a
       positioned pseudo-element it painted over the card's TEXT too, and the veil it cast
       depends on where on the diagonal the text sits. On a 776px box the highlight is still
       near its full ~43% white where the first heading is, so the same colour token came out
       as two: `Personal Information` at the top of a box read as a washed-out grey while
       `Address Information` further down the same box read as the real --eg-ink.

       Under the content, over the card's own background: a negative-z pseudo-element paints
       after the element's background and before its in-flow content, and the `isolation` above
       is what keeps it from escaping this card and landing behind the background instead. */
    z-index: -1;
}

/* A photo inside a card used to take that veil, which is why media was lifted into its own
   layer. The glaze is under the content now, so the lift no longer does that job - it stays
   as this sheet's one line about where media sits on a card. */
.glass-card img,
.box-a img {
    position: relative;
    z-index: 1;
}

/* Hover changes colour and shadow only — never position. The old 2px lift is
   gone from both card families (see the net at the end of this sheet).
   The raised card also has to sit ABOVE its neighbours: a grid paints in DOM
   order, so the card in the next row — or the next column — is painted over the
   shadow this one casts, and the shadow reads as if it had been cut off at that
   card's edge. One step above the siblings is enough; the header keeps its 1030. */
.glass-card:hover,
.box-a:hover {
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.78), var(--eg-shadow-hover) !important;
    z-index: 2;
}

.box-shadow-4:not(.box-shadow-hover) {
    box-shadow: unset;
}

/* Legacy sections that carried no class of their own still need the edge */
.box-shadow-10,
.box-shadow-4 {
    border-radius: 14px;
}

.kpi {
    display: grid;
    gap: 10px;
    padding: 16px;
}

.kpi-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.kpi-label {
    font-size: 12px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--eg-subtle);
    font-weight: 600;
}

.kpi-icon {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border-radius: 8px;
    border: 1px solid rgba(148, 163, 184, 0.4);
    background: rgba(255, 255, 255, 0.72);
    color: #33547d;
}

.kpi-icon .material-symbols-outlined {
    font-size: 18px;
}

.bento-tile {
    min-width: 0;
}

.btn {
    border-radius: var(--radius);
    font-family: "Noto Sans Thai", "Open Sans", Arial, sans-serif;
    font-weight: 500 !important;
}

.btn-gradient-primary {
    border: 0;
    color: #fff;
    background: linear-gradient(135deg, var(--eg-primary-a), var(--eg-primary-b));
    box-shadow: 0 10px 24px -18px rgba(29, 78, 216, 0.8);
}

.btn-gradient-primary:hover {
    color: #fff;
    filter: brightness(1.05);
}

.btn-gradient-secondary {
    border: 0;
    color: #fff;
    background: linear-gradient(135deg, var(--eg-secondary-a), var(--eg-secondary-b));
}

.btn-outline-secondary {
    border-color: rgba(100, 116, 139, 0.45);
    color: #334155;
}

.form-control,
.form-select {
    border-radius: 10px;
    border-color: rgba(148, 163, 184, 0.35);
    background-color: rgba(255, 255, 255, 0.84);
}

.form-control:focus,
.form-select:focus {
    border-color: rgba(86, 127, 189, 0.62);
    box-shadow: 0 0 0 0.2rem rgba(86, 127, 189, 0.16);
}

/* The pigeon detail page's old three-column countdown (.time-countdown2 and its two
   spans) went with that markup: the page now wears the shared .pc-cd panel below. */

.pigeon-circles {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    flex-wrap: wrap;
}

.pigeon-circle {
    width: 10px;
    height: 10px;
    border-radius: 999px;
    background: linear-gradient(135deg, #94a3b8, #64748b);
}

.pigeon-circle-more {
    width: auto;
    min-width: 26px;
    height: 20px;
    padding: 0 7px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    font-size: 11px;
    color: #fff;
    background: linear-gradient(135deg, #556987, #344864);
}

hr.hr1,
hr.hr2 {
    border: 0;
    border-top: 1px solid rgba(148, 163, 184, 0.3);
    margin: 10px 0;
}

.search-highlight {
    background: rgba(251, 191, 36, 0.36);
    color: inherit;
    border-radius: 4px;
    padding: 0 2px;
}

/* ---------- Announcements ---------- */
.ann-section {
    padding: 18px 0 6px;
}

.ann-panel {
    /* bottom matches the 18px side padding so the gap under the last alert
       equals the left/right gap */
    padding: 16px 18px 18px;
}

/* Collapsed bar ("mini") — the head sits dead centre and the panel takes the
   yellow tone. padding-top matches the 14px bottom, and the head's 10px
   bottom padding (which only exists to space the open list) is dropped.
   Border is status.warning so the bar reads crisply, matching the green one. */
.ann-panel.is-collapsed {
    padding-top: 18px;
    background: #fffbeb !important;
    background-image: none !important;
    border-color: #f59e0b !important;
}

.ann-panel.is-collapsed .ann-panel-head {
    padding-bottom: 0;
}

.ann-panel.is-collapsed .ann-panel-title {
    color: #92400e;
}

.ann-panel.is-collapsed .ann-showall,
.ann-panel.is-collapsed .ann-showall:hover {
    color: #92400e;
}

.ann-panel.is-collapsed .ann-toggle {
    border-color: #f59e0b;
    background: rgba(255, 255, 255, 0.72);
}

.ann-panel.is-collapsed .ann-toggle:hover {
    background: rgba(146, 64, 14, 0.08);
}

.ann-panel.is-collapsed .ann-toggle .material-symbols-outlined {
    color: #92400e;
}

.ann-panel,
.ann-panel:hover {
    transform: none !important;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.74), var(--eg-shadow) !important;
}

.ann-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding-bottom: 10px;

}

.ann-panel-title {
    margin: 0;
    font-size: 18px;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: var(--eg-ink);
}

.ann-showall {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 13px;
    font-weight: 500;
    color: #3f5e8c;
    white-space: nowrap;
}

.ann-showall:hover {
    color: #24405f;
}

.ann-showall .material-symbols-outlined {
    font-size: 16px;
}

.ann-panel-heading {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

/* Elementa .badge + .badge-red, scoped so Bootstrap's .badge is untouched */
.ann-count {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 10px;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 500;
    line-height: 1.4;
    white-space: nowrap;
    background: #fee2e2;
    color: #991b1b;
}

.ann-count[hidden] {
    display: none;
}

.ann-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid var(--eg-border);
    border-radius: var(--radius);
    background: rgba(255, 255, 255, 0.84);
    cursor: pointer;
    transition: background 0.2s ease, border-color 0.2s ease;
}

.ann-toggle:hover {
    background: rgba(63, 94, 140, 0.1);
}

.ann-toggle .material-symbols-outlined {
    font-size: 18px;
    color: #94a3b8;
    transition: transform 200ms ease;
}

/* Elementa accordion: chevron_right, rotated 90deg while expanded */
.ann-panel:not(.is-collapsed) .ann-toggle .chev {
    transform: rotate(90deg);
}

.ann-list {
    display: block;
    interpolate-size: allow-keywords;
    block-size: auto;
    overflow: hidden;
    transition: block-size 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}

.ann-panel.is-collapsed .ann-list {
    block-size: 0;
}

/* Elementa "Alert" look, warning tone: .alert + .alert-warning */
.ann-item {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    border: 1px solid #fde68a;
    border-radius: 4px;
    background: #fffbeb;
    color: #92400e;
    font-size: 14px;
}

.ann-item + .ann-item {
    margin-top: 8px;
}

/* .alert-body — the content column */
.ann-item-body {
    display: grid;
    gap: 4px;
    flex: 1 1 auto;
    min-width: 0;
}

.ann-item-title {
    font-size: 14px;
    font-weight: 500;
    line-height: 1.5;
    color: #92400e;
}

.ann-item-text {
    font-size: 14px;
    font-weight: 400;
    line-height: 1.5;
    color: #92400e;
}

.ann-item-text a {
    color: #92400e;
    font-weight: 500;
    text-decoration: underline;
}

.ann-item-text a:hover {
    color: #78350f;
}

.ann-empty {
    padding: 16px 2px;
    font-size: 14px;
    color: #6b7a92;
}

.ann-panel-foot {
    padding-top: 12px;
    text-align: center;
}

.ann-panel-foot .btn .material-symbols-outlined {
    font-size: 16px;
    vertical-align: middle;
}

/* ---------- Last Bids cards (inside the Last Bids glass panel) ----------
   Focus is on the price, time and date; the ramp / ring / fancier line stays
   quiet. Values are Elementa light tokens: --text #0f172a, --text-muted
   #475569, --text-subtle #64748b, --border #cbd5e1. */
/* The space between two rows of cards - and nothing after the last one. A margin
   under the bottom row is space the panel's own padding cannot see: it left 28px
   under the last card against 18px at the sides. row-gap only ever exists BETWEEN
   lines, so the two agree on a wide screen (two cards per row) and on a phone
   (one) without asking which card happens to be last. */
#LastBids {
    row-gap: 10px;
}

/* Collapsed ("mini") Last Bids bar takes the green tone instead of the yellow
   one the announcement panel uses — Elementa alertTones.success values, with a
   brighter status.success border so the bar reads crisply. */
#lastBidsPanel.is-collapsed {
    background: #f0fdf4 !important;
    background-image: none !important;
    border-color: #22c55e !important;
}

/* Last Bids badge: solid high-contrast green (7.2:1 with white text) */
#lastBidsPanel .ann-count {
    background: #166534;
    color: #ffffff;
}

#lastBidsPanel.is-collapsed .ann-panel-title {
    color: #166534;
}

#lastBidsPanel.is-collapsed .ann-toggle {
    border-color: #22c55e;
}

#lastBidsPanel.is-collapsed .ann-toggle:hover {
    background: rgba(22, 101, 52, 0.08);
}

#lastBidsPanel.is-collapsed .ann-toggle .material-symbols-outlined {
    color: #166534;
}

/* Open, the Last Bids list is 10px taller than its cards: the list clips at its own
   edge (it has to, it is the collapsing box) and the hover shadow of the bottom row
   reaches 8px past the card, so that room has to exist INSIDE the list. The panel's
   own bottom padding gives up those 10px, so the gap under the last card is still the
   18px the sides have - 10 (list) + 8 (panel). Both live only in the open state: a
   collapsed list is 0 tall and padding is never compressed, and a collapsed bar
   keeps the 18px it had at the bottom, where its head is centred. */
#lastBidsPanel {
    transition: padding-bottom 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}

#lastBidsPanel:not(.is-collapsed) {
    padding-bottom: 8px;
}

#lastBidsPanel:not(.is-collapsed) .ann-list {
    padding-bottom: 10px;
}

#LastBids .lastbid-card {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px;
    border: 1px solid var(--eg-border);
    border-radius: 10px;
    background: #ffffff;
    font-family: "Noto Sans Thai", "Open Sans", Arial, sans-serif;
    text-decoration: none;
    transition: box-shadow 0.2s ease, border-color 0.2s ease;
}

/* The hover shadow, in the card's own edges. The old one was inset 22px with a
   28px blur, so it stopped 8px short of both corners and read as a smudge that had
   been cut off. This one is inset by just over half its blur, so what is left of it
   fades out AT the side edges - the list behind it clips at its own edge and the
   card sits flush with that edge, so a shadow reaching past the card would be cut
   for real. It reaches 8px below the card, which is what the 10px of bottom padding
   on the list below is for. */
#LastBids .lastbid-card:hover {
    border-color: #94a3b8;
    box-shadow: 0 10px 20px -12px rgba(31, 41, 55, 0.45);
}

#LastBids .lastbid-thumb {
    position: relative;
    width: 52px;
    height: 52px;
    flex-shrink: 0;
    border-radius: 8px;
    background-color: #e2e8f0;
    background-size: cover;
    background-position: center;
    overflow: hidden;
}

#LastBids .lastbid-seq {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 1px 5px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.4;
    color: #ffffff;
    background: linear-gradient(to top, rgba(15, 23, 42, 0.78), rgba(15, 23, 42, 0));
}

#LastBids .lastbid-body {
    flex: 1 1 auto;
    min-width: 0;
}

#LastBids .lastbid-line {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px;
    font-size: 13px;
    line-height: 1.35;
    color: #64748b;
}

#LastBids .lastbid-ring {
    font-weight: 500;
}

#LastBids .lastbid-fancier {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* price — the primary focus */
#LastBids .lastbid-price {
    margin-top: 3px;
    font-size: 20px;
    font-weight: 600;
    line-height: 1.2;
    color: #0f172a;
    font-variant-numeric: tabular-nums;
}

#LastBids .lastbid-cur {
    font-size: 12px;
    font-weight: 500;
    color: #64748b;
}

/* date + time — the secondary focus */
#LastBids .lastbid-when {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-top: 2px;
    font-size: 14px;
    line-height: 1.3;
    color: #475569;
    font-variant-numeric: tabular-nums;
}

#LastBids .lastbid-date {
    font-weight: 500;
}

#LastBids .lastbid-time {
    font-weight: 600;
    color: #0f172a;
}

#LastBids .lastbid-ago {
    padding-left: 8px;
    border-left: 1px solid var(--eg-border);
    font-size: 12px;
    font-weight: 500;
    color: #0073c7;              /* Elementa accent-600 */
    white-space: nowrap;
}

#LastBids .lastbid-ago:empty {
    padding-left: 0;
    border-left: 0;
}

/* ---------------------------------------------------------------------------
   Static pages: /Information, /Privacy-Policy, /Disclaimer

   One column of prose and no title band of its own - the nav already names the
   section and a heading above the panel would only repeat it. The text is the
   body face (Noto Sans Thai) at 14px; it used to sit inside `.tec-f2`, the
   sheet's condensed DISPLAY face, so a page of running text was set in a heading
   font at the body's 16px. All three pages share this shell, so they cannot
   drift apart.
   --------------------------------------------------------------------------- */
.info-body {
    padding: 18px 0 34px;
}

.info-panel {
    font-size: 14px;
}

/* These panels have two left margins and they are the document's own.

   LEVEL 1 is what sits directly on the panel - the page's title, its date, and the intro
   paragraphs that come before any heading - and it stays on the panel's edge. LEVEL 2 is a
   section's content, and it is read from the words of that section's heading: every <h4> here
   leads with its icon in a flex row (a 20px glyph plus the row's 8px gap), so the heading's own
   words begin 28px in.

   45px is that 28px plus one step, and the step is the list's own marker: a ul draws its bullet
   in the gutter it is given, so the BULLET lands on the heading's words and the item's text one
   step in from it - a section's list and its heading start from the same line, which is the one
   a reader's eye follows down the page. A paragraph has no marker to place, so it takes the
   same 45px and its first word sits on the item text: what lines up for the eye is the marker,
   and one rule for both keeps them from drifting apart.

   Held together with `h4 ~`: a paragraph that comes before the first heading is level 1
   whatever the markup calls it, so the sibling combinator IS the rule and no class is needed.
   A div holding PROSE is level 2 as well; a div holding a BLOCK - the payment list and the
   closing logo - is a layout wrapper and not text, so it keeps the panel edge and the list
   inside it brings its own gutter. */
.info-panel > h4 ~ p,
.info-panel > h4 ~ ul,
.info-panel > h4 ~ ol,
.info-panel > h4 ~ div:not(.text-center):not(:has(> ol, > ul, > img)) {
    padding-left: 45px;
}

/* One shadow, in both states: these panels do not change under the pointer. They are
   pages of running text, not tiles in a grid - nothing on them opens or navigates, so
   the lift the card families use as a "you can click this" cue would be a promise the
   panel does not keep. (They were unlocked for one round on 2026-10-04 and re-locked
   the same day on request; do not lift them again.)

   z-index stays auto: the panels are stacked in a single column with a gap between
   them, not packed into a grid, so there is no neighbour's shadow for a raised panel
   to be painted under (that is the only thing the grid families need z-index: 2 for). */
.info-panel,
.info-panel:hover {
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.74), var(--eg-shadow) !important;
    z-index: auto;
}

/* Every section heading leads with its icon. As an inline glyph the icon sat on
   the text's baseline and, being larger than the caps, read as if it were sinking
   below the line; the heading is a flex row now, so the icon and the words share
   one centre line whatever their sizes are. */
.info-panel h4 {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 18px;
    font-weight: 600;
    line-height: 1.35;
    color: var(--eg-ink);
}

.info-panel h4 .material-symbols-outlined {
    flex: none;
    font-size: 20px;
    line-height: 1;
    color: var(--accent-600);
}

/* The closing logo is the one the header uses, so a logo change lands in one
   file. It is a square PNG and needs a width of its own. */
.info-logo {
    width: 132px;
    height: auto;
}

@media (max-width: 575.98px) {
    .info-panel h4 {
        font-size: 16px;
    }
}

#footer {
    background: transparent;
    margin-top: 0;
}

#footer .footer-copyright {
    border-top: 1px solid rgba(148, 163, 184, 0.25);
    background: rgba(248, 250, 255, 0.62);
    backdrop-filter: blur(8px);
}

/* One bar, not a column grid. The legacy markup stacked `py-2` on `py-4` and a 24px
   bottom margin on the first column, so two lines of text took a 102px band; the
   bar's own padding is now the only height it has. The text is the sheet's small
   size (13px, Noto Sans Thai) rather than the 14px condensed the footer carried,
   and the chevrons are the same Material glyphs the rest of the app leads with,
   so the footer reads as part of the page instead of part of the old theme. */
.footer-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 8px 24px;
    padding: 12px 0;
    font-size: 13px;
    color: var(--eg-subtle);
}

.footer-copy {
    margin: 0;
}

#sub-menu ul {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 18px;
}

/* The link is inline-level inside its <li>, and an inline box sits on the line's
   baseline: that put both links, and their chevrons, ~1.3px above the bar's
   middle while the copyright sat level with it. The list and the item are flex
   rows now, so each link is centred as a box instead of on a baseline. */
#sub-menu li {
    display: flex;
    align-items: center;
    white-space: nowrap;
}

#sub-menu a {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    color: var(--eg-subtle);
}

#sub-menu a:hover {
    color: var(--accent-600);
}

#sub-menu .material-symbols-outlined {
    font-size: 16px;
    line-height: 1;
    color: rgba(100, 116, 139, 0.75);
}

@media (max-width: 575.98px) {
    .footer-bar {
        justify-content: center;
        text-align: center;
        padding: 10px 0;
    }
}

/* ---------------------------------------------------------------------------
   Header nav — two layouts, one breakpoint at 768px
   ---------------------------------------------------------------------------
   Tablet and desktop put the WHOLE menu in the top nav bar (every item with its
   label), and the ☰ button is not rendered there at all. Below 768px the menu
   moves into the ☰ as a dropdown panel hanging under the button, at the header
   row's right edge — the row is the anchor so the panel lines up with the
   button rather than with the header's own edge (the container keeps a margin
   there). */
@media (max-width: 767.98px) {
    #header .header-row {
        position: relative;
        min-height: 64px;
    }

    #header .header-column:last-of-type {
        flex: 0 0 auto;
    }

    #header .header-nav-main {
        position: absolute;
        /* Spans the header row rather than the viewport: the panel stays aligned
           with the ☰ on the right AND cannot run off the left of the screen, which
           a fixed `100vw - 24px` width did (the container keeps a margin both
           sides, so the ☰ is not at the viewport's edge). */
        left: 0;
        right: 0;
        width: auto;
        top: calc(100% + 6px);
        z-index: 1050;
    }

    #header .header-nav-main #mainNavMenu {
        display: none;
        width: 100%;
        margin: 0;
        padding: 6px;
        border: 1px solid var(--eg-border);
        border-radius: var(--radius);
        /* Solid, like the bell's and the avatar's panels: a dropdown that lets the
           cards behind it ghost through reads as a rendering fault, not as glass. */
        background: #fff;
        box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9), 0 22px 42px -30px rgba(31, 41, 55, 0.75);
        overflow: hidden;
        isolation: isolate;
        /* No open/close effect: the panel appears and disappears the moment it is
           asked to. Bootstrap's own .collapsing transition animates the height and
           is outranked by this rule, so the reveal is instant as well. */
        transition: none;
    }

    #header .header-nav-main #mainNavMenu.collapse:not(.show) {
        display: block;
        max-height: 0;
        overflow: hidden;
        visibility: hidden;
        pointer-events: none;
    }

    #header .header-nav-main #mainNavMenu.collapsing {
        display: block;
        overflow: hidden;
        visibility: visible;
        pointer-events: none;
    }

    /* The panel owns the bell's and the avatar's popups, and it is not much taller
       than one row of items — so once it is open the clipping has to stop or those
       popups render cut off inside it. The collapsed and collapsing states keep
       their own overflow: hidden, which is what the reveal needs. */
    #header .header-nav-main #mainNavMenu.show {
        display: block;
        overflow: visible;
        max-height: 420px;
        visibility: visible;
        pointer-events: auto;
    }

    /* One item per row, and every row fills the panel. */
    #mainNav {
        flex-direction: column;
        flex-wrap: nowrap;
        align-items: stretch;
        justify-content: flex-start;
        gap: 4px;
    }

    #mainNav .dropdown-item {
        padding: 9px 12px;
    }

    /* Every top-level row shares one icon size, icon-to-label gap and left edge, so the labels
       start on the same line; the bell, search and avatar otherwise carry their own larger
       glyph, gap and padding from the icon-only bar. */
    #mainNav > li > .dropdown-item,
    #mainNav > li > .notify-bell,
    #mainNav > li > .nav-search,
    #mainNav > li > .member-avatar {
        gap: 6px;
        padding: 9.5px 12px 6.5px;
    }

    #mainNav > li > .dropdown-item .material-symbols-outlined,
    #mainNav > li > .notify-bell .material-symbols-outlined,
    #mainNav > li > .nav-search .material-symbols-outlined,
    #mainNav > li > .member-avatar .material-symbols-outlined {
        font-size: 18px;
        color: inherit;
    }
}

@media (min-width: 768px) {
    .header-btn-collapse-nav {
        display: none !important;
    }

    /* `!important` because Bootstrap's own `.collapse:not(.show)` hides it, and
       this layout never lets the element take that class's meaning. */
    #header .header-nav-main #mainNavMenu {
        display: block !important;
    }

    #mainNav {
        flex-direction: row;
        flex-wrap: nowrap;
        align-items: center;
        justify-content: flex-end;
        gap: 2px;
    }

    /* The bar shrinks instead of spilling over the brand. `white-space: nowrap`
       makes every label's min-content its full text width, and a flex item's
       automatic minimum size is exactly that — so the wrappers, the items and the
       labels all have to give up that floor for the row to be able to hold the
       nav at all. With it, a long login name ellipsizes. */
    #header .header-nav,
    #header .header-nav-main,
    #header .header-nav-main nav,
    #mainNav,
    #mainNav > li,
    #mainNav > li > .dropdown-item,
    #mainNav .nav-label {
        min-width: 0;
    }

    /* The bell and the avatar are icons in the bar: a label is what a ☰ menu row
       needs to name itself, not a top-bar item that already sits under a glyph.
       The magnifier is the same kind of item, and it carries its name in the
       title and the aria-label instead. */
    #mainNav > li > .notify-bell .nav-label,
    #mainNav > li > .member-avatar .nav-label,
    #mainNav > li > .nav-search .nav-label {
        display: none;
    }

    /* ...and with no label shown there is nothing for the glyph to line up WITH, so the lift
       every other item takes (see the padding note on `#mainNav > li > .dropdown-item`) is off
       here: these two stay centred in their own box, which is what an icon alone wants. Below
       this breakpoint the ☰ panel shows their labels again, and the lift is back on. */
    #mainNav > li > .notify-bell .material-symbols-outlined,
    #mainNav > li > .member-avatar .material-symbols-outlined,
    #mainNav > li > .nav-search .material-symbols-outlined {
        transform: none;
    }

    #mainNav .nav-label {
        max-width: 140px;
    }
}

/* ---------------------------------------------------------------------------
   Scrollbar — Elementa's sheet layer
   ---------------------------------------------------------------------------
   Elementa's scrollbar layer is 6px of nothing but a pill: no track, no
   buttons, no corner, and a rounded thumb inset by 1px so the visible bar is
   4px. The surface's own bar is the fallback, and it is what the window keeps
   (the JS floating pill is only for an opted-in surface, and never for the
   window). The two colours are the ones the sheet defines for a thumb at rest
   and a thumb in use.

   The rule is deliberately unscoped, exactly as in elementa.css, so every
   scroller the app grows later (.table-responsive, a dropdown, the mobile
   nav) picks it up without a per-component rule. */
::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: var(--scrollbar-thumb);
    border: 1px solid transparent;
    border-radius: 9999px;
    background-clip: padding-box;
    transition: background 200ms;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--scrollbar-thumb-active);
}

::-webkit-scrollbar-corner {
    background: transparent;
}

::-webkit-scrollbar-button {
    display: none;
    height: 0;
    width: 0;
}

/* Firefox has no ::-webkit-scrollbar, so the same thin bar and the same two
   colours are expressed with the standard properties. The guard keeps Chromium
   off this branch: there scrollbar-color would win over ::-webkit-scrollbar
   and the resting/hover pair above would stop working. */
@supports not selector(::-webkit-scrollbar) {
    * {
        scrollbar-width: thin;
        scrollbar-color: var(--scrollbar-thumb) transparent;
    }
}

/* ---------------------------------------------------------------------------
   Hover feedback — colour and shadow only, never position
   ---------------------------------------------------------------------------
   The declarations above already drop the 2px lift from the two card families
   this sheet owns. A page can still declare its own tiles in a <style> block,
   and that block arrives AFTER this file, so an override there would win on
   order alone. This net is the app-wide guarantee: every surface whose hover is
   defined anywhere in the app is listed here, and !important is what makes the
   guarantee hold against that later block. Add a class here when a new surface
   is introduced — that is the whole maintenance cost. */
.card:hover,
.box-shadow-10:hover,
.box-shadow-4:hover,
.glass-card:hover,
.box-a:hover,
.auction-kpi-tile:hover,
.lastbid-card:hover,
#LastBids .alert:hover,
.icon-link-hover:hover > .bi {
    transform: none !important;
}

/* ---------------------------------------------------------------------------
   Pigeon photo — inset with a gap, plus its zoom chip
   ---------------------------------------------------------------------------
   The photo used to be full-bleed and clipped by the card's rounded top. It now
   sits on the same 16px axis as the card body (Bootstrap's .p-3 is 1rem), with
   12px under it to clear the title, and takes the sheet's single radius. */
.pigeon-img-panel {
    position: relative;
    padding: 16px 16px 12px;
}

/* display:block kills the inline baseline gap that used to supply the space
   under the picture; the panel's bottom padding replaces it. */
.pigeon-img-panel img {
    display: block;
    border-radius: var(--radius);
    background-color: #e2e8f0;
}

/* The zoom chip is dark-on-media, the same language as #LastBids .lastbid-seq,
   so it stays legible over a bright or a dark photo. 18px = the panel's 16px
   inset + 2px, and z-index 2 clears the img's own layer (1) and the card glaze.
   Round rather than --radius: the chip is a lens on the photo, not a card. */
.pc-zoom {
    position: absolute;
    top: 18px;
    right: 18px;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid rgba(148, 163, 184, 0.6);
    border-radius: 50%;
    background: rgba(15, 23, 42, 0.55);
    color: #ffffff;
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    transition: background 0.2s ease;
}

/* --- Member's standing on the lot ------------------------------------------
   The zoom chip's twin on the other corner: the same glass, the same 18px inset
   and 1px translucent ring — a pill rather than a disc, because it states a fact
   about the member instead of offering an action. Blue while their bid is
   winning, the zoom chip's own dark grey once it is not, green once they have
   won it. The countdown engine swaps the first for the last at zero. */
.pc-status {
    position: absolute;
    top: 18px;
    left: 18px;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 34px;
    padding: 0 13px 0 10px;
    border: 1px solid rgba(255, 255, 255, 0.5);
    border-radius: 9999px;
    color: #ffffff;
    font-family: "Noto Sans Thai", "Open Sans", Arial, sans-serif;
    font-size: 13px;
    font-weight: 500;
    line-height: 1;
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
}

.pc-status .material-symbols-outlined {
    font-size: 17px;
    line-height: 1;
}

/* The ring is the chip's own colour rather than a white halo: on a photo the
   white one read as a seam, and grey/green/blue carry their own edge. The grey
   chips (Outbid, and the zoom disc it mirrors) take #94a3b8, the slate this
   sheet already uses for exactly this kind of secondary mark. */
.pc-status.is-top {
    background: rgba(0, 115, 199, 0.9);
    border-color: var(--accent-600);
}

.pc-status.is-outbid {
    background: rgba(15, 23, 42, 0.6);
    border-color: rgba(148, 163, 184, 0.6);
}

.pc-status.is-winner {
    background: rgba(22, 163, 74, 0.9);
    border-color: #16a34a;
}

/* The pigeon detail page states the LOT's state on its photo rather than the member's
   standing: upcoming until the bidding window opens, live through it, ended once it
   closes. Same glass and geometry. Live wears the accent blue the running state takes
   in the timeline panel beside it, while the two states in which nothing can be bid
   wear the neutral slate the grey chips use. The countdown engine repaints the chip
   as the lot moves through them. */
.pc-status.is-live {
    background: rgba(0, 115, 199, 0.9);
    border-color: var(--accent-600);
}

.pc-status.is-upcoming,
.pc-status.is-ended {
    background: rgb(206 209 217 / 0%);
    border-color: rgb(154 170 191 / 40%);
}

/* `[hidden]` loses to the display this component sets, so it has to be said. */
.pc-maxbid[hidden] {
    display: none !important;
}

/* Bootstrap 5.3 ships `button:not(:disabled) { cursor: pointer }`, which weighs
   (0,2,0) and beats a bare `.pc-zoom` (0,1,0). This ties on weight and wins on
   order, so the affordance is genuinely zoom-in rather than just pointer. */
.pc-zoom:not(:disabled) {
    cursor: zoom-in;
}

.pc-zoom .material-symbols-outlined {
    font-size: 20px;
}

.pc-zoom:hover {
    background: rgba(15, 23, 42, 0.78);
}

.pc-zoom:focus-visible {
    outline: 2px solid #ffffff;
    outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   Pigeon card type scale
   ---------------------------------------------------------------------------
   Every marker in this card — .h-h4, .tec-ring, .no2, .time-countdown2 — was
   left undefined when the Elementa sheet replaced custom.css, so all of them
   fell through to the 16px body default and the card read as bulk rather than
   hierarchy. One compact scale instead, sized against the Last Bids panel the
   card sits beside (13px secondary, 20px price, 11px labels).

   Scoped to the card body, so the same markers elsewhere — the /Auctions lot
   tile, the pigeon detail header — keep the sizes they have today. */
.pigeon-img-panel ~ .p-3 {
    font-size: 13px;
}

.pigeon-img-panel ~ .p-3 .h-h4 {
    font-size: 15px;
    font-weight: 600;
    line-height: 1.3;
}

/* --- Card title: one line, full name in a popup ------------------------------
   A long name wrapped to a second line and pushed everything under it down the
   card, so no two cards in a row agreed on where the countdown panel started.
   The title is one line with an ellipsis now, and the full name is one hover (or
   one tap) away — the same recipe as the lot line and the two meta rows below
   it.

   The clip sits on the title's own span (.pc-title-text), never on the <h4> that
   hosts the popup: a clip there would cut the popup off with it. The popup is
   built by _Layout and only for a title that is really cut off — the title
   measures itself first — so a short name keeps its plain line. */
.pigeon-img-panel ~ .p-3 .h-h4.pc-title {
    position: relative;
    cursor: help;
}

.pigeon-img-panel ~ .p-3 .h-h4 .pc-title-text {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* A title that fits is not clipped and has no popup, so it must not promise one. */
.pigeon-img-panel ~ .p-3 .h-h4.pc-title.no-pop {
    cursor: default;
}

.pigeon-img-panel ~ .p-3 .pc-title-pop {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(100% + 6px);
    z-index: 3;
    padding: 8px 10px;
    border: 1px solid var(--eg-border);
    border-radius: var(--radius);
    background: #ffffff;
    box-shadow: var(--eg-pop-shadow);
    color: var(--eg-ink);
    font-size: 13px;
    font-weight: 400;
    line-height: 1.45;
    white-space: normal;
    opacity: 0;
    visibility: hidden;
    transition: opacity 160ms ease, visibility 160ms ease;
}

/* A precise pointer gets hover (and keyboard focus, since only a clipped title is
   in the tab order). A touch screen has no hover, so _Layout toggles .is-open. */
@media (hover: hover) and (pointer: fine) {
    .pigeon-img-panel ~ .p-3 .h-h4.pc-title:hover .pc-title-pop,
    .pigeon-img-panel ~ .p-3 .h-h4.pc-title:focus-within .pc-title-pop {
        opacity: 1;
        visibility: visible;
    }
}

.pigeon-img-panel ~ .p-3 .h-h4.pc-title.is-open .pc-title-pop {
    opacity: 1;
    visibility: visible;
}

.pigeon-img-panel ~ .p-3 .tec-ring {
    font-size: 13px;
    font-weight: 500;
}

/* The utility carries !important, so the override has to carry it too. */
.pigeon-img-panel ~ .p-3 .f15 {
    font-size: 13px !important;
    /* The Fancier / Country rows lead with an icon instead of the words. Flex
       rather than a baseline hack, so the icon and the value line up whatever
       the font size is; the .visually-hidden label is absolutely positioned, so
       it does not become a third item. */
    display: flex;
    align-items: center;
    gap: 4px;
}

.pigeon-img-panel ~ .p-3 .f15 .material-symbols-outlined {
    font-size: 15px;
    line-height: 1;
    flex: none;
}

/* --- Fancier / Country rows: one line, full value in a popup ----------------
   A long fancier name used to wrap to a second line and push the countdown panel
   and the price down the card. The value clips to one line with an ellipsis now,
   and the full text is one hover (or one tap) away — the same recipe as the lot
   line above.

   The clip sits on the value span (.pc-meta-text), never on the row that hosts
   the popup: a clip on the row would cut the popup off with it. The popup is
   built by _Layout and only for a row that is really cut off — the row measures
   itself first — so a short country keeps its plain line instead of echoing
   itself in a bubble. */
.pigeon-img-panel ~ .p-3 .f15.pc-meta {
    position: relative;
    cursor: help;
}

/* flex:1 + min-width:0 is what lets the text shrink and ellipsis inside a flex
   row; a flex item's default min-width:auto would push the row wider instead of
   clipping it, and the ellipsis would never appear. */
.pigeon-img-panel ~ .p-3 .f15 .pc-meta-text {
    flex: 1 1 auto;
    min-width: 0;
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pigeon-img-panel ~ .p-3 .pc-meta-pop {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(100% + 6px);
    z-index: 3;
    padding: 8px 10px;
    border: 1px solid var(--eg-border);
    border-radius: var(--radius);
    background: #ffffff;
    box-shadow: var(--eg-pop-shadow);
    color: var(--eg-ink);
    font-size: 13px;
    line-height: 1.45;
    white-space: normal;
    opacity: 0;
    visibility: hidden;
    transition: opacity 160ms ease, visibility 160ms ease;
}

/* A precise pointer gets hover. A touch screen has no hover, so the click
   handler in _Layout toggles .is-open instead. The row is deliberately not in
   the tab order — the ellipsis is visual only and the full value is already the
   row's text — so :focus-within is kept merely for a focusable child arriving
   later. */
@media (hover: hover) and (pointer: fine) {
    .pigeon-img-panel ~ .p-3 .pc-meta:hover .pc-meta-pop,
    .pigeon-img-panel ~ .p-3 .pc-meta:focus-within .pc-meta-pop {
        opacity: 1;
        visibility: visible;
    }
}

.pigeon-img-panel ~ .p-3 .pc-meta.is-open .pc-meta-pop {
    opacity: 1;
    visibility: visible;
}

/* The price stays the anchor of the card, and it shares its row with the VIEW
   button — so the two have to finish at the same height. The button is 36px
   (24px line + 2x6px padding); the block was 43.5px, two lines at 1.5 leading.
   The sizes stay and only the leading tightens — 19px over 14px — which is
   also how a price and its "from" line want to sit. */
.pigeon-img-panel ~ .p-3 .no2 {
    font-family: "Noto Sans Thai", "Open Sans", Arial, sans-serif;
    font-size: 18px;
    font-weight: 600;
    line-height: 19px;
}

/* The currency suffix sits inside .no2, so it would inherit the price size;
   it takes the same quiet 12px the Last Bids price uses. */
.pigeon-img-panel ~ .p-3 .cur {
    font-size: 12px;
    font-weight: 500;
}

/* Second half of the same 36px: the "from ... THB" line under the price.
   .f11 is 11px !important, so this only carries the leading. */
.pigeon-img-panel ~ .p-3 .f11 {
    line-height: 14px;
}

/* The other half of the row is the VIEW button, and its column was the other
   thing keeping the row over height: an inline-block sits on the baseline, so
   the line box keeps room below it. Flex drops that line box. The price column
   is a column-flex with its content centred, so the block lines up with the
   button and a fraction of a pixel of rounding never shows as slack under the
   "from" line. Both halves then finish at the button's 36px. */
.pigeon-img-panel ~ .p-3 .row.g-0 > .col {
    display: flex;
    justify-content: center;
}

.pigeon-img-panel ~ .p-3 .row.g-0 > .col:first-child {
    flex-direction: column;
}

/* ---------------------------------------------------------------------------
   Pigeon card type face
   ---------------------------------------------------------------------------
   The sheet used to split in two: --font-sans (Noto Sans Thai) for body and
   general text, and --font-display (Barlow Semi Condensed) for
   headings, buttons, badges and big numbers. The card's general text still
   carried the condensed face it inherited from the legacy .tec-f2, so it now
   takes Noto Sans Thai like the rest of the page. VIEW keeps the display face
   because it is a button and the price because it is a big number — the two
   jobs the sheet reserves it for.

   That split is gone: the sheet carries ONE face now — Noto Sans Thai — for body
   text, headings, buttons, badges and numbers alike, so .tec-f2 is no longer a
   second family and this card needs no override to keep its general text in the
   app face. */

/* ---------------------------------------------------------------------------
   Countdown — status first, end time second
   ---------------------------------------------------------------------------
   The section used to split Starts / Countdown / Ends into three equal columns,
   which read as three peer facts when only one of them matters. It is now the
   remaining time as a single prominent line, with the end date and time kept
   underneath as secondary information.

   startCountdown(..., 'status') writes that whole line, tags [data-cd-host] with
   data-cd-state, and stops itself at zero. Colour escalates with urgency and
   only then: the warning and urgent tints appear under 24 hours, never above. */
/* One definition, three surfaces: the bidding item card (.pigeon-img-panel ~ .p-3),
   the Auction Lot card (.auction-kpi-tile) and the pigeon detail page's own panel
   (.lot-cd, on the column that holds it) share every rule below. :is() keeps the
   original specificity, which is the higher argument's, so the item card is
   untouched and the other two inherit the exact same design. */
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .lot-price-box {
    position: relative;
    padding: 8px 10px;
    border: 1px solid var(--eg-border);
    border-radius: var(--radius);
    /* Frosted, not solid: the panel's own fill is its state colour with the alpha
       byte set to 0x36 (21%) over a 6px blur of what is behind it, so the card's
       background — and the card's watermark — reads through the panel. Neutral
       here, tinted by the state rules below; the blur is stated once, here, and
       every state inherits it. */
    background: #f1f5f936;
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
}

/* The detail page's price sits in a box of its own so that the price and the
   countdown can share one row. It wears the panel surface above and none of the
   countdown's states, and the price is the box's whole point: .no2 and .cur are
   scoped to the two cards, so out here they were reaching it as plain 16px body
   text. It takes the big-number size a price wants and the currency suffix the
   same quiet 12px the card gives it. */
.lot-price-box .no2 {
    font-family: "Noto Sans Thai", "Open Sans", Arial, sans-serif;
    font-size: 24px;
    font-weight: 600;
    line-height: 1.1;
    color: var(--eg-ink);
}

.lot-price-box .cur {
    font-size: 12px;
    font-weight: 500;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .pc-cd {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .pc-cd-line {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 5px;
    /* One size for every state: the line reads at 14px whether it is counting
       down, ending today or ended, so the panel keeps its height as a lot moves
       through its states. */
    font-size: 14px;
    line-height: 1.25;
    font-variant-numeric: tabular-nums;
    color: var(--eg-ink);
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .pc-cd-line .cd-ico {
    font-size: 17px;
    line-height: 1;
    flex: none;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .pc-cd-line .cd-strong {
    font-weight: 600;
}

/* The unit letter is a tag, not part of the number: "21h 32m 59s" reads as three
   figures with three small marks after them, rather than one long bold string. */
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .pc-cd-line .cd-unit {
    font-weight: 400;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .pc-cd-line .cd-soft {
    font-size: 12px;
    font-weight: 400;
    color: #64748b;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .pc-cd-sub {
    font-size: 12px;
    line-height: 1.3;
    color: #64748b;
    font-variant-numeric: tabular-nums;
}

/* The date and the clock are separated by a plain hyphen, written as TEXT in the
   markup — "Sat 03/10/2026 - 23:08", and "Ending today - 4h 41m 12s left" on the
   status line. It replaced the "·" character, which was then replaced by a drawn 4px
   dot; the dot could not be made to look the same twice — at a fractional device
   pixel ratio the same 4px disc rasterises crisp in one row and soft in the next, and
   the status line's had to be a different size from the two instants' — and a hyphen
   has no size to get wrong. */

/* The two dates are a COLUMN, not a run of text: both rows put their hyphen and
   their clock on the same two vertical lines. 93px is the widest weekday the format
   can print — "Mon 30/09/2026", 92.53px measured at 12px in Noto Sans Thai; "Fri" is
   81px — and without it each row's clock lands wherever its own weekday ends, so the
   two clocks (and the hyphens before them) do not line up.
   Row budget: 44px label + space + 93 + " - " + clock = about 180px of the 212px the
   lot card has, and of the 240px the item card has. */
.pc-cd-sub .pc-cd-sub-date,
.pc-cd-sub .pc-cd-sub-start-date {
    display: inline-block;
    min-width: 93px;
}

/* The two instants are the VALUES of their rows, so they keep a weight above the
   label and the date beside them — but their COLOUR belongs to the panel they sit
   in. A tinted panel carries text of its own hue: a blue panel blue text, an amber
   panel amber text, a red panel red text, so the time never reads as a foreign ink
   dropped inside the box. Each coloured state therefore takes a step DEEPER in its
   own hue (see the per-state rules with the tints below) instead of the row's own
   shade, which would have erased the value-over-label contrast the row is built on.
   The neutral panels keep the card's own navy, the ink the ordinal pill (.no-1)
   uses. Both rows share the rule — they are stacked, and a darker Ends over a
   lighter Starts (as it was: only the Ends time was recoloured) read as a mistake. */
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .pc-cd-sub .pc-cd-sub-time,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .pc-cd-sub .pc-cd-sub-start-time {
    font-weight: 500;
    color: #33547d;
}

/* A lot is a window between two instants, so its card keeps BOTH — as two labelled
   rows whose dates line up under one another. The item card has no label column,
   so this rule reaches the lot card and the pigeon detail page. */
:is(.auction-kpi-tile, .lot-cd) .pc-cd-when {
    display: inline-block;
    min-width: 44px;
    color: #94a3b8;
}

/* The panel is three stacked lines that share ONE left edge for their text: line 1
   is [icon][text], and "Starts"/"Ends" line up under the TEXT rather than under the
   icon, so the two instants are indented by exactly the icon plus the line's own 5px
   gap (15 + 5 = 20px). The icon's box is 1em square, so resizing the icon moves this
   indent with it — the two numbers have to change together.
   Every rule here carries one class more than it looks like it needs: :is() in the
   shared rule above takes the specificity of its most specific argument, so the
   shared rule is (0,3,0) and a two-class override would simply lose to it. */
:is(.auction-kpi-tile, .lot-cd) .pc-cd .pc-cd-sub {
    padding-left: 20px;
}

/* The lot panel is narrower than the item panel (it sits inside a 3-up grid), so the
   status line is a point smaller than the shared 15px — and 14px does not fit. The
   widest line the engine writes is "Ending today · 23h 59m 59s left" (the today tier
   starts under 24 hours, so a two-digit hour is reachable every day): 238px against
   the 232px the panel can offer, which wrapped it onto two lines. At 13px it is
   221px. */
.auction-kpi-tile .pc-cd .pc-cd-line {
    font-size: 13px;
}

.auction-kpi-tile .pc-cd .pc-cd-line .cd-ico {
    font-size: 15px;
}

/* Under 24 hours the section takes the warning tone, under an hour the urgent
   one. Above 24 hours it stays on the neutral surface — no warm colour at all.
   The fill is the state's own colour at 21% (hex alpha 36) over the panel's blur. */
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="today"] {
    border-color: #f59e0b;
    background: #fffbeb36;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="today"] .pc-cd-line {
    color: #92400e;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="today"] .cd-soft,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="today"] .pc-cd-sub,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="today"] .pc-cd-when {
    color: #b45309;
}

/* The time of an amber panel is the same amber, one step deeper. */
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="today"] .pc-cd-sub .pc-cd-sub-time,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="today"] .pc-cd-sub .pc-cd-sub-start-time {
    color: #92400e;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="soon"] {
    border-color: #ef4444;
    background: #fee2e236;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="soon"] .pc-cd-line {
    color: #991b1b;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="soon"] .cd-soft,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="soon"] .pc-cd-sub,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="soon"] .pc-cd-when {
    color: #b91c1c;
}

/* The time of a red panel is the same red, one step deeper. */
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="soon"] .pc-cd-sub .pc-cd-sub-time,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="soon"] .pc-cd-sub .pc-cd-sub-start-time {
    color: #7f1d1d;
}

/* Ended: the surface stays neutral and the icon carries the outcome. */
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="ended"] .pc-cd-line {
    color: #475569;
}

/* A neutral panel gets a neutral time: the deepest slate step, so the value still
   stands out from the #64748b row without introducing a hue the panel lacks. */
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="ended"] .pc-cd-sub .pc-cd-sub-time,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="ended"] .pc-cd-sub .pc-cd-sub-start-time {
    color: #334155;
}

/* A live auction has a moving edge: a gradient travels the border so the state
   reads as alive, in the state's own tone — theme blue above a day, amber under
   a day, red under an hour — and it travels faster as the deadline nears.

   The ramp is clipped to the element's BORDER AREA (`background-clip: border-area`),
   which is what lets the panel's own fill stay translucent. The previous recipe put
   the tint in a background LAYER above the ramp, clipped to the padding box, so the
   ramp was hidden only while that layer was OPAQUE — a frosted panel was impossible
   with it. A clip to the border area needs no cover: the ramp lands in the border
   band, follows the same rounded corners, and keeps the band exactly the border's
   width. The colour's own clip is the second value (`padding-box`), because
   background-clip maps to the layers in order and the background-colour is the
   last one — with a single value the fill would be clipped away with the ramp.
   border-color goes transparent so the ramp shows through the band rather than
   being covered by the state's flat border colour.

   The pattern repeats TWICE around the ring — 0% and 50% carry the same colour.
   Without that an edge and its opposite sit 180deg apart in the ramp: on a wide,
   short panel the top edge shows the light stop while the bottom shows the dark
   one, and the darker line reads as a thicker border. Two cycles make every edge
   match its opposite, in colour and in weight, at any moment; the duration is
   doubled so a highlight still reaches an edge every 9s / 6.4s / 4.4s.

   @property is what makes the conic angle animatable; where it is not
   supported the border is simply a static gradient, never a missing edge. */
@property --eg-cd-angle {
    syntax: "<angle>";
    inherits: false;
    initial-value: 0deg;
}

/* Stop 2 of the ramp is the state's own border colour; stops 1 and 3 are its
   light and dark steps, so the travel stays inside the state's tone. The tint the
   ramp used to hide is now the state's own background-colour (the rules above). */
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="open"],
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="today"],
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="soon"] {
    --cd-ring-a: var(--eg-primary-a);
    --cd-ring-b: var(--eg-accent-300);
    --cd-ring-c: var(--eg-primary-b);
    --cd-ring-dur: 9s;
    border-color: transparent;
    background-image:
        conic-gradient(from var(--eg-cd-angle),
            var(--cd-ring-a) 0%,
            var(--cd-ring-b) 12.5%,
            var(--cd-ring-c) 25%,
            var(--cd-ring-b) 37.5%,
            var(--cd-ring-a) 50%,
            var(--cd-ring-b) 62.5%,
            var(--cd-ring-c) 75%,
            var(--cd-ring-b) 87.5%,
            var(--cd-ring-a) 100%);
    background-clip: border-area, padding-box;
    background-origin: border-box, padding-box;
    animation: eg-cd-border-flow var(--cd-ring-dur) linear infinite;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="today"] {
    --cd-ring-a: #fde68a;
    --cd-ring-b: #f59e0b;
    --cd-ring-c: #d97706;
    --cd-ring-dur: 6.4s;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="soon"] {
    --cd-ring-a: #fecaca;
    --cd-ring-b: #ef4444;
    --cd-ring-c: #b91c1c;
    --cd-ring-dur: 4.4s;
}

/* Firefox and older Safari cannot clip a background to the border area, and an
   unsupported value drops the whole declaration — the `background-clip` above would
   fall back to border-box and the ramp would flood the panel instead of ringing it.
   Those engines keep the state's solid tint and its real border, and simply lose the
   moving edge: a flat panel, never a broken one. */
@supports not (background-clip: border-area) {
    :is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="open"] {
        background-image: none;
        background-color: var(--surface-100);
        border-color: var(--eg-border);
        animation: none;
    }

    :is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="today"] {
        background-image: none;
        background-color: #fffbeb;
        border-color: #f59e0b;
        animation: none;
    }

    :is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="soon"] {
        background-image: none;
        background-color: #fee2e2;
        border-color: #ef4444;
        animation: none;
    }
}

@keyframes eg-cd-border-flow {
    to {
        --eg-cd-angle: 360deg;
    }
}

@media (prefers-reduced-motion: reduce) {
    :is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="open"],
    :is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="today"],
    :is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="soon"] {
        animation: none;
    }
}

/* Not opened yet: an information tone, not a warning. Nothing about an auction
   that has not started is urgent, so amber and red stay reserved for an auction
   that is running out of time. The fill is that tone at 21% over the panel's blur
   (the blur itself is on the shared rule above, so no state repeats it). */
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="pending"],
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="starting"] {
    border-color: #93c5fd;
    background: #eff6ff36;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="pending"] .pc-cd-line,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="starting"] .pc-cd-line {
    color: #1e40af;
}

:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="pending"] .cd-soft,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="pending"] .pc-cd-sub,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="pending"] .pc-cd-when,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="starting"] .cd-soft,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="starting"] .pc-cd-sub,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="starting"] .pc-cd-when {
    color: #1d4ed8;
}

/* The time of a blue panel is the same blue, one step deeper. */
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="pending"] .pc-cd-sub .pc-cd-sub-time,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="pending"] .pc-cd-sub .pc-cd-sub-start-time,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="starting"] .pc-cd-sub .pc-cd-sub-time,
:is(.pigeon-img-panel ~ .p-3, .auction-kpi-tile, .lot-cd) .countdown-bg-wrapper[data-cd-state="starting"] .pc-cd-sub .pc-cd-sub-start-time {
    color: #1e3a8a;
}

/* ---------------------------------------------------------------------------
   Toast — Elementa's .app-toast, ported
   ---------------------------------------------------------------------------
   The host is inert markup, pinned bottom-centre above everything; the element
   is built by the JS (a toast is never hand-written). Values are the library's,
   with the tokens it names mapped onto the ones this sheet already carries:
   --border -> --eg-border, --glass-card-bg -> --eg-glass-top, --text -> --eg-ink,
   --shadow-sm -> --eg-shadow. */
.app-toast-host {
    position: fixed;
    bottom: 1.5rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: var(--z-top);
    display: grid;
    gap: 8px;
    justify-items: center;
    pointer-events: none;
}

.app-toast {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 16px;
    border: 1px solid var(--eg-border);
    border-radius: var(--radius);
    background: var(--eg-glass-top);
    color: var(--eg-ink);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    box-shadow: var(--eg-shadow);
    white-space: nowrap;
    font-size: 14px;
    opacity: 1;
    transition: opacity 0.3s;
    pointer-events: auto;
}

.app-toast.is-hiding {
    opacity: 0;
}

.app-toast .material-symbols-outlined {
    font-size: 18px;
}

.app-toast.success .material-symbols-outlined {
    color: #16a34a;
}

.app-toast.error .material-symbols-outlined {
    color: #dc2626;
}

.app-toast.warning .material-symbols-outlined {
    color: #d97706;
}

.app-toast.info .material-symbols-outlined {
    color: var(--accent-600);
}

/* --- Notification list (member zone) -------------------------------------- */
.notify-list {
    display: grid;
    gap: 6px;
}

.notify-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 12px;
    border: 1px solid var(--eg-border);
    border-radius: var(--radius);
    background: #fff;
    color: var(--eg-ink);
    text-decoration: none;
}

a.notify-row:hover {
    background: var(--hover-bg);
    border-color: #94a3b8;
}

/* The tone belongs to the news: amber for "someone went above you", green for
   "it is yours". A read notice drops the colour altogether. */
.notify-row .notify-ico {
    font-size: 20px;
    line-height: 1.2;
    flex: none;
    color: #94a3b8;
}

.notify-row[data-notify-type="outbid"] .notify-ico {
    color: #d97706;
}

.notify-row[data-notify-type="winner"] .notify-ico {
    color: #16a34a;
}

.notify-row .notify-body {
    display: grid;
    gap: 2px;
    min-width: 0;
    flex: 1 1 auto;
}

.notify-title {
    font-size: 14px;
    font-weight: 600;
}

.notify-msg {
    font-size: 13px;
    color: #64748b;
    overflow-wrap: anywhere;
}

.notify-time {
    flex: none;
    font-size: 12px;
    color: #94a3b8;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* An unread notice carries a dot at the end of its timestamp: the timestamp is
   the last thing in the row, so an ellipsised message cannot swallow the mark,
   and the dot goes the moment the row is read. */
.notify-row:not(.is-read) .notify-time::after {
    content: "";
    display: inline-block;
    width: 7px;
    height: 7px;
    margin-left: 6px;
    border-radius: 9999px;
    background: var(--accent-600);
    vertical-align: middle;
}

/* Read notices step back so the unread ones are what the eye lands on. The icon
   rule has to come after the [data-notify-type] pair — same specificity, so
   order is what decides it. */
.notify-row.is-read {
    background: #fbfcfe;
    border-color: #e2e8f0;
}

.notify-row.is-read .notify-ico {
    color: #94a3b8;
}

.notify-row.is-read .notify-title {
    font-weight: 500;
    color: #475569;
}

.notify-row.is-read .notify-msg {
    color: #94a3b8;
}

/* The bell's panel: five notices and one way out. Wider than the avatar's menu
   because each row carries a title and a line of message. */
#mainNav .notify-menu {
    min-width: 340px;
    max-height: 420px;
    overflow-y: auto;
}

#mainNav .notify-menu .notify-list {
    gap: 4px;
}

/* In the panel the rows behave as menu items: no card edges until the pointer
   is on them. */
#mainNav .notify-menu .notify-row {
    padding: 8px 10px;
    background: transparent;
    border-color: transparent;
}

#mainNav .notify-menu .notify-row:hover {
    background: var(--hover-bg);
    border-color: var(--eg-border);
}

#mainNav .notify-menu .notify-title {
    font-size: 13px;
}

#mainNav .notify-menu .notify-time {
    font-size: 11px;
}

/* One line in the panel — the page is where the full text lives. */
#mainNav .notify-menu .notify-msg {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The last row of the panel, behind the sheet's own hairline. */
.notify-showall {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    margin-top: 6px;
    padding: 9px 10px;
    border-top: 1px solid var(--eg-border);
    color: #385378;
    font-family: "Noto Sans Thai", "Open Sans", Arial, sans-serif;
    font-size: 13px;
    font-weight: 500;
    text-decoration: none;
}

.notify-showall .material-symbols-outlined {
    font-size: 17px;
}

.notify-showall:hover {
    background: var(--hover-bg);
    color: #22324a;
}

.notify-empty {
    padding: 14px 10px;
    color: #64748b;
    font-size: 13px;
    text-align: center;
}

/* --- Lot line: one line, full text in a popup ------------------------------
   The line was reserving a fixed 80px so cards with a long description stayed
   in step; at 13px that left most of the reserve empty. It is one line with an
   ellipsis now, and the full text is one hover (or one tap) away.

   The clip sits on .pc-lot-text, NOT on .pc-lot, because the popup is a child
   of .pc-lot: a clip there would cut the popup off too. */
.pigeon-img-panel ~ .p-3 .pc-lot {
    position: relative;
    /* Same shape as the Fancier / Country rows: icon, then the value — and the
       same colour, so the three rows of the box read as one group instead of
       the LOT line sitting darker than the two under it. */
    display: flex;
    align-items: center;
    gap: 4px;
    color: #61708a;
    line-height: 1.35;
    cursor: help;
}

/* flex:1 + min-width:0 is what lets the text shrink and ellipsis inside a flex
   row; a flex item's default min-width:auto would push the row wider instead of
   clipping it, and the ellipsis would never appear. */
.pigeon-img-panel ~ .p-3 .pc-lot-text {
    flex: 1 1 auto;
    min-width: 0;
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pigeon-img-panel ~ .p-3 .pc-lot > .material-symbols-outlined {
    font-size: 15px;
    line-height: 1;
    flex: none;
}

/* The icon sat ~1.5px LOW beside its text in this card - and it was not the icon's fault.
   These rows centre the icon's 1em box, and the icon font puts its ink dead centre in that box;
   Noto Sans Thai does not. The face's descent is large (Thai needs room for the marks that hang
   below the baseline), so a line of capitals ends up about **0.14em above the centre of its own
   line box**, whatever that line box's height is.

   Measured, per glyph, with a zero-height inline-block appended to each run (an inline-block's
   baseline is its bottom edge, so its bottom IS the run's baseline, and 0 height leaves the line
   box alone): the letters' ink is centred 1.75px above the row's centre, while the icons' ink is
   centred on it - the `category` icon needs 1.77px, `person` 1.25px, so the glyphs differ by
   half a pixel among themselves. 1.5px is the value that leaves all of them within a quarter of
   a pixel of the letters, which is as close as icon glyphs can be brought to a line of text.

   NOT applied to `.lot-info` (the lot page): those rows align their items to the TOP by their
   own rule in custom-v2.css, so their geometry is a different one and their icons - unchanged,
   as the member sees them - are not part of this measurement. */
.pigeon-img-panel ~ .p-3 .pc-lot > .material-symbols-outlined,
.pigeon-img-panel ~ .p-3 .f15 .material-symbols-outlined {
    transform: translateY(-1.5px);
}

/* A lot line that fits is not clipped and has no popup, so it must not promise
   one: _Layout marks it .no-pop and the help cursor goes with the bubble. */
.pigeon-img-panel ~ .p-3 .pc-lot.no-pop {
    cursor: default;
}

.pigeon-img-panel ~ .p-3 .pc-lot-pop {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(100% + 6px);
    z-index: 3;
    padding: 8px 10px;
    border: 1px solid var(--eg-border);
    border-radius: var(--radius);
    background: #ffffff;
    box-shadow: var(--eg-pop-shadow);
    color: var(--eg-ink);
    font-size: 13px;
    line-height: 1.45;
    white-space: normal;
    opacity: 0;
    visibility: hidden;
    transition: opacity 160ms ease, visibility 160ms ease;
}

/* A precise pointer gets hover (and keyboard focus). A touch screen has no
   hover, so the click handler in _Layout toggles .is-open instead. */
@media (hover: hover) and (pointer: fine) {
    .pigeon-img-panel ~ .p-3 .pc-lot:hover .pc-lot-pop,
    .pigeon-img-panel ~ .p-3 .pc-lot:focus-within .pc-lot-pop {
        opacity: 1;
        visibility: visible;
    }
}

.pigeon-img-panel ~ .p-3 .pc-lot.is-open .pc-lot-pop {
    opacity: 1;
    visibility: visible;
}

/* The arrow rides the button's middle instead of the text baseline: as a float it
   sat 4px above it. Absolute positioning leaves the centred VIEW label alone, and
   absolute also cancels Bootstrap's `float: right !important` on .float-end. */
.pigeon-img-panel ~ .p-3 .btn.btn-gradient-primary {
    position: relative;
}

.pigeon-img-panel ~ .p-3 .btn .material-symbols-outlined {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 14px;
}

/* --- Max-bid chip ----------------------------------------------------------
   Shown only while the member's own ceiling is still the leading bid: it answers
   "how high am I exposed on this lot" without leaving the card. Hover or focus
   opens the value, and a tap toggles .is-open for touch (both wired in _Layout).
   The card clips its own overflow, so the popup opens upward inside it. */
.pigeon-img-panel ~ .p-3 .pc-maxbid {
    position: relative;
    display: inline-flex;
    align-items: center;
    margin-left: 4px;
    vertical-align: middle;
    color: var(--accent-600);
    cursor: help;
}

.pigeon-img-panel ~ .p-3 .pc-maxbid .material-symbols-outlined {
    position: static;
    transform: none;
    font-size: 17px;
    line-height: 1;
}

.pigeon-img-panel ~ .p-3 .pc-maxbid-pop {
    position: absolute;
    left: 0;
    bottom: calc(100% + 6px);
    z-index: 3;
    padding: 6px 9px;
    border: 1px solid var(--eg-border);
    border-radius: var(--radius);
    background: #ffffff;
    box-shadow: var(--eg-pop-shadow);
    color: var(--eg-ink);
    font-family: "Noto Sans Thai", "Open Sans", Arial, sans-serif;
    font-size: 12px;
    font-weight: 400;
    line-height: 1.45;
    white-space: nowrap;
    opacity: 0;
    visibility: hidden;
    transition: opacity 160ms ease, visibility 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
    .pigeon-img-panel ~ .p-3 .pc-maxbid:hover .pc-maxbid-pop,
    .pigeon-img-panel ~ .p-3 .pc-maxbid:focus-within .pc-maxbid-pop {
        opacity: 1;
        visibility: visible;
    }
}

.pigeon-img-panel ~ .p-3 .pc-maxbid.is-open .pc-maxbid-pop {
    opacity: 1;
    visibility: visible;
}
