/*
 * Geist-flavoured layer over bootstrapcn.
 *
 * The brief is Vercel's dashboard, so the conventions here are theirs: Geist at
 * 14px text on an untouched 16px root, borders instead of shadows, one radius
 * everywhere, and near-black as the accent rather than a brand blue.
 *
 * Colour and type values were read out of vercel.com's own stylesheet rather
 * than recalled, after the first pass got the page/surface relationship
 * backwards and rescaled the root. Both are noted where they bit.
 *
 * Everything is scoped tightly enough to win against the theme without using
 * !important. Where a token exists, the token is overridden rather than the
 * rule that consumes it.
 */

/* ---------------------------------------------------------------- type ---- */

/* Geist, pinned. Three weights is the whole UI: 400 body, 500 headers and
   buttons, 600 page titles. swap so text paints immediately in the fallback. */
@font-face {
    font-family: "Geist";
    src: url("https://cdn.jsdelivr.net/npm/geist@1.7.2/dist/fonts/geist-sans/Geist-Regular.woff2") format("woff2");
    font-weight: 400;
    font-display: swap;
}
@font-face {
    font-family: "Geist";
    src: url("https://cdn.jsdelivr.net/npm/geist@1.7.2/dist/fonts/geist-sans/Geist-Medium.woff2") format("woff2");
    font-weight: 500;
    font-display: swap;
}
@font-face {
    font-family: "Geist";
    src: url("https://cdn.jsdelivr.net/npm/geist@1.7.2/dist/fonts/geist-sans/Geist-SemiBold.woff2") format("woff2");
    font-weight: 600;
    font-display: swap;
}

/* ------------------------------------------------------------- palette ---- */

/*
 * Corrected against a screenshot of the actual dashboard, which is the only
 * evidence that settles it. The page is GREY and the cards are WHITE -- the
 * ordinary elevation pattern, not the white-on-white this file previously
 * claimed. Two earlier passes got this relationship wrong in opposite
 * directions; reasoning about token names rather than looking is what produced
 * both, so the values below are only what the screenshot shows.
 *
 *   #fafafa   the page, and the sidebar with it
 *   #ffffff   cards, sitting on the page and outlined
 *   #eaeaea   every border -- the outline is what defines a card, not a shadow
 *   #4d4d4d   secondary text   8.45:1 on white
 *   #171717   primary text    17.93:1 on white
 *
 * Because page and card now differ, `--background` and `--card` are no longer
 * interchangeable. Anything that reads as a raised surface -- a card, an input,
 * a dropdown -- takes `--card`; only the page itself takes `--background`.
 *
 * Dark mode carries the same relationship one step down: #000 page, #0a0a0a
 * card. That half is reasoned from the light values by symmetry, not observed
 * -- I have no screenshot of their dark dashboard.
 *
 * Scoped to :root:not([data-bs-theme="dark"]) rather than :root -- the theme
 * sets light on :root and dark on [data-bs-theme=dark] at equal specificity,
 * resolved by source order, and this file loads after it, so a bare :root rule
 * would win against the dark block and break dark mode.
 */
:root:not([data-bs-theme="dark"]) {
    --background: 0 0% 98%;       /* #fafafa  the page */
    --card: 0 0% 100%;            /* #ffffff  raised on it */
    --foreground: 0 0% 9%;        /* #171717 */
    --muted: 0 0% 98%;            /* #fafafa  recessed inside a card: footers, table heads */
    --muted-foreground: 0 0% 30%; /* #4d4d4d */
    --border: 0 0% 92%;           /* #eaeaea */
    --input: 0 0% 92%;
    --primary: 0 0% 9%;           /* near-black buttons, not a brand blue */
    --radius: 0.5rem;

    /* The theme ships the sidebar white, which read as a card once the page
       went grey. In the screenshot it is the page colour: the sidebar is part
       of the ground, and the cards are the only raised things on screen. */
    --sidebar: 0 0% 98%;          /* #fafafa, with the page */
    --sidebar-accent: 0 0% 92%;   /* #ebebeb, the active nav row */
    --sidebar-border: 0 0% 92%;   /* #eaeaea, with every other border */
}

:root[data-bs-theme="dark"] {
    --background: 0 0% 0%;        /* #000000  the page */
    --card: 0 0% 4%;              /* #0a0a0a  raised on it */
    --foreground: 0 0% 93%;       /* #ededed */
    --muted: 0 0% 10%;            /* #1a1a1a */
    --muted-foreground: 0 0% 63%; /* #a0a0a0  8.03:1 on black */
    --border: 0 0% 18%;           /* #2e2e2e */
    --input: 0 0% 18%;

    --sidebar: 0 0% 0%;           /* with the page */
    --sidebar-accent: 0 0% 12%;
    --sidebar-border: 0 0% 18%;
}

