/* =============================================================
 * Knowyn Components (supplier portal)
 * Page-level components ported from the estate's custom.css, kept
 * to the small set the portal actually uses. Token-based; pairs
 * with design-tokens.css + knowyn-shell.css. Authored fresh (not a
 * re-import of the 3.5k-line custom.css) so it stays scoped/clean.
 * ============================================================= */

/* Self-hosted fonts matching knowyn-web: Fira Sans (body UI font, already first in the
 * --font-family-sans stack) + Raleway (brand wordmark). */
@font-face {
    font-family: 'Fira Sans';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/fira-sans-400.woff2') format('woff2');
}
@font-face {
    font-family: 'Fira Sans';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('../fonts/fira-sans-500.woff2') format('woff2');
}
@font-face {
    font-family: 'Fira Sans';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('../fonts/fira-sans-600.woff2') format('woff2');
}
@font-face {
    font-family: 'Raleway';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/raleway-400.woff2') format('woff2');
}
.top-bar-brand .brand-text {
    font-family: 'Raleway', var(--font-family-sans);
    font-weight: 400;
}
/* App-name mark next to the wordmark (top bar), mirroring the login "Procure" mark. */
/* App-name mark next to the wordmark (top bar), mirroring the login "Procure" mark.
   Typography comes from the shared label voice (knowyn-shell.css). */
.brand-mark {
    margin-left: var(--space-2);
    font-family: var(--font-family-sans);
    color: rgba(255, 255, 255, 0.7);
    vertical-align: middle;
}

/* Skip-to-content link — hidden (Bootstrap .visually-hidden-focusable) until keyboard-focused. */
.skip-link:focus {
    position: fixed;
    top: var(--space-2);
    left: var(--space-2);
    z-index: 1080;
    padding: var(--space-2) var(--space-3);
    background: var(--color-primary-600);
    color: var(--text-inverse);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-2);
}

/* Title bar for list/form/detail pages. The title sits directly on the canvas (no card box),
 * closed off by a hairline that gives the page its first ledger rule. */
.page-title-bar--soft {
    background: transparent;
    border: 0;
    border-bottom: 1px solid var(--border-default);
    padding: 0 0 var(--space-4);
    border-radius: 0;
    margin-bottom: var(--space-5);
    box-shadow: none;
}
.page-title-bar--soft .page-title {
    color: var(--text-primary);
}

/* Tinted page sections — break up the white expanse. */
.page-section--soft,
.page-section--neutral {
    padding: var(--space-5);
    border-radius: var(--radius-lg);
    margin-bottom: var(--space-5);
}
.page-section--soft { background: var(--color-primary-50); }
.page-section--neutral { background: var(--color-neutral-100); }

/* Segmented control: a proper segmented switch (recessed track, active segment lifted to white),
 * replacing outline-button groups. Standalone, deliberately NOT a Bootstrap .btn-group (whose
 * :not-chain corner/margin rules would have to be out-fought on every Bootstrap bump); the active
 * segment carries `.active` and the container keeps role="group" for assistive tech. */
.segmented-control {
    display: inline-flex;
    align-items: stretch;
    gap: 2px;
    padding: 3px;
    background: var(--color-neutral-100);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
}
.segmented-control .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    min-height: 28px;
    padding-block: 0;
    padding-inline: var(--space-3);
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-secondary);
    font-size: var(--font-sm);
    font-weight: var(--weight-medium);
    box-shadow: none;
}
.segmented-control .btn:hover {
    color: var(--text-primary);
    background: transparent;
}
.segmented-control .btn.active {
    background: var(--surface-card);
    color: var(--text-primary);
    box-shadow: var(--shadow-1);
}
/* Dark: the card surface sits below the recessed track tone, so lift the active segment instead. */
:root[data-theme="dark"] .segmented-control .btn.active {
    background: var(--surface-hover);
}

/* Metrics band: the KPI tiles form ONE bordered strip divided by internal hairlines (a ledger
 * row, not four floating boxes). The quilt trick (every cell draws top+left hairlines, shifted
 * -1px; the wrapper clips the outer edge) keeps the dividers correct at any wrap width. */
