/*
 * Goldeneye brand theme — shadcn/NeoUI tokens generated from the legacy
 * Tombarany.hu palette (see migration/04-ui-and-design.md for the hex sources).
 * Loaded last in the CSS chain; rules are deliberately unlayered so they win
 * over the NeoUI theme files (which live in `@layer base`), including their
 * `.dark` variants. The app is single-theme: dark-red-and-gold, no toggle.
 */

/* Inter, self-hosted as a VARIABLE font. Nothing is fetched from Google at runtime: an
   internal portal should not depend on a third party to render its own text.

   ONE file, subset for Hungarian, and that is the point. Google ships Inter split into
   "latin" and "latin-ext", and ő ű Ő Ű (U+0150/0151/0170/0171) live in latin-ext — so with
   only latin preloaded, every heading rendered its plain letters immediately and then
   visibly re-drew the double acutes a moment later when the second file arrived. In
   Hungarian those characters are in almost every word, so it was a constant flicker rather
   than the edge case it would be in English (Attila spotted it on /users/salespersons).

   Preloading latin-ext too would have cost 84 KB to fix it. Instead this is a custom subset
   of the full variable font: the latin range plus exactly those four codepoints — every
   other Hungarian accent (á é í ó ö ú ü) already sits in Latin-1 Supplement. 68 KB in one
   request against 132 KB in two, no unicode-range split, and nothing to swap in late.

   Regenerate with fonttools if the ranges ever need to change:
     pyftsubset InterVariable.ttf --flavor=woff2 --output-file=inter.woff2 \
       --layout-features="kern,liga,calt,tnum,ccmp,locl,mark,mkmk" \
       --unicodes="U+0000-00FF,U+0131,U+0150-0153,U+0170-0171,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD"
   Keep tnum in the feature list — the tabular figures the data grids rely on come from it. */
@font-face {
    font-family: "Inter";
    src: url("../fonts/inter.woff2") format("woff2");
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
}

:root {
    /* Surfaces */
    --background: oklch(0.3683 0.1271 25.13);          /* #741719 page red */
    --foreground: oklch(1 0 0);                         /* white text */
    --card: oklch(0.4201 0.1700 25.77 / 0.302);         /* the ACTUAL legacy panel: left.box.bg.png = rgba(148,1,18,0.302) */
    --card-foreground: oklch(1 0 0);
    --popover: oklch(0.4209 0.1694 23.44);              /* #94011A popup background */
    --popover-foreground: oklch(1 0 0);
    --surface: oklch(0.3875 0.1409 25.39);              /* #7f1418 */
    --surface-foreground: oklch(1 0 0);

    /* Brand */
    --primary: oklch(0.4209 0.1694 23.44);              /* #94011A */
    --primary-foreground: oklch(1 0 0);
    --secondary: oklch(0.4956 0.1945 25.53);            /* #B70F1E zebra/selected */
    --secondary-foreground: oklch(1 0 0);
    --muted: oklch(0.3875 0.1409 25.39);                /* #7f1418 */
    --muted-foreground: oklch(0.8452 0 0);              /* #ccc */
    --accent: oklch(0.7903 0.1589 74.23);               /* #f6a924 gold */
    --accent-foreground: oklch(0.3683 0.1271 25.13);    /* red text on gold */

    /* Errors are GOLD on this site (legacy #eaa122), deliberately not red. */
    --destructive: oklch(0.7618 0.1529 74.47);          /* #eaa122 */
    --destructive-foreground: oklch(0.3683 0.1271 25.13);

    /* Structure */
    --border: oklch(0.4209 0.1694 23.44);               /* #94011A card borders */
    --input: oklch(0.8452 0 0 / 0.55);                  /* #ccc-ish input borders */
    --ring: oklch(0.7903 0.1589 74.23);                 /* gold focus ring */
    --radius: 0.375rem;

    /* Selection */
    --selection: oklch(0.7903 0.1589 74.23);
    --selection-foreground: oklch(0.3683 0.1271 25.13);

    /* Sidebar — same family, slightly darker red (#5f1214) */
    --sidebar: oklch(0.3211 0.1094 24.71);
    --sidebar-foreground: oklch(1 0 0);
    --sidebar-primary: oklch(0.4209 0.1694 23.44);
    --sidebar-primary-foreground: oklch(1 0 0);
    --sidebar-accent: oklch(0.4209 0.1694 23.44);       /* hover: #94011A */
    --sidebar-accent-foreground: oklch(0.7903 0.1589 74.23); /* gold text on hover */
    --sidebar-border: oklch(0.2733 0.0896 23.77);       /* #4a0e10 */
    --sidebar-ring: oklch(0.7903 0.1589 74.23);

    /* Charts — legacy chart colors first */
    --chart-1: oklch(0.7903 0.1589 74.23);              /* #f6a924 */
    --chart-2: oklch(0.9127 0.1902 100.64);             /* #ffe400 */
    --chart-3: oklch(0.5240 0.2108 23.09);              /* #C70228 */
    --chart-4: oklch(0.4956 0.1945 25.53);              /* #B70F1E */
    --chart-5: oklch(0.8452 0 0);                       /* #ccc */

    /* Typography: Inter throughout (Attila, 2026-09-04, replacing Philosopher).
       ONE family, deliberately. Philosopher was set on the whole content area while the
       sidebar and inputs ran on a system sans, and that split was most of what read as
       old-school. Headings are now separated by WEIGHT and tighter tracking instead of by
       a different face — see .ge-page-title.
       --font-heading is kept as a name so the heading rules still read intentionally, and
       so a future display face has one place to land. */
    --font-sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-heading: var(--font-sans);

    /* Money and dates line up in columns all over this app. */
    --font-numeric: tabular-nums;
}

html {
    background-color: var(--background);
}

body {
    background-color: var(--background);
    color: var(--foreground);
    font-family: var(--font-sans);
}

::selection {
    background-color: var(--selection);
    color: var(--selection-foreground);
}

/* Default link gold lives in the base layer so component utilities (e.g. NeoUI's
   hover:text-accent-foreground on outline LinkButtons) can override it — an
   unlayered rule here made hover text gold-on-gold and invisible. */
@layer base {
    a {
        color: var(--accent);
    }
}

/* The sidebar's resting gold text is a deliberate brand choice (approved look);
   keep it explicit now that the generic rule is layered. Active items carry their
   own gold-chip rule. */
[data-collapsible] a:not([data-active="true"]) {
    color: var(--accent);
}