/*
 * The page paints itself, rather than inheriting whatever the theme set. With
 * page and card no longer the same colour this is load-bearing: a card only
 * reads as raised if the ground behind it is actually the darker of the two.
 */
body {
    background-color: hsl(var(--background));
}

.card {
    background-color: hsl(var(--card));
}

/*
 * Controls are raised surfaces too. shadcn's palette has --background and
 * --card identical in its default light theme, so everything downstream reads
 * --background for an input and never notices. Here they differ, and an input
 * left on --background would sit grey-on-white inside every card.
 */
.form-control,
.form-select,
.ts-dropdown,
.ts-wrapper.form-select,
.ts-wrapper.form-control {
    background-color: hsl(var(--card));
}

/*
 * The root stays at the browser default. Vercel's own reset is
 * `body { font-size: 100% }` -- they never rescale the root, and setting
 * `html { font-size: 14px }` (which the first pass did) shrinks far more than
 * text: every padding, margin, gap and radius expressed in rem comes down by
 * the same 12.5%, so the generous spacing that makes their dashboard readable
 * quietly collapses along with the type.
 *
 * Text is 14px/20px, which is their dense-UI size; the rem scale underneath it
 * stays 16px-based, so a 1.25rem card padding is a real 20px.
 *
 * Their ladder, from their stylesheet: 40 / 36 / 32 / 24 for headings,
 * 16 base, 14 UI, 13 and 12 for labels and meta.
 */
body {
    font-family: "Geist", ui-sans-serif, system-ui, -apple-system,
        "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    font-size: 0.875rem;         /* 14px */
    line-height: 1.4286;         /* 20px, the pairing they use throughout */
    font-feature-settings: "cv11", "ss01";
    -webkit-font-smoothing: antialiased;
    letter-spacing: -0.011em;    /* Geist is set slightly tight in Vercel's UI */
}

h1, h2, h3, h4, h5, h6 {
    letter-spacing: -0.021em;    /* tightens further as size grows */
    font-weight: 600;
}

/* ------------------------------------------------------------- surfaces --- */

.card {
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius);
    box-shadow: none;            /* Geist separates with borders, never shadow */
}

.card + .card,
.card + .sf-empty,
.sf-empty + .card {
    margin-top: 1.5rem;
}

/* Header: the region's name, and the figure it is about, on one line. */
.card > .card-header {
    display: flex;
    align-items: center;
    gap: 1rem;
    min-height: 3.25rem;
    padding: 0.75rem 1.25rem;
    background-color: transparent;
    border-bottom: 1px solid hsl(var(--border));
    font-weight: 500;
    letter-spacing: -0.011em;
}

.card > .card-body {
    padding: 1.25rem;
}

/*
 * The footer bar. Vercel's most recognisable element: a muted strip carrying
 * the hint that explains the data above, and any secondary action. Marked up
 * as the last .card-body in a card, so no template needs a new class.
 */
.card > .card-body:last-child.sf-note,
.card > .sf-note {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.75rem 1.25rem;
    background-color: hsl(var(--muted));
    border-top: 1px solid hsl(var(--border));
    color: hsl(var(--muted-foreground));
    font-size: 0.8125rem;        /* 13px -- their label size */
    border-radius: 0 0 calc(var(--radius) - 1px) calc(var(--radius) - 1px);
}

.card > .sf-note > :last-child {
    margin-left: auto;
}

/* --------------------------------------------------------------- tables --- */

/* Room to breathe. Bootstrap's default cell padding is half this, and the
   difference between a scannable table and a dense one is mostly here. */
.card .table {
    --bs-table-bg: transparent;
    margin-bottom: 0;
}

.card .table > thead > tr > th {
    padding: 0.625rem 1.25rem;
    font-size: 0.8125rem;
    font-weight: 500;
    color: hsl(var(--muted-foreground));
    background-color: transparent;
    border-bottom: 1px solid hsl(var(--border));
}

.card .table > tbody > tr > td {
    padding: 0.75rem 1.25rem;
    border-bottom-color: hsl(var(--border));
    vertical-align: middle;
}

.card .table > tbody > tr:last-child > td {
    border-bottom: 0;
}

/* Figures line up digit for digit, so an order-of-magnitude error is visible
   at a glance rather than needing to be read. */