.dashboard-stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    background: var(--surface-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-1);
    overflow: hidden;
}
.stat-card {
    position: relative;
    display: block;
    background: var(--surface-card);
    border: 0;
    border-top: 1px solid var(--border-subtle);
    border-left: 1px solid var(--border-subtle);
    margin: -1px 0 0 -1px;
    border-radius: 0;
    box-shadow: none;
    text-decoration: none;
    color: inherit;
}
/* Color-code by tinting the header icon (cleaner than the old 4px left rail). */
.stat-card--primary .stat-card__header .app-icon { color: var(--color-primary-500); }
.stat-card--success .stat-card__header .app-icon { color: var(--color-success-500); }
.stat-card--warning .stat-card__header .app-icon { color: var(--color-warning-500); }
.stat-card--info    .stat-card__header .app-icon { color: var(--color-info-500); }
.stat-card__body { padding: var(--space-4); }
.stat-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}
/* Typography comes from the shared label voice (knowyn-shell.css). */
.stat-card__label {
    margin: 0;
    color: var(--text-secondary);
}
.stat-card__value {
    margin: var(--space-2) 0 0;
    font-size: var(--font-2xl);
    font-weight: var(--weight-semibold);
    line-height: 1;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;   /* aligned digits as the drilled metric re-renders */
}
/* A name (not a number) as the headline: smaller and clamped to two lines so long article/customer
   names never blow out the tile. */
.stat-card__value--text {
    font-size: var(--font-lg);
    line-height: 1.25;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
/* Clickable cells (those carrying a stretched-link) tint on hover for affordance; cells in a band
   don't lift. */
.stat-card:has(.stretched-link) { transition: background-color 0.15s ease; }
.stat-card:has(.stretched-link):hover { background: var(--surface-sunken); }

/* Drill tiles (data-trend-metric): click to re-plot the trend below. */
.stat-card--drill { cursor: pointer; touch-action: manipulation; transition: background-color 0.15s ease; }
/* Comparison columns (the price-spread min/median/max) align on tabular figures. */
.price-spread td.text-end { font-variant-numeric: tabular-nums; }
.stat-card--drill:hover { background: var(--surface-sunken); }
.stat-card--drill:focus-visible { outline: 2px solid var(--color-primary-500); outline-offset: -2px; }
/* Selected drill tile: a 2px brand bar along the cell's top edge, "this one is plotted" said with
   a line rather than a wash. */
.stat-card--drill.active {
    box-shadow: inset 0 2px 0 var(--color-primary-500);
}
.stat-card--drill.active .stat-card__label {
    color: var(--color-primary-600);
}

/* Period-over-period delta line under a KPI value: a coloured arrow + percent, green up / red down /
   muted flat. Same grammar as the estate dashboards' d-pos/d-neg comparison text. */
.stat-card__delta {
    font-size: var(--font-sm);
    font-weight: var(--weight-medium);
}
.stat-card__delta--up   { color: var(--color-success-600); }
.stat-card__delta--down { color: var(--color-danger-500); }
.stat-card__delta--flat { color: var(--text-secondary); }

/* A denser tile grid for the per-customer / per-article mini-dashboards (more tiles, tighter columns). */
.dashboard-stats-grid--compact { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }

/* Chart canvas wrappers — a fixed height lets Chart.js (maintainAspectRatio:false) fill the card and
   stay responsive on resize. */
.chart-box { position: relative; height: 300px; }
.chart-box--tall { height: 380px; }

/* Base inline-icon sizing (inline SVG rendered by <knowyn-icon>). The sidebar menu icons are sized more
 * specifically by knowyn-shell.css; this covers icons elsewhere (theme menu label, login theme-cycle).
 * width/height track font-size (1em) so the existing font-size overrides keep controlling the size, and
 * fill: currentColor lets color rules (e.g. the stat-card tints) tint the glyph. */
.app-icon {
    font-size: 1.1em;
    width: 1em;
    height: 1em;
    line-height: 1;
    vertical-align: -0.15em;
    fill: currentColor;
}

/* Login — navy particles hero + white card form, matching knowyn-web's auth layout.
 * On desktop the hero's negative bottom margin lets the .auth-form card overlap it.
 * Particles.js background is self-hosted; the card uses tokens (themeable in dark mode). */
/* knowyn-web's auth-hero navy. Portal-local (the shared design-tokens.css mirrors the estate
 * token set 1:1, so this one brand value lives behind a name here rather than as a literal). */
:root { --color-auth-hero: #061552; }
.auth {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    min-height: 100vh;
    background-color: var(--surface-bg);
}
.auth-header {
    position: relative;
    overflow: hidden;
    width: 100%;
    background-color: var(--color-auth-hero);
    color: #fff;
    text-align: center;
    padding: 2rem 1.25rem 0;
}
.auth-header canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
}
.auth-header > div { position: relative; z-index: 1; }
.auth-header h1 {
    font-family: 'Raleway', var(--font-family-sans);
    font-weight: 400;
    font-size: clamp(2.5rem, 10vw, 3.5rem);
    margin: 0;
    position: relative;
    color: #fff;
}
.auth-header .auth-region {
    position: absolute;
    right: 0;
    bottom: -5px;
    font-size: 11px;
    color: #fff;
    opacity: 0.85;
}
/* Hero link ("Kontakta oss") — amber, matching knowyn-web's .auth-header a (#F7C46C). */
.auth-header a { color: var(--color-warning-400); }
.auth-header a:hover { color: var(--color-warning-300); }
/* "Har du inget konto? Kontakta oss" — 14px, with a 16px gap down to the form card (the card is
   pulled up under the hero on desktop, so the spacing lives on this prompt). */
.auth-subprompt { font-size: var(--font-sm); margin-bottom: 16px; }
.auth-form {
    position: relative;
    z-index: 1;
    width: 100%;
    padding: 2rem 1.25rem 0;
}
.auth-footer { padding: var(--space-4); color: var(--text-secondary); font-size: var(--font-sm); text-align: center; }
.auth-footer a { color: var(--color-primary-600); }

@media (min-width: 768px) {
    .auth-header {
        margin-bottom: -3rem;
        padding-top: 6rem;
        padding-bottom: 3rem;
    }
    .auth-form {
        max-width: 450px;
        padding: 3rem 2rem 1rem;
        border-radius: var(--radius-md);
        background-color: var(--surface-card);
        /* shadow-3 (not the estate's subtle shadow-1) so the card lifts off the bg more. */
        box-shadow: var(--shadow-3);
    }
    .auth-footer { max-width: 420px; }
}

/* Floating theme cycle button (bottom-right) on the login page. */
.auth-theme-cycle {
    position: fixed;
    right: var(--space-4);
    bottom: var(--space-4);
    z-index: var(--z-fixed);
    width: 44px;
    height: 44px;
    font-size: 1rem;
    border-radius: var(--radius-pill);
    border: 1px solid var(--border-default);
    background: var(--surface-card);
    color: var(--text-primary);
    box-shadow: var(--shadow-3);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
}
.auth-theme-cycle:hover,
.auth-theme-cycle:focus-visible {
    transform: translateY(-1px);
    border-color: var(--color-primary-400);
    color: var(--text-link-hover);
    outline: 0;
}
.auth-theme-cycle[data-theme-state="dark"] {
    background: var(--color-primary-500);
    border-color: var(--color-primary-500);
    color: var(--text-inverse);
}

/* Remove-row "×" in the import review table — a clear, sizeable hit target, no link underline. */
.import-remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.1rem;
    height: 2.1rem;
    font-size: 1.75rem;
    opacity: 0.75;
    transition: opacity var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
}
.import-remove:hover,
.import-remove:focus-visible {
    opacity: 1;
    transform: scale(1.1);
}