/* The legacy skin lowercased labels, buttons and headings. That is GONE as of 2026-09-03
   (Attila: "remove the lowercasing for good"), and nothing here should reintroduce it.

   It was never only a style choice. Any of those elements can contain a NAME, and CSS
   cannot lowercase the label half of a string and spare the name half. The case that
   settled it: NeoUI renders a Select trigger as a <button>, so the closed dropdown on the
   "Ügyfél áthelyezése" dialog read "bögöly andrás (1025711189)" while the open list right
   underneath it read "Bögöly András" correctly — the same person, spelled two ways, one of
   them wrong.

   If the lowercase look is ever wanted back, it belongs on specific classes whose content
   is known to be a fixed caption, never on bare element selectors. */

/* Content and chrome are one family now; this stays only so the rule is explicit rather
   than relying on inheritance through NeoUI's own resets. */
.ge-main,
.ge-auth-wrap {
    font-family: var(--font-sans);
}

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

.ge-sidebar-logo {
    display: block;
    padding: 0.75rem 0.5rem 0.5rem;
}

.ge-sidebar-logo img {
    width: 100%;
    height: auto;
}

.ge-topbar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    height: 3rem;
    padding: 0 1rem;
    border-bottom: 1px solid var(--sidebar-border);
}

/* The sidebar open/close toggle — NeoUI's SidebarTrigger, the only button up here.
   Gold like every other actionable icon in the app (Attila, 2026-09-04); it was
   inheriting the default white and read as decoration rather than a control. */
.ge-topbar button {
    color: var(--accent);
    border-radius: var(--radius, 0.375rem);
    transition: background 0.15s ease, color 0.15s ease;
}

.ge-topbar button:hover {
    background: linear-gradient(180deg, #f4c454, #eaa122);
    color: #5f1214;
}

.ge-topbar button:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 2px;
}

.ge-main {
    flex: 1;
    padding: 1.5rem 2rem 3rem;
    /* Legacy red-silk backdrop, recreated as a scalable SVG (img/swirl.svg).
       Fixed attachment keeps it anchored to the viewport for a stable, deep feel. */
    background: #6e1316 url("../img/swirl.svg") no-repeat fixed center / cover;
}

.ge-page-title {
    font-family: var(--font-heading);
    font-size: 2.25rem;
    line-height: 1.15;
    /* 600 and negative tracking, not 400: a serif carried a display size on its own
       shapes, a UI sans needs weight to hold the same position. */
    font-weight: 600;
    letter-spacing: -0.02em;
    /* Gold (Attila, 2026-09-04). Neither this app nor the legacy one ever coloured the
       heading TEXT — the gold at a page title came from the little arrow image in front
       of it (legacy h1.page-title carried Images/index.cols.arrow.png; this app carried
       the same ornament on ::before). Removing that ornament removed the only gold there,
       so the colour moves into the type itself.
       5.65:1 on the page red — comfortably past the 3.0:1 WCAG bar for large text. */
    color: var(--accent);
    margin: 0 0 1.5rem;
    text-wrap: balance;
}

/* ---- Auth pages ---------------------------------------------------------- */

.ge-auth-page {
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
}

.ge-auth-wrap {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
    background: #6e1316 url("../img/swirl.svg") no-repeat fixed center / cover;
}

.ge-login-logo {
    margin-bottom: 2rem;
}

.ge-login-logo img {
    display: block;
    width: min(320px, 80vw);
    height: auto;
}

/* Legacy centered form card: 480 wide, translucent tile, #94011A border. */
.ge-login-card {
    width: min(480px, 92vw);
}

.ge-form-title {
    font-family: var(--font-heading);
    font-size: 2.25rem;
    line-height: 1.15;
    font-weight: 600;
    letter-spacing: -0.02em;
    color: var(--accent);
    margin-bottom: 1.5rem;
}

.ge-form-buttons {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-top: 1.5rem;
}

/* Gold submit button — matched to the legacy .buttonYellow: #eaa122 base with
   bevel borders (#f3ca64 top / #ecab33 sides) and WHITE text, as on the old site. */
/* The gold call-to-action. Dialog confirm buttons are pulled in here too: NeoUI gives
   them ButtonVariant.Default, which is bg-primary — the deep red of this theme — so an
   OK button came out red against a red card (Attila, 2026-08-23). The cancel button is
   an Outline variant and carries no bg-*, so it stays as it is. */