.table td.text-end,
.table th.text-end,
.sf-detail dd,
.sf-figure {
    font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------- details lists ---- */

/*
 * Label/value rows: the shape Vercel uses for an object's properties. Labels
 * are muted and narrow, values carry the weight.
 */
.sf-detail {
    display: grid;
    grid-template-columns: minmax(8rem, 14rem) 1fr;
    gap: 0.75rem 1.5rem;
    margin: 0;
}

.sf-detail dt {
    font-weight: 400;
    color: hsl(var(--muted-foreground));
}

.sf-detail dd {
    margin: 0;
    color: hsl(var(--foreground));
}

/* A single prominent number, for a card that exists to show one. */
.sf-figure {
    font-size: 1.75rem;
    font-weight: 600;
    letter-spacing: -0.021em;
    line-height: 1.2;
}

/* --------------------------------------------------------------- chrome --- */

.btn {
    --bs-btn-padding-y: 0.4375rem;
    --bs-btn-padding-x: 0.75rem;
    --bs-btn-font-size: 0.875rem;
    --bs-btn-font-weight: 500;
    --bs-btn-border-radius: calc(var(--radius) - 0.125rem);
}

.form-control, .form-select {
    border-color: hsl(var(--border));
    border-radius: calc(var(--radius) - 0.125rem);
    font-size: 0.875rem;
}

.badge {
    font-weight: 500;
    letter-spacing: 0;
}

/* Vercel's dashboard is a wide, airy measure: content gets room at the top and
   a generous run-out at the bottom so the last card is never flush to the fold. */
.bootcn-main > main.container,
main.container {
    max-width: 1200px;
    padding-top: 2rem;
    padding-bottom: 4rem;
}

/* Page heading block: title, one line of context, and the primary action. */
.sf-page-head {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.sf-page-head h1 {
    font-size: 1.5rem;           /* 24px */
    line-height: 1.25;
    margin: 0;
}

.sf-page-head p {
    margin: 0.25rem 0 0;
    color: hsl(var(--muted-foreground));
}

.sf-page-head .sf-page-actions {
    margin-left: auto;
    display: flex;
    gap: 0.5rem;
    flex: none;
}

/* Show the icon for the theme you would switch TO; hide the other. */
[data-bs-theme="light"] .sf-theme-light { display: none; }
[data-bs-theme="dark"] .sf-theme-dark { display: none; }

/* An empty screen is an invitation to act, so it gets the action in it. */
.sf-empty {
    border: 1px dashed hsl(var(--border));
    border-radius: var(--radius);
    padding: 3rem 2rem;
    text-align: center;
    color: hsl(var(--muted-foreground));
}

.sf-empty p {
    margin-bottom: 1rem;
}

/* ------------------------------------------------------- searchable select - */

/*
 * Tom Select, bound to the same tokens as everything else.
 *
 * Read off the library's own stylesheet rather than guessed. Two things in it
 * matter, and only one is obvious:
 *
 *   .ts-control, .ts-control input, .ts-dropdown { color: #343a40 }
 *
 * is hardcoded, while the dropdown's background is `var(--bs-body-bg)` and so
 * follows the theme. In dark mode that pairs a dark ground with dark grey text
 * and the options vanish. The selector below matches theirs exactly so it wins
 * on source order -- which only works because the vendor sheet is loaded above
 * app.css; see the note in layout.twig.
 *
 * The second: Tom Select copies the original <select>'s classes onto its
 * wrapper, so .ts-wrapper carries .form-select and already has this app's
 * border, radius and caret. The inner .ts-control is forced transparent and
 * borderless with !important, so styling it again would fight for nothing.
 * Only the text and the dropdown need saying.
 */
.ts-control,
.ts-control input,
.ts-dropdown {
    color: hsl(var(--foreground));
}

.ts-control input::placeholder {
    color: hsl(var(--muted-foreground));
}

/* Bootstrap's blue focus glow, on a palette whose accent is near-black. The
   wrapper carries the ring instead, matching every other control. */
.ts-wrapper.focus .ts-control {
    box-shadow: none;
}

.ts-wrapper.form-select.focus,
.ts-wrapper.form-control.focus {
    border-color: hsl(var(--foreground));
}

.ts-dropdown {
    /* --card, not --background: a dropdown floats above the page. */
    background-color: hsl(var(--card));
    border: 1px solid hsl(var(--border));
    border-radius: calc(var(--radius) - 0.125rem);
    font-size: 0.875rem;
    /* The one shadow in the app. A dropdown floats over content, so a border
       alone does not separate it the way it does for a card sitting in flow. */
    box-shadow: 0 4px 12px rgb(0 0 0 / 0.08);
}

.ts-dropdown .option {
    padding: 0.5rem 0.75rem;
    color: hsl(var(--foreground));
}

.ts-dropdown .active,
.ts-dropdown .active.create {
    background-color: hsl(var(--muted));
    color: hsl(var(--foreground));
}

.ts-dropdown .no-results,
.ts-dropdown .optgroup-header {
    padding: 0.5rem 0.75rem;
    background: transparent;
    color: hsl(var(--muted-foreground));
}

/* The match highlight ships as a fixed translucent yellow, which is invisible
   on a dark ground and loud on a light one. */
.ts-dropdown [data-selectable] .highlight {
    background: hsl(var(--muted-foreground) / 0.28);
    border-radius: 2px;
    color: inherit;
}

/* A quantity sitting inline in the stock sidebar: tabular so the column of
   figures lines up, but body-sized, unlike .sf-figure. */
.sf-figure-inline {
    font-variant-numeric: tabular-nums;
    font-size: 0.8125rem;
}

/* The kind glyph beside a SKU. Muted and body-sized: it is a mark on a row,
   not a control, and it repeats on every line -- at full icon size it would
   read as a column of buttons. */
.sf-kind-icon {
    display: inline-flex;
    vertical-align: -0.15em;
    margin-right: 0.375rem;
}

.sf-kind-icon .bootcn-icon {
    width: 1rem;
    height: 1rem;
}

/* -------------------------------------------------------------- buttons --- */

/*
 * The theme leaves Bootstrap's outline buttons alone, so they kept the
 * defaults: transparent background, #6c757d text and border. Transparent is
 * the problem. A card's footer bar is --muted, and a transparent button on it
 * reads as a faint rectangle rather than a control -- the complaint that
 * prompted this was that the button looked like the strip it sat on.
 *
 * Secondary becomes a solid chip instead: --card ground, the app's border, the
 * app's text. That is the pairing the reference dashboard uses, and it reads on
 * a white card and a muted footer alike rather than depending on which surface
 * it lands on.
 */
.btn-outline-secondary {
    --bs-btn-color: hsl(var(--foreground));
    --bs-btn-bg: hsl(var(--card));
    /* A touch darker than a card's border. The card border only has to
       separate two near-identical surfaces; a button has to look pressable
       on both of them, and --border at 1.15:1 against the footer strip did
       not. */
    --bs-btn-border-color: hsl(var(--muted-foreground) / 0.45);
    --bs-btn-hover-color: hsl(var(--foreground));
    --bs-btn-hover-bg: hsl(var(--border));
    --bs-btn-hover-border-color: hsl(var(--muted-foreground) / 0.6);
    --bs-btn-active-color: hsl(var(--foreground));
    --bs-btn-active-bg: hsl(var(--border));
    --bs-btn-active-border-color: hsl(var(--muted-foreground) / 0.6);
    --bs-btn-disabled-color: hsl(var(--muted-foreground));
    --bs-btn-disabled-bg: hsl(var(--card));
    --bs-btn-disabled-border-color: hsl(var(--border));
}

/* Destructive stays outlined and keeps its own colour -- Delete should not
   read as the thing to click. Only the transparency is fixed, so it is a chip
   on the footer rather than a shape the footer shows through. */
.btn-outline-danger {
    --bs-btn-bg: hsl(var(--card));
}

/*
 * An icon inside a button.
 *
 * The button becomes a flex row rather than the icon being nudged onto the text
 * baseline. A 24-unit glyph is a square that fills its em box, and a baseline is
 * positioned for lowercase letters, so `vertical-align` can only ever be a guess
 * that is wrong at some size -- which is what the first pass did. Centring the
 * two against each other is exact at every size and needs no magic number.
 *
 * `gap` rather than a margin, so an icon-only button needs no exception: gap
 * only applies between rendered children, and the theme toggle's hidden glyph
 * is `display: none` and therefore not one.
 *
 * Sized in `em`, so btn-sm and the default need no separate rule. 0.9em rather
 * than a full em: Lucide draws edge to edge at stroke-width 2, so a glyph the
 * full height of the em box reads heavier than the lowercase text beside it.
 */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5em;
}

.btn > .bootcn-icon {
    width: 0.9em;
    height: 0.9em;
    /* Never let a long label squeeze the glyph out of square. */
    flex: 0 0 auto;
}

/* d-grid and w-100 buttons stay full width; inline-flex alone would shrink
   them to their content. */
.d-grid > .btn,
.btn.w-100 {
    display: flex;
}