/* Error column: fixed-width cell that wraps, so showing/clearing the message never reflows the table. The "Fel"
   header label shows only while a row is invalid (the column keeps its reserved width either way). */
.import-error-cell {
    white-space: normal;
    word-break: break-word;
}
.import-review:not(.has-errors) .import-error-head span {
    visibility: hidden;
}

/* Responsive tables: .table-responsive supplies the horizontal scroll, but a table defaults to width:100% and
   auto-layout will collapse columns (text wrapping, inputs shrinking to a few px) to fit a narrow viewport BEFORE
   the scrollbar ever appears. A min-width is what makes it side-scroll while keeping columns usable. The floor is
   below the laptop content width, so it only engages on small/narrow screens. */
.table-responsive > .table {
    min-width: 44rem;
}
/* The editable import grid is 11 columns of inputs -- it needs a wider floor so a field like "Namn" never shrinks
   to an unwritable sliver. Equal specificity to the rule above + declared after, so it wins for these tables. */
.table-responsive > .import-review {
    min-width: 78rem;
}

/* htmx loading feedback. htmx adds .htmx-request to the requesting element and to any hx-indicator
 * target during a request; the indicator stays hidden until then. Inline action buttons use
 * hx-disabled-elt instead (Bootstrap :disabled is the visible cue); this drives the search spinner. */
.htmx-indicator { opacity: 0; transition: opacity var(--duration-fast) var(--ease-out); }
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { opacity: 1; }

/* Top progress bar for boosted page navigations. htmx holds the outgoing page until the response lands, so
   without a cue a menu click looks frozen; site.js starts this on htmx:beforeRequest and completes it on
   settle, gated to boosted nav (inline widgets — pager/search/inline edits — don't flash it). Trickles toward
   90% while waiting, then snaps to 100% and fades. */