button.ge-button-gold,
[role="alertdialog"] button.bg-primary,
[role="alertdialog"] button.bg-destructive,
[role="dialog"] button.bg-primary,
[role="dialog"] button.bg-destructive {
    transition: background 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
    background: linear-gradient(180deg, #f3ca64 0%, #eaa122 55%, #dd9317 100%);
    border: 1px solid #ecab33;
    border-top-color: #f3ca64;
    color: #fff;
    text-shadow: 0 1px 1px rgb(0 0 0 / 0.3);
    box-shadow: 0 2px 8px rgb(0 0 0 / 0.35);
}

button.ge-button-gold:hover,
[role="alertdialog"] button.bg-primary:hover,
[role="alertdialog"] button.bg-destructive:hover,
[role="dialog"] button.bg-primary:hover,
[role="dialog"] button.bg-destructive:hover {
    background: linear-gradient(180deg, #ffd977 0%, #f9b437 55%, #ea9f1d 100%);
    color: #fff;
    box-shadow: 0 4px 14px rgb(246 169 36 / 0.45);
    transform: translateY(-1px);
}

.ge-remember-me {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    font-size: 0.9375rem;
}

.ge-remember-me input[type="checkbox"] {
    width: 1rem;
    height: 1rem;
    accent-color: var(--accent);
}

/* Validation errors render in gold, like the old site. */
.ge-validation-error,
.validation-message {
    color: var(--destructive);
    font-size: 0.875rem;
    margin: 0.25rem 0 0.75rem;
}

/* ---- Read-only detail rows (GFormRow) ------------------------------------ */
/* Legacy editor-label/editor-field rhythm: 186px right-aligned lowercase label. */

.ge-form-row {
    display: flex;
    align-items: baseline;
    gap: 1.25rem;
    margin-bottom: 0.5rem;
}

.ge-form-label {
    flex: 0 0 186px;
    text-align: right;
    color: var(--muted-foreground);
}

.ge-form-value {
    flex: 1;
    min-width: 0;
}

@media (max-width: 640px) {
    .ge-form-row {
        flex-direction: column;
        gap: 0.125rem;
    }

    .ge-form-label {
        flex-basis: auto;
        text-align: left;
    }
}

/* ---- Sidebar footer / logout -------------------------------------------- */

.ge-user-badge {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.5rem;
    min-width: 0;
}

.ge-user-badge-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1;
}

.ge-user-name {
    font-size: 0.8125rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ge-user-username {
    font-size: 0.6875rem;
    color: var(--muted-foreground);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ge-user-company {
    font-size: 0.75rem;
    color: var(--muted-foreground);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ge-logout-form {
    display: contents;
}

.ge-logout-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.375rem;
    border: none;
    border-radius: calc(var(--radius) * 0.8);
    background: transparent;
    color: var(--sidebar-foreground);
    cursor: pointer;
}

.ge-logout-button:hover {
    color: var(--accent);
    background-color: var(--sidebar-accent);
}

/* ---- Dashboard ----------------------------------------------------------- */

.ge-dashboard {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: 1.5rem;
    align-items: start;
}

@media (max-width: 900px) {
    .ge-dashboard {
        grid-template-columns: minmax(0, 1fr);
    }
}

.ge-rate-box h4 {
    font-family: var(--font-heading);
    font-size: 1rem;
    color: var(--accent);
    margin: 0.75rem 0 0.125rem;
}

.ge-rate-box .ge-rate-date {
    color: var(--foreground);
    font-size: 1.125rem;
    margin-top: 0;
}

.ge-rate-value {
    margin: 0;
    font-size: 1rem;
}

.ge-rate-separator {
    margin: 0.5rem 0;
}

.ge-price-chart {
    max-width: 100%;
    height: auto;
    margin-top: 1rem;
}

.ge-denied-card {
    max-width: 480px;
}

.ge-denied-card p {
    margin: 0 0 1rem;
}

/* Icon-collapsed sidebar: NeoUI hides its own components' label spans, but our
   custom pieces (logo wordmark, raw logout button, user badge text) need explicit
   rules keyed on the sidebar's data-collapsible attribute. */
[data-collapsible="icon"] .ge-sidebar-logo {
    display: flex;
    justify-content: center;
    padding: 0.75rem 0 0.5rem;
}

/* The wordmark's leftmost square is the sun glyph — crop to it as the collapsed brand mark. */
[data-collapsible="icon"] .ge-sidebar-logo img {
    width: 2rem;
    height: 2rem;
    object-fit: cover;
    object-position: left center;
}

[data-collapsible="icon"] .ge-user-badge-text {
    display: none;
}

/* Footer collapses to a vertical stack: avatar on top, logout icon under it. */
[data-collapsible="icon"] .ge-user-badge {
    flex-direction: column;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.25rem 0;
}

[data-collapsible="icon"] .ge-user-badge > span:first-child {
    width: 2rem;
    height: 2rem;
}

/* ---- Legacy visual "pop" pass (2026-08-19, from Attila's reference shots) ---- */

/* Panels: glassy red glow — translucent white-over-swirl with the #94011A frame,
   soft drop shadow and a 1px inner highlight, like the legacy formContent tiles. */
[data-slot="card"] {
    background: var(--card);
    border-color: rgb(148 1 26 / 0.9);
    box-shadow: 0 10px 28px rgb(0 0 0 / 0.35), inset 0 1px 0 rgb(255 255 255 / 0.06);
}

/* Active sidebar item: the legacy selected-menu look — gold gradient, dark red text.
   (NeoUI marks the anchor with data-active but no data-sidebar attribute, so the
   selector scopes through the sidebar root's data-collapsible attribute instead.) */
[data-collapsible] a[data-active="true"],
[data-collapsible] a[data-active="true"]:hover,
[data-collapsible] button[data-active="true"],
[data-collapsible] button[data-active="true"]:hover {
    background: linear-gradient(180deg, #f4c454, #eaa122);
    color: #5f1214;
    font-weight: 600;
}

/* The gold dotted cross that used to precede every heading is gone (Attila,
   2026-09-02): it carried no meaning, and on a detail screen the back button now
   occupies that spot. See GPageTitle.razor. */

/* ---- Page heading row (GPageTitle) --------------------------------------- */

.ge-page-head {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 0 0 1.5rem;
}

/* Only when there are Actions: a list screen's heading should not stretch. */
.ge-page-head-split {
    justify-content: space-between;
    flex-wrap: wrap;
}

.ge-page-head-main {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    min-width: 0;
}

/* The margin lives on the row now, so the heading inside it must not add its own. */
.ge-page-head .ge-page-title {
    margin: 0;
}

.ge-back-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--radius, 0.375rem);
    background: transparent;
    color: var(--accent);
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.ge-back-button:hover {
    background: rgb(255 255 255 / 0.06);
    border-color: var(--accent);
}

.ge-back-button:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 1px;
}

/* ---- Golden footer (legacy footer.bg.png, resampled into a CSS gradient) ------ */

.ge-footer {
    margin-top: auto;
}

.ge-footer-band {
    height: 71px;
    background: linear-gradient(180deg,
        #190505 0,
        #230600 1px,
        #753c03 2px,
        #bc7f29 3px,
        #a07025 4px,
        #84590e 9%,
        #8e6010 25%,
        #a06b12 50%,
        #b27914 75%,
        #bd7f0f 93%,
        #bb801d 96%,
        #ba8133 98%,
        #5b1d18 100%);
    border-bottom: 2px solid #610507;
}

.ge-footer-copy {
    margin: 0;
    padding: 0.55rem 1rem;
    text-align: center;
    font-family: var(--font-sans);
    font-size: 0.875rem;
    color: rgb(255 240 224 / 0.92);
    background: #741719;
    border-top: 1px solid #9b2b2f;
    border-bottom: 1px solid #530f11;
}

/* ---- Blazor circuit-error banner: branded, Hungarian, hidden until Blazor
       reveals it (server restart / lost SignalR circuit). ---------------------- */
#blazor-error-ui {
    display: none;
    position: fixed;
    left: 0;
    bottom: 0;
    width: 100%;
    z-index: 1000;
    box-sizing: border-box;
    padding: 0.6rem 2.5rem 0.7rem 1.25rem;
    background: #4a0e10;
    border-top: 2px solid #eaa122;
    color: #fff;
    font-family: var(--font-sans);
    font-size: 0.9rem;
    text-align: center;
    box-shadow: 0 -4px 14px rgb(0 0 0 / 0.4);
}

#blazor-error-ui .reload {
    color: #f6a924;
    font-weight: 600;
    text-decoration: underline;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
    color: #ccc;
}

/* Open select trigger: NeoUI pairs bg-background with text-accent-foreground in the
   expanded+hover state; our accent-foreground is dark red, so the value vanished.
   Force readable text while expanded. */
button[role="combobox"][aria-expanded="true"] {
    color: var(--foreground);
}

/* ---- DataTable action icons + modernized pager (2026-08-19 design round) ----- */

/* Row action icon buttons (edit/delete ghosts): gold so they read as actionable;
   hover flips to the gold-gradient chip like the sidebar's active item.
   .row-actions is listed on its own so a plain table gets the same treatment as a
   GDataGrid — the class names the pattern, and the pattern should not depend on which
   table happens to host it (Attila, 2026-09-03). */
.ge-datagrid td button,
.row-actions button {
    color: #f6a924;
}

.ge-datagrid td button:hover,
.row-actions button:hover {
    background: linear-gradient(180deg, #f4c454, #eaa122);
    color: #5f1214;
}

/* Pager: shadcn-style — square bordered chips, gold iconography, tidy type.
   (NeoUI's DataTable owns page state internally and exposes no page API, so this
   is a restyle of its built-in pagination bar rather than a replacement.) */
.ge-datagrid .flex.items-center.gap-1 button {
    width: 2.1rem;
    height: 2.1rem;
    border: 1px solid rgb(255 255 255 / 0.22);
    background: rgb(255 255 255 / 0.05);
    color: #f6a924;
    border-radius: 0.5rem;
}

.ge-datagrid .flex.items-center.gap-1 button svg {
    width: 1rem;
    height: 1rem;
}

.ge-datagrid .flex.items-center.gap-1 button:hover:not(:disabled) {
    background: linear-gradient(180deg, #f4c454, #eaa122);
    border-color: #ecab33;
    color: #5f1214;
}

.ge-datagrid .flex.items-center.gap-1 button:disabled {
    opacity: 0.35;
}

/* Pager texts: warm light instead of grey-muted. */
.ge-datagrid .text-muted-foreground {
    color: rgb(255 236 214 / 0.85);
}

/* Table action items: identical hit-area, spacing and gold treatment whether the
   cell renders icon Buttons (Rates etc.) or icon links (Clients view/edit). */
.ge-datagrid td .row-actions,
.row-actions {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}

.ge-datagrid td .row-actions a,
.ge-datagrid td a.edit-link,
.row-actions a,
.row-actions a.edit-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 0.45rem;
    color: #f6a924;
}

.ge-datagrid td .row-actions a:hover,
.ge-datagrid td a.edit-link:hover,
.row-actions a:hover,
.row-actions a.edit-link:hover {
    background: linear-gradient(180deg, #f4c454, #eaa122);
    color: #5f1214;
}

/* Buttons and button-shaped links behave identically, whether rendered as <button> or
   as a NeoUI LinkButton <a>. */
button,
a.inline-flex.no-underline {
    cursor: pointer;
}

/* NeoUI's Link-variant button paints text-primary — the brand dark red, which
   disappears against the red backdrop (the login page's "elfelejtett jelszó" was
   unreadable). Links are gold on this site, hover matching the gold button hover. */
a.inline-flex.text-primary {
    color: var(--accent);
}

a.inline-flex.text-primary:hover {
    color: #ffd977;
}

/* ---- Sectioned form layout (2026 redesign, option C from migration/07) ------- */

.ge-form-page {
    max-width: 60rem;
    margin-inline: auto;
}

.ge-form-section {
    margin-bottom: 1.5rem;
    border: 1px solid rgb(148 1 26 / 0.9);
    border-radius: calc(var(--radius) * 1.5);
    background: var(--card);
    box-shadow: 0 10px 28px rgb(0 0 0 / 0.35), inset 0 1px 0 rgb(255 255 255 / 0.06);
}

/* Flat variant (?flat=1): no card chrome, sections separated by their headers. */
.ge-form-page.flat .ge-form-section {
    border-color: transparent;
    background: none;
    box-shadow: none;
    margin-bottom: 2.25rem;
}

.ge-form-section-head {
    padding: 1.1rem 1.5rem 0.9rem;
    border-bottom: 1px solid rgb(255 255 255 / 0.09);
}

.ge-form-page.flat .ge-form-section-head {
    padding-inline: 0;
    border-bottom-color: rgb(148 1 26 / 0.8);
}

.ge-form-section-head h2 {
    margin: 0;
    font-family: var(--font-heading);
    font-size: 1.3rem;
    line-height: 1.3;
    color: #fff;
}

.ge-form-section-head p {
    margin: 0.3rem 0 0;
    font-size: 0.85rem;
    /* Linear-style opacity discipline: non-actionable text sits at ~60%. */
    color: rgb(255 236 214 / 0.6);
}

.ge-form-section-body {
    padding: 1.3rem 1.5rem 1.45rem;
}

.ge-form-page.flat .ge-form-section-body {
    padding-inline: 0;
}

/* Paired short fields share a row (Baymard: clusters yes, whole-form columns no). */
.ge-grid-2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 1.25rem;
    align-items: start;
}

@media (max-width: 760px) {
    .ge-grid-2 {
        grid-template-columns: 1fr;
    }
}

/* ---- Sticky dirty-state save bar --------------------------------------------- */

.ge-save-bar {
    position: sticky;
    bottom: 0.75rem;
    z-index: 30;
    margin-top: 1.25rem;
    opacity: 0;
    transform: translateY(10px);
    pointer-events: none;
    transition: opacity 0.18s ease, transform 0.18s ease;
}

.ge-save-bar.is-visible {
    opacity: 1;
    transform: none;
    pointer-events: auto;
}

.ge-save-bar-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.65rem 1rem;
    border: 1px solid rgb(148 1 26 / 0.9);
    border-radius: 0.75rem;
    background: rgb(52 9 11 / 0.93);
    backdrop-filter: blur(6px);
    box-shadow: 0 -6px 24px rgb(0 0 0 / 0.45);
}

.ge-save-bar-hint {
    font-size: 0.85rem;
    color: rgb(255 236 214 / 0.7);
}

.ge-save-bar-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

/* Desktop density: sections flow in two columns so a whole record fits with
   minimal scrolling (sections stay single-column inside — scan order per card
   is preserved; only the CARDS are side by side). */
.ge-form-page {
    max-width: 100rem;
}

.ge-form-columns {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 1.5rem;
    align-items: start;
}

@media (max-width: 1280px) {
    .ge-form-columns {
        grid-template-columns: 1fr;
    }
}

/* Hybrid form variant (Attila, 2026-08-19): ONE wide card around the whole form,
   sections become headed groups inside it; field layout unchanged. */
.ge-form-page.hybrid {
    background: var(--card);
    border: 1px solid rgb(148 1 26 / 0.9);
    border-radius: calc(var(--radius) * 1.5);
    box-shadow: 0 10px 28px rgb(0 0 0 / 0.35), inset 0 1px 0 rgb(255 255 255 / 0.06);
    padding: 1.75rem 2rem 1.5rem;
}

.ge-form-page.hybrid .ge-form-section {
    border: none;
    background: none;
    box-shadow: none;
    margin-bottom: 1.9rem;
}

.ge-form-page.hybrid .ge-form-section-head {
    padding: 0 0 0.6rem;
    border-bottom: 1px solid rgb(255 255 255 / 0.12);
}

.ge-form-page.hybrid .ge-form-section-body {
    padding: 0.95rem 0 0;
}

/* Short forms (one section, a handful of fields) keep the legacy narrow column
   instead of stretching the wide card across the screen. */
.ge-form-page.narrow {
    max-width: 40rem;
}

/* Form controls share one height — NeoUI's text Input and Select trigger ship
   with different vertical metrics and looked mismatched side by side. */
.ge-form-page .ge-form-section-body input:not([type="checkbox"]):not([type="radio"]),
.ge-form-page .ge-form-section-body button[role="combobox"],
.ge-form-page .ge-form-section-body .ge-date-trigger {
    height: 2.75rem;
}

/* ---- Date trigger (GDatePicker) ---------------------------------------------- */

/* NeoUI's own DatePicker bakes w-[280px] and h-9 into the trigger's class list, which
   is why dates were narrower and shorter than every neighbouring control. GDatePicker
   renders its own trigger, and this is the shape it takes everywhere — inside a form
   section (where the rule above matches too) and on the contract list's filter bar. */
button.ge-date-trigger {
    height: 2.75rem;
    min-width: 12rem;
    justify-content: flex-start;
    gap: 0.55rem;
    font-weight: 400;
}

/* Inside a form the date fills its Field, like every input and select trigger. On the
   contract list's filter bar it sizes to its own content instead. */
.ge-form-page .ge-form-section-body button.ge-date-trigger {
    width: 100%;
}

/* The filter bar's submit matches the date trigger beside it. The rule lives here
   rather than in Contracts.razor.css because scoped CSS never reaches the markup a
   child component renders — the Button's <button> carries the component's own scope. */
.filter-form button[type="submit"] {
    height: 2.75rem;
}

/* The placeholder reads as absent data, not as a value. */
button.ge-date-trigger .ge-date-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

button.ge-date-trigger.invalid {
    border-color: var(--destructive);
}

/* ---- Checkboxes -------------------------------------------------------------- */

/* NeoUI renders a checkbox as <button role="checkbox" data-state="checked|unchecked">
   at h-4 w-4 (16px), a size below the rest of the form controls. */
button[role="checkbox"] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.25rem;
    border-color: rgb(148 1 26 / 0.9);
}

/* Checked uses the brand gold with a dark-red tick — the same accent language as the
   active sidebar item. The tick is an SVG stroked with currentColor, so `color` sets it. */
button[role="checkbox"][data-state="checked"] {
    background: linear-gradient(180deg, #f4c454, #eaa122);
    border-color: #ecab33;
    color: #5f1214;
}

/* NeoUI's horizontal Field centres its control with a :has([data-slot=checkbox]) rule,
   but the checkbox button carries no data-slot attribute, so the selector never matched
   and the box sat top-aligned against its label. Same intent, hooks that exist. */
[data-slot="field"][data-orientation="horizontal"]:has(button[role="checkbox"]) {
    align-items: center;
}

/* Contract wizard quantity boxes. Lives here, not in the page's scoped CSS: the
   class is passed to a NeoUI Input COMPONENT, and Blazor's scoped-CSS attribute
   never lands on a child component's own markup, so a scoped rule can't match. */
.product-table .quantity-input {
    max-width: 6rem;
    margin-left: auto;
}

/* NeoUI Calendar day cells: the day button is inline-flex inside a block td, so
   every cell carries ~5px of phantom baseline space — except the selected one,
   which made the highlight sit visibly offset in some rows. Flex-level buttons
   collapse the line box; the tr.flex>td>button shape exists only in the calendar. */
tr.flex > td > button.h-9.w-9 {
    display: flex;
}

/* Same phantom space, second source: the EMPTY cells padding the first and last
   week hold no button to flex, so they kept their line box and left those two rows
   at 41px against the middle rows' 36px — the day button then sat at the top of an
   over-tall row with 5px of slack under it, which is the residual misalignment in
   exactly the first and last rows. Flexing the cell collapses the line box whether
   or not it holds a day. */
tr.flex > td {
    display: flex;
}

/* Calendar selected day. Stock shadcn paints the CELL with bg-accent as a
   range-selection backdrop — near-invisible grey there, vivid gold here — so
   square gold corners peeked out behind the rounded dark day button. The cell
   layer goes; the selected DAY becomes the rounded gold chip, matching hover
   and the sidebar's active item. */
tr.flex > td:has([aria-selected]) {
    background: transparent;
}

/* NeoUI emits aria-selected as an EMPTY attribute (aria-selected=""), so match
   on presence — only the selected day carries the attribute at all. */
tr.flex > td > button[aria-selected],
tr.flex > td > button[aria-selected]:hover,
tr.flex > td > button[aria-selected]:focus {
    background: linear-gradient(180deg, #f4c454, #eaa122);
    color: #5f1214;
    border-radius: 0.45rem;
    font-weight: 600;
}

/* ---- GDatePicker own caption (Dropdown mode, currently ToS only) -------------
   NeoUI's Dropdown caption used native <select>s (untheme-able OS list) and
   overflowed the panel; GDatePicker renders its own GSelect-based caption above
   a Label-mode calendar instead. The wrapper takes over as the visible panel and
   the calendar's inner chrome + internal "january 2011" caption are stripped. */
.ge-cal {
    /* Column so the day grid centers under the caption — the caption (fixed month
       basis) is wider than the Calendar's 7-column table, which otherwise hugged
       the left of the popover. The caption itself still stretches edge to edge. */
    display: inline-flex;
    flex-direction: column;
    align-items: center;
}

.ge-cal-caption {
    align-self: stretch;
}

.ge-cal-owncaption {
    background: var(--background);
    border: 1px solid var(--border);
    border-radius: 0.45rem;
    box-shadow: 0 10px 28px rgb(0 0 0 / 0.35);
    padding-top: 0.7rem;
}

.ge-cal-owncaption .inline-block.bg-background {
    background: transparent;
    border: 0;
    box-shadow: none;
}

.ge-cal-owncaption div.relative.flex.w-full.items-center.justify-center {
    display: none;
}

.ge-cal-caption {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0 0.75rem;
}

/* Fixed basis so the caption's width never follows the month name — an auto basis
   made the whole popover jump between "május" and "szeptember" (the longest). The
   7.75rem fits "szeptember" with slack; grow only soaks up space the grid grants. */
.ge-cal-monthwrap {
    flex: 1 0 7.75rem;
}

.ge-cal-yearwrap {
    flex: 0 0 5.25rem;
}

.ge-cal-menuwrap {
    position: relative;
}

.ge-cal-ddl {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.35rem;
    width: 100%;
    height: 2.25rem;
    padding-inline: 0.5rem;
    font-size: 0.8125rem;
    color: var(--foreground);
    background: transparent;
    border: 1px solid var(--input);
    border-radius: 0.4rem;
}

.ge-cal-ddl:hover {
    background: var(--sidebar-accent);
    color: var(--accent);
}

.ge-cal-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    /* max-content so long month names never force a horizontal scrollbar. */
    width: max-content;
    min-width: 100%;
    max-height: 15rem;
    overflow-y: auto;
    overflow-x: hidden;
    z-index: 70;
    padding: 0.25rem;
    background: var(--popover);
    border: 1px solid rgb(0 0 0 / 0.4);
    border-radius: 0.5rem;
    box-shadow: 0 12px 30px rgb(0 0 0 / 0.5);
    /* Themed thin scrollbar — the default classic tracks painted white frames. */
    scrollbar-width: thin;
    scrollbar-color: rgb(255 255 255 / 0.35) transparent;
}

.ge-cal-menu::-webkit-scrollbar {
    width: 8px;
}

.ge-cal-menu::-webkit-scrollbar-track {
    background: transparent;
}

.ge-cal-menu::-webkit-scrollbar-thumb {
    background: rgb(255 255 255 / 0.3);
    border-radius: 4px;
}

.ge-cal-menu > button {
    display: block;
    width: 100%;
    padding: 0.3rem 0.6rem;
    text-align: left;
    font-size: 0.8125rem;
    color: var(--popover-foreground);
    background: transparent;
    border: 0;
    border-radius: 0.35rem;
    white-space: nowrap;
}

.ge-cal-menu > button:hover {
    background: var(--secondary);
}

.ge-cal-menu > button.is-selected {
    background: linear-gradient(180deg, #f4c454, #eaa122);
    color: #5f1214;
    font-weight: 600;
}

.ge-cal-caption > button {
    flex: 0 0 auto;
    width: 2.25rem;
    height: 2.25rem;
}

/* ---- Accept-terms page (moved from scoped CSS 2026-08-20; global so the rules
   also reach the NeoUI Card markup without ::deep). The card is wide and the ÁSZF
   body is a spread-height scroll window: the page is a full-height flex column and
   the body soaks up whatever the viewport offers, so the accept row stays on
   screen without page scrolling — on phones too. *@ */

/* The auth layout only sets MIN-height, which lets content grow past the viewport;
   the spread-height scroll window needs a HARD viewport cap on the whole chain —
   scoped to this page via :has so login/change-password keep their natural sizing. */
.ge-auth-page:has(.terms-page) {
    height: 100dvh;
}

.ge-auth-wrap:has(.terms-page) {
    min-height: 0;
}

.terms-page {
    flex: 1;
    min-height: 0;
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
}

.terms-logo img {
    margin-inline: auto;
    width: min(240px, 60vw);
}

.terms-page .terms-card {
    width: min(1100px, 96vw);
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    margin-bottom: 1rem;
}

.terms-page .terms-card > * {
    /* NeoUI CardContent — must become the flex column the body stretches in. */
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.terms-page .terms-card form {
    display: flex;
    flex-direction: column;
}

.terms-effective {
    color: var(--muted-foreground);
    font-size: 0.875rem;
    margin-bottom: 0.75rem;
}

.terms-body {
    flex: 1;
    min-height: 12rem;
    overflow-y: auto;
    padding: 1rem 1.25rem;
    margin-bottom: 1rem;
    border: 1px solid var(--input);
    border-radius: var(--radius, 0.375rem);
    background-color: var(--background);
    font-size: 0.9375rem;
    line-height: 1.65;
    overflow-wrap: break-word;
    scrollbar-width: thin;
    scrollbar-color: rgb(255 255 255 / 0.35) transparent;
}

.terms-body::-webkit-scrollbar { width: 8px; }
.terms-body::-webkit-scrollbar-track { background: transparent; }
.terms-body::-webkit-scrollbar-thumb { background: rgb(255 255 255 / 0.3); border-radius: 4px; }

/* Admin-authored HTML: keep headings proportionate inside the scroll window. */
.terms-body h1, .terms-body h2, .terms-body h3 {
    font-size: 1.15rem;
    line-height: 1.4;
    margin: 0.75rem 0 0.5rem;
}

.terms-page .ge-form-title {
    margin-bottom: 0.75rem;
}

.terms-page .ge-form-buttons {
    margin-top: 1rem;
}

@media (max-width: 640px) {
    .terms-page .terms-card {
        width: 100vw;
    }

    .terms-body {
        padding: 0.75rem;
    }
}

/* Client list search box: the filter sits above the grid and matches the input
   height used across the forms. */
.client-search {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: min(28rem, 100%);
    margin-bottom: 1rem;
    padding-inline: 0.75rem;
    height: 2.75rem;
    border: 1px solid var(--input);
    border-radius: var(--radius, 0.375rem);
    background: var(--card);
    color: var(--muted-foreground);
}

.client-search:focus-within {
    outline: 2px solid var(--ring);
    outline-offset: 1px;
}

.client-search-input {
    flex: 1;
    min-width: 0;
    height: 100%;
    border: 0;
    background: transparent;
    color: var(--foreground);
    font-size: 0.9375rem;
}

.client-search-input:focus {
    outline: none;
}

.client-search-input::placeholder {
    color: var(--muted-foreground);
}

/* A search box sitting inside a .report-filters row (the Szerződések screen). The
   stand-alone box carries a bottom margin for the grid below it; the row already
   spaces itself, and .report-filters aligns on flex-end, so the margin would lift the
   box a full 1rem above the controls beside it. */
.report-filters .client-search {
    margin-bottom: 0;
}

/* A NeoUI Checkbox with its caption, on the same baseline as the controls beside it.
   The Checkbox renders a <button>, so the <label> wrapper is what makes the caption
   clickable — a for= attribute has nothing to bind to. */
.ge-filter-check {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    height: 2.75rem;
    cursor: pointer;
    user-select: none;
    font-size: 0.9375rem;
}

/* Website article body inside the read-only news detail card. */
.news-body {
    margin: 0.5rem 0 1.5rem;
    line-height: 1.65;
    overflow-wrap: break-word;
}

.news-body p {
    margin: 0 0 0.75rem;
}

.news-body img {
    max-width: 100%;
    height: auto;
    border-radius: var(--radius, 0.375rem);
}

.news-body a {
    color: var(--accent);
}

/* Explanatory note under a form field (e.g. "the excerpt is generated"). */
.ge-form-page .field-note {
    margin: 0.25rem 0 0;
    font-size: 0.8125rem;
    color: var(--muted-foreground);
}

/* ---- Product image editor -------------------------------------------------
   Slot rail on the left (front/back), the working stage on the right. The stage
   is deliberately light: these are product shots on a white studio background,
   and judging a crop against the app's dark red would misrepresent them. */

.image-editor {
    display: grid;
    grid-template-columns: 220px minmax(0, 1fr);
    gap: 1.25rem;
    align-items: start;
}

.image-editor-slots {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 1rem;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 0.6rem;
}

.image-slot {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding: 0.5rem;
    background: #fff;
    border: 2px solid transparent;
    border-radius: 0.5rem;
    cursor: pointer;
}

.image-slot.is-selected {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgb(246 169 36 / 0.25);
}

.image-slot img {
    width: 100%;
    aspect-ratio: 1;
    object-fit: contain;
}

.image-slot-label {
    font-size: 0.75rem;
    font-weight: 600;
    color: #5f1214;
}

.image-slot-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    aspect-ratio: 1;
    color: #8a6a2f;
    font-size: 0.75rem;
}

.image-upload {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    height: 2.5rem;
    padding-inline: 0.75rem;
    border: 1px solid var(--input);
    border-radius: 0.45rem;
    cursor: pointer;
    font-size: 0.8125rem;
}

.image-upload input[type="file"] {
    display: none;
}

.image-editor-stage {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    min-width: 0;
}

.image-editor-canvas {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 520px;
    padding: 0.75rem;
    background: #f4f1ec;
    border: 1px solid var(--border);
    border-radius: 0.6rem;
    overflow: hidden;
}

.image-editor-host {
    width: 100%;
}

.image-editor-placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    color: #6b625a;
}

.image-editor-status {
    padding: 0.5rem 0.75rem;
    font-size: 0.8125rem;
    background: rgb(246 169 36 / 0.15);
    border: 1px solid rgb(246 169 36 / 0.4);
    border-radius: 0.45rem;
}

.image-editor-tools {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: center;
    padding: 0.75rem;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 0.6rem;
}

.tool-group {
    display: flex;
    align-items: center;
    gap: 0.35rem;
}

.tool-group-end {
    margin-left: auto;
}

.tool-label {
    margin-right: 0.15rem;
    font-size: 0.75rem;
    color: var(--muted-foreground);
}

.image-editor-hint {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--muted-foreground);
}