.nav-progress {
    position: fixed;
    top: 0;
    left: 0;
    height: 3px;
    width: 100%;
    transform: scaleX(0);
    transform-origin: left center;
    background: var(--color-primary-500);
    opacity: 0;
    z-index: 2000;
    pointer-events: none;
    transition: transform var(--duration-fast) var(--ease-out), opacity var(--duration-fast) var(--ease-out);
}
.nav-progress.is-active {
    opacity: 1;
    transform: scaleX(0.9);
    transition: transform 8s cubic-bezier(0.1, 0.9, 0.2, 1);
}
.nav-progress.is-done {
    opacity: 0;
    transform: scaleX(1);
    transition: transform var(--duration-fast) var(--ease-out), opacity var(--duration-fast) var(--ease-out) 0.1s;
}
@media (prefers-reduced-motion: reduce) {
    .nav-progress.is-active { transition-duration: 0.01ms; }
}

/* --- Web Interface Guidelines polish ------------------------------------------------------------- */

/* Tabular figures so number/money columns align and don't shift width as values change. Right-aligned
   table cells are the numeric ones across the app; .num covers the non-table cases (e.g. the pager). */
.stat-card__value,
.num,
.table td.text-end,
.table th.text-end { font-variant-numeric: tabular-nums; }

/* Remove the 300ms double-tap delay and the grey tap-flash on touch targets (active/focus styles still
   provide feedback). */
a, button, [role="button"], input, select, textarea, summary, label {
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

/* Modal/drawer scrolling must not chain to the page behind it. */
.modal, .modal-body, .offcanvas, .offcanvas-body { overscroll-behavior: contain; }

/* Long single-line cell content (article/customer names) truncates instead of stretching the table. */
.cell-truncate { max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- Clickable rows -------------------------------------------------------------------------------- */

/* Whole-row navigation: site.js routes a click anywhere on the row (off interactive controls) to the row's
   primary link, so the entire row is a click target without breaking the inline buttons it contains. */
tr.row-link { cursor: pointer; }

/* --- Price-spread analysis modal ------------------------------------------------------------------- */

/* Each customer's price as a bar relative to the highest price, the gap at a glance. Sits under the
   price figure in its right-aligned cell. */
.spread-bar {
    width: 100%;
    max-width: 8rem;
    height: 5px;
    margin-top: 4px;
    border-radius: var(--radius-pill);
    background: var(--color-neutral-100);
    overflow: hidden;
}
.spread-bar__fill {
    height: 100%;
    border-radius: var(--radius-pill);
    background: var(--color-primary-400);
}

/* --- Empty state --------------------------------------------------------------------------------- */

/* A designed pause, not an apology: the module's own glyph in a quiet circle over one line of text. */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-6) var(--space-4);
    text-align: center;
    color: var(--text-secondary);
}
.empty-state__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: var(--radius-pill);
    background: var(--color-neutral-100);
    color: var(--text-secondary);
}
.empty-state__icon .app-icon { font-size: 20px; }
.empty-state p { margin: 0; max-width: 36rem; }

/* --- Order-flow spine ---------------------------------------------------------------------------- */

/* The order's position in its real sequence (approved -> sent) as stations on a hairline. A filled
   teal dot is a completed station; a hollow amber dot is the station waiting on the supplier. The
   same dot grammar as .status-label, so state reads identically across tables and details. */
.order-spine {
    display: flex;
    align-items: flex-start;
    list-style: none;
    margin: 0 0 var(--space-5);
    padding: 0;
}
.order-spine__step {
    position: relative;
    flex: 1;
    min-width: 0;
    padding-top: 20px;
    padding-right: var(--space-3);
    font-size: var(--font-sm);
    font-weight: var(--weight-medium);
    color: var(--text-secondary);
}
.order-spine__step::before {
    content: "";
    position: absolute;
    top: 4px;
    left: 0;
    width: 10px;
    height: 10px;
    border-radius: var(--radius-pill);
    background: var(--surface-card);
    border: 2px solid var(--color-neutral-300);
}
.order-spine__step::after {
    content: "";
    position: absolute;
    top: 8.5px;
    left: 18px;
    right: var(--space-3);
    height: 1px;
    background: var(--border-default);
}
.order-spine__step:last-child { flex: 0 0 auto; padding-right: 0; }
.order-spine__step:last-child::after { display: none; }
.order-spine__step.is-done { color: var(--text-primary); }
.order-spine__step.is-done::before {
    background: var(--color-success-500);
    border-color: var(--color-success-500);
}
/* The connector represents the leg TO the next station, so it only turns green once that next
   station is itself done; a green line into a pending station would overstate progress. */
.order-spine__step:has(+ .order-spine__step.is-done)::after { background: var(--color-success-300); }
.order-spine__step.is-current { color: var(--text-primary); }
.order-spine__step.is-current::before {
    border-color: var(--color-warning-500);
}
.order-spine__time {
    display: block;
    font-size: var(--font-xs);
    font-weight: var(--weight-regular);
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}