.image-shortcut {
    margin-top: 0.5rem;
}

@media (max-width: 900px) {
    .image-editor {
        grid-template-columns: 1fr;
    }

    .image-editor-slots {
        flex-direction: row;
        flex-wrap: wrap;
    }

    .image-slot {
        flex: 1 1 40%;
    }
}

/* URL import block in the image editor's slot rail. */
.image-import {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--border);
}

.image-import label {
    font-size: 0.75rem;
    color: var(--muted-foreground);
}

.image-editor-canvas.is-dragover {
    border-color: var(--accent);
    box-shadow: inset 0 0 0 2px rgb(246 169 36 / 0.5);
}

/* ---- Product preview: the website's own product layout, inside the admin ----
   Light stage and dark ink deliberately: this is what the customer sees, so it
   must not be re-skinned into the admin's dark red. */

.product-preview {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 2rem;
    padding: 1.75rem;
    background: #fbf9f6;
    border: 1px solid var(--border);
    border-radius: 0.75rem;
    color: #2b2118;
}

.product-preview-images {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
    padding: 1.5rem;
    background: #f2ece3;
    border: 1px solid #e4dbcd;
    border-radius: 1rem;
}

.product-preview-images.is-pair {
    grid-template-columns: 1fr 1fr;
}

.product-preview-images img {
    width: 100%;
    aspect-ratio: 1;
    object-fit: contain;
    mix-blend-mode: multiply;
}

.product-preview-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    aspect-ratio: 1;
    justify-content: center;
    color: #8a7f70;
}

.product-preview-eyebrow {
    font-size: 0.75rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: #9a7b45;
}

.product-preview-title {
    margin: 0.5rem 0 0;
    font-family: var(--font-heading);
    font-size: clamp(1.75rem, 3vw, 2.5rem);
    font-weight: 500;
    color: #2b2118;
    text-transform: none;
}

.product-preview-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 1rem;
}

.product-preview .chip {
    padding: 0.375rem 0.75rem;
    border-radius: 999px;
    font-size: 0.78rem;
    font-weight: 600;
    background: #ece5da;
    color: #5c5145;
}

.product-preview .chip-material {
    background: #f3e2e4;
    color: #94011a;
}

/* Buy-back only: the one chip that is a status rather than a classification, so it
   carries the brand gold to stand apart from the material and type chips. */
.product-preview .chip-buyback {
    background: #fbe6c2;
    color: #8a5a00;
}

.product-preview-specs {
    width: 100%;
    margin-top: 1.5rem;
    border-collapse: collapse;
}

.product-preview-specs th,
.product-preview-specs td {
    padding: 0.6rem 0;
    text-align: left;
    border-bottom: 1px solid #e7dfd3;
    font-size: 0.9375rem;
}

.product-preview-specs th {
    width: 40%;
    font-weight: 500;
    color: #7a6f61;
}

.product-preview-slug {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 1.25rem;
    font-size: 0.8125rem;
    color: #7a6f61;
}

.detail-actions {
    display: flex;
    gap: 0.5rem;
}

@media (max-width: 900px) {
    .product-preview {
        grid-template-columns: 1fr;
    }
}

/* Save-size readout in the image editor; turns into a warning past the limit. */
.image-editor-size {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0;
    font-size: 0.8125rem;
    color: var(--muted-foreground);
}

.image-editor-size.is-over {
    color: #ffd977;
    font-weight: 600;
}

/* ---- 13-month percentage dialog -------------------------------------------
   One field in a modal, so it borrows the form pages' note styling rather than
   living under .ge-form-page (dialogs render in a portal, outside that scope). */
/* ---- Select popup scrollbar ------------------------------------------------
   NeoUI's SelectContent puts its options in a `max-h-60 overflow-auto` viewport and
   then hides the scrollbar with [&::-webkit-scrollbar]:hidden. The list does scroll,
   but with no scrollbar there is nothing to say so: the parent-picker holds 28 entries
   and only seven are above the fold, which reads as "items are missing" (Attila,
   2026-08-23). Same thin themed bar the datepicker menu uses. */
[role="listbox"] .overflow-auto {
    scrollbar-width: thin;
    scrollbar-color: rgb(255 255 255 / 0.35) transparent;
}

[role="listbox"] .overflow-auto::-webkit-scrollbar {
    display: block;
    width: 8px;
}

[role="listbox"] .overflow-auto::-webkit-scrollbar-track {
    background: transparent;
}

[role="listbox"] .overflow-auto::-webkit-scrollbar-thumb {
    background: rgb(255 255 255 / 0.3);
    border-radius: 4px;
}

[role="listbox"] .overflow-auto::-webkit-scrollbar-thumb:hover {
    background: rgb(255 255 255 / 0.45);
}

/* ---- Modal icon -----------------------------------------------------------
   NeoUI's DialogService paints its icon bubble straight from the Tailwind palette —
   blue for Info, green for Success, yellow for Warning — none of which belong to a
   red-and-gold brand (Attila, 2026-08-23: "instead of the blue icon in all modals, we
   should use our gold color"). Destructive already resolves to gold through
   --destructive, so pointing the rest at --accent puts every modal on one colour.

   The bubble is matched by its shape classes because that is all the markup gives us;
   three classes plus the role attribute outrank Tailwind's single-class utilities. */
[role="alertdialog"] .h-10.w-10.rounded-full,
[role="dialog"] .h-10.w-10.rounded-full {
    background-color: color-mix(in oklab, var(--accent) 12%, transparent);
}

[role="alertdialog"] .h-10.w-10.rounded-full > svg,
[role="dialog"] .h-10.w-10.rounded-full > svg {
    color: var(--accent);
}

/* Small single-field dialogs (13-month percentage, client move). The note and error
   lines cannot borrow .ge-form-page's, since a dialog renders in a portal outside it. */
.ge-form-dialog {
    /* Keeps a margin on a phone instead of running edge to edge. */
    max-width: min(26rem, calc(100vw - 2rem));
}

.ge-form-dialog .dialog-note,
.ge-form-dialog .dialog-error {
    margin: 0.25rem 0 0;
    font-size: 0.8125rem;
}

.ge-form-dialog .dialog-note {
    color: var(--muted-foreground);
}

.ge-form-dialog .dialog-error {
    color: var(--destructive);
}

/* Company names run long, so the parent picker gets more room. */
.move-dialog {
    max-width: min(32rem, calc(100vw - 2rem));
}

/* ---- Report screens (havi / negyedéves elszámolás) -------------------------
   Global rather than component-scoped: two pages share them, and scoped CSS does not
   reach past the component it belongs to. */
/* Filter bar: the two selectors, Lekérdezés and the export form on one baseline. */

.report-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.75rem;
    margin-bottom: 1.25rem;
}

.report-field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 11rem;
}

.report-label {
    font-size: 0.8125rem;
    color: var(--muted-foreground);
}

/* The export button posts a real form, so it is a form rather than a Button — this
   keeps it on the same line as the others instead of starting a block of its own. */
.report-export {
    display: inline-flex;
    margin: 0;
}

.report-empty,
.report-count {
    color: var(--muted-foreground);
}

.report-table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 2.5rem;
}

/* Tabular figures wherever digits stack in columns. Inter ships proper tabular numerals,
   which is most of why it suits this app: contract numbers, dates and money all line up
   digit for digit instead of drifting with the width of a 1. Applied to the tables rather
   than globally, because proportional figures read better in running prose. */
.report-table,
.ge-datagrid table,
.doc-admin-table {
    font-variant-numeric: var(--font-numeric);
}

.report-table th,
.report-table td {
    padding: 0.35rem 0.75rem 0.35rem 0;
    text-align: left;
    vertical-align: top;
}

.report-table thead th {
    border-bottom: 1px solid rgb(255 255 255 / 0.2);
    white-space: nowrap;
}

.report-table tbody tr:nth-child(even) {
    background: rgb(255 255 255 / 0.04);
}

.report-number {
    text-align: right;
    white-space: nowrap;
}

/* A product retired from sale (Contracts:DisallowedProductIds). It stays in the list
   because buy-backs and portfolios still need it; the badge says why it is not offered. */
.product-name-cell {
    display: inline-flex;
    align-items: baseline;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.product-retired {
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    background: rgb(255 255 255 / 0.12);
    color: var(--accent);
}

/* ---- Pending user badge ----------------------------------------------------
   A struck-through username says a partner or sales person cannot log in yet, but not
   WHY (Attila, 2026-08-24). Admin activation moves PendingActivation -> Inactive, never
   straight to Active, so there are two distinct waits and the badge names the one that
   applies; the title attribute carries the full explanation. */
.user-state-cell {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
}

.user-state-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    font-size: 0.68rem;
    white-space: nowrap;
    background: rgb(255 255 255 / 0.12);
    color: var(--accent);
    cursor: help;
}

/* ---- Document catalog admin (/maintenance/documents) ---------------------- */

.doc-admin-note {
    max-width: 60ch;
    margin: -0.5rem 0 1.25rem;
    color: var(--muted-foreground);
}

.doc-admin-table code {
    font-size: 0.8125rem;
    color: var(--muted-foreground);
}

/* One column per partner type: the availability tick, an upload control and a withdraw
   button share the cell, so they sit on one baseline rather than stacking. */
.doc-admin-availability {
    white-space: nowrap;
    text-align: center;
}

.doc-admin-availability > * {
    vertical-align: middle;
}

.doc-admin-yes {
    color: var(--accent);
}

.doc-admin-no {
    color: var(--muted-foreground);
}

/* The real <input type=file> is unusable as-is; the label is the button and the input
   inside it is hidden but still reachable by keyboard through the label. Used in the
   "Fájlok kezelése" dialog — the grid's partner-type columns are status only. */
.doc-admin-upload {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.75rem;
    border: 1px solid var(--accent);
    border-radius: var(--radius, 0.375rem);
    color: var(--accent);
    cursor: pointer;
    font-size: 0.875rem;
}

.doc-admin-upload:hover {
    background: rgb(255 255 255 / 0.08);
}

.doc-admin-upload input[type="file"] {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

/* One partner type's row inside the "Fájlok kezelése" dialog. */
.doc-admin-file-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.6rem 0;
    border-bottom: 1px solid var(--border);
}

.doc-admin-file-row:last-of-type {
    border-bottom: 0;
}

.doc-admin-file-name {
    flex: 1;
    min-width: 0;
}

.doc-admin-file-state {
    font-size: 0.8125rem;
    color: var(--muted-foreground);
}

/* A document with no file behind it is invisible to every user, which is worth shouting
   about on the row rather than leaving to be inferred from empty columns. */
.doc-admin-orphan {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    margin-left: 0.5rem;
    font-size: 0.8125rem;
    color: var(--destructive);
}
