
/* Responsive Design */
@media (max-width: 640px) {
  .desktop-only {
    display: none !important;
  }
  .tablet-only {
    display: none !important;
  }
  .mobile-hidden {
    display: none !important;
  }
}
@media (min-width: 640px) and (max-width: 1024px) {
  .mobile-only{
    display: none !important;
  }
  .desktop-only {
    display: none !important;
  }
  .tablet-hidden {
    display: none !important;
  }
}
@media (min-width: 1025px) {
  .mobile-only {
    display: none !important;
  }
  .tablet-only {
    display: none !important;
  }
  .desktop-hidden {
    display: none !important;
  }
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* Drag-to-scroll cursor helpers for desktop: only show grab if horizontally scrollable */
.table-wrapper.h-scrollable {
  cursor: grab;
}
.table-wrapper.h-scrollable.drag-scrolling {
  cursor: grabbing !important;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
    font-size: var(--fs-xs);
    color: #374151; /* text-gray-700 */
    padding: 0.125rem 0.8rem; /* px-1 py-0.5 */
    border-radius: 0.8rem; /* rounded */
    border: 1px solid #e8e8e8;
    background: #f7f7f7;
}

/* Canonical form-control surface — the single source of truth for input styling. `.prefixed-input`
   (a div wrapping a grayed prefix + a borderless input; see Components/Shared/PrefixedTextInput) opts
   into the identical look by joining these selectors, so these values live in exactly one place. */
input, textarea, select, .prefixed-input {
    width: 100%;
    padding: 10px 12px;
    font-weight: normal;
    font-size: var(--fs-sm);
    outline: none;
    border-radius: 10px;
    background: #eef3f8;
    border: 1px solid transparent;
    transition: all 0.2s;
    box-sizing: border-box;
}
input:focus, textarea:focus, select:focus, .prefixed-input:focus-within {
    background: #fff;
    border-color: color-mix(in srgb, var(--accent-color) 45%, transparent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-color) 16%, transparent);
}
input[disabled], textarea[disabled], select[disabled], .prefixed-input.disabled {
    background: #e9edf2;
}
input::placeholder, textarea::placeholder {
    color: #8695a5;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input[type="number"] {
    appearance: textfield;
}
input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 2.75rem;
  height: 1.5rem;
  border-radius: 999px;
  background: #d1d5db;
  border: 1px solid #c7cdd6;
  position: relative;
  cursor: pointer;
  transition: background-color 0.2s ease, border-color 0.2s ease;
  padding: 0;
}

input[type="checkbox"]::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 2px;
  width: 1.2rem;
  height: 1.2rem;
  border-radius: 50%;
  background: #fff;
  transform: translateY(-50%);
  transition: transform 0.2s ease;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}

input[type="checkbox"]:checked {
  background: var(--primary-color);
  border-color: var(--primary-color);
}

input[type="checkbox"]:checked::before {
  transform: translate(1.2rem, -50%);
}

/* Teilauswahl: der Knopf steht mittig, die Bahn ist nur angefärbt. Ein echtes `indeterminate` ginge
   nur über JS — es ist eine DOM-Eigenschaft, kein Attribut; die Klasse kommt ohne Interop aus, und
   aria-checked="mixed" sagt Hilfsmitteln dasselbe. */
input[type="checkbox"].is-partial {
  background: color-mix(in srgb, var(--primary-color) 45%, #d1d5db);
  border-color: color-mix(in srgb, var(--primary-color) 45%, #c7cdd6);
}

/* Halb so weit wie :checked (1.2rem) — der Knopf landet in der Mitte der Bahn. */
input[type="checkbox"].is-partial::before {
  transform: translate(0.6rem, -50%);
}

input[type="checkbox"]:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.filterbg {
    position: absolute;
    z-index: -1;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    content: '';
    background: linear-gradient(100deg, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0) 100%);
    box-shadow: 0px -10px 10px inset rgba(0, 0, 0, 0.1);
}

html.sidebar-open,
body.sidebar-open {
    overflow: hidden;
    overscroll-behavior: none;
}

/* ── Navigation progress ────────────────────────────────────────────────────────────────────────────
   Driven by wwwroot/js/nav-progress.js, hosted in App.razor outside <Routes> so the per-navigation
   teardown of the whole .app subtree cannot take it with it. Global rather than scoped for the same
   reason — there is no component to scope it to. */
#nav-progress {
    position: fixed;
    /* Below the app header, not across it. The bar's gradient and the header's own fill are close
       enough in colour that three pixels drawn on top of it barely register. --app-header-h is
       published by sidebar.js from the live header box, so this tracks it — including the header
       growing or wrapping. Where the header is hidden it measures 0 (every mobile page but the home
       page), which leaves the bar at the very top edge there, exactly as before. */
    top: var(--app-header-h, 0px);
    left: 0;
    right: 0;
    height: 3px;
    z-index: 9998; /* under toasts (9999), over the error UI's neighbours */
    pointer-events: none;
}

.nav-progress-bar {
    height: 100%;
    width: 100%;
    transform: scaleX(0);
    transform-origin: 0 50%;
    background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
    box-shadow: 0 0 8px rgba(13, 108, 172, 0.5);
    /* Matches the trickle interval so the creep reads as continuous motion rather than steps. */
    transition: transform 200ms ease-out;
}

/* The final jump to 100% is quick, then the whole bar fades rather than snapping away. */
.nav-progress-bar.is-done {
    transition: transform 120ms ease-out, opacity 220ms ease 120ms;
    opacity: 0;
}

/* Dimming the outgoing page says "this content is stale" and, just as usefully, stops a second link
   being clicked while the first navigation is still in flight. Applied ~150ms in, so a fast response
   never flashes it. */
.app[data-navigating] .main {
    opacity: 0.6;
    pointer-events: none;
    transition: opacity 180ms ease;
}

@media (prefers-reduced-motion: reduce) {
    .nav-progress-bar {
        transition: none;
    }

    .app[data-navigating] .main {
        transition: none;
    }
}

/* ── Dialog placeholder shell ───────────────────────────────────────────────────────────────────────
   Painted by wwwroot/js/dialog-shell.js in the same frame as the click, and torn down the moment the
   real DialogInstance renders. Global rather than scoped: the host div lives in App.razor outside the
   Blazor root (so it survives the routed subtree being rebuilt), which scoped CSS cannot reach.
   Values are kept in step with DialogInstance.razor.css and LoadingText.razor.css so the handover to
   the real dialog is not a visible jump. */
body.dialog-shell-open {
    overflow: hidden;
}

/* Exactly DialogInstance's .modal-backdrop values. The real backdrop renders on top of this one for
   the frame before the observer tears it down, so any difference in colour would show as a flicker. */
.dialog-shell-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.35);
    z-index: 500;
    width: 100vw;
    animation: dialog-fadein 0.2s;
}

/* Handing over to the real dialog: fade out across the same 200ms its own backdrop fades in, so the
   page never brightens between the two scrims. Clicks stop landing here at once — the real dialog is
   already arriving and must own them. */
#dialog-shell[data-closing] .dialog-shell-backdrop {
    animation: none;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease;
}

@keyframes dialog-fadein {
    from { opacity: 0; }
    to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .dialog-shell-backdrop {
        animation: none;
    }

    #dialog-shell[data-closing] .dialog-shell-backdrop {
        transition: none;
    }
}
/* ── Import / Export ────────────────────────────────────────────────────────────────────────────────
   Global rather than scoped: the export tree, the import review and the conflict diff are four
   components that share one visual language, and Blazor's scoped CSS does not cross component
   boundaries. */

.exchange-body {
    padding: 1rem 1.25rem;
    overflow-y: auto;
    max-height: 60dvh;
}

.exchange-hint {
    margin: 0 0 1rem;
    color: var(--text-secondary, #555);
    font-size: 0.9rem;
    line-height: 1.5;
}

.exchange-footer {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    padding: 0.75rem 1.25rem;
    border-top: 1px solid var(--border-color, #e3e3e3);
}

.exchange-footer-actions {
    display: flex;
    gap: 0.5rem;
    margin-left: auto;
}

.exchange-toggle {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.875rem;
    color: var(--text-secondary, #555);
    cursor: pointer;
}

/* Relation tree */

.relation-tree {
    list-style: none;
    margin: 0;
    padding: 0;
}

.relation-tree.nested {
    /* A rail rather than deep indentation: nesting stays readable without running out of width. */
    margin-left: 0.6rem;
    padding-left: 0.9rem;
    border-left: 2px solid var(--border-color, #e3e3e3);
}

.relation-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.35rem 0.25rem;
    border-radius: var(--border-radius, 6px);
    cursor: pointer;
}

.relation-row:hover {
    background: var(--hover-color, rgba(0, 0, 0, 0.04));
}

.relation-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.relation-count {
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    font-size: 0.8rem;
    color: var(--text-secondary, #666);
    background: var(--surface-alt, rgba(0, 0, 0, 0.05));
    border-radius: 999px;
    padding: 0.05rem 0.5rem;
}

.relation-tag {
    flex: 0 0 auto;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-secondary, #666);
    border: 1px solid var(--border-color, #ddd);
    border-radius: 999px;
    padding: 0 0.4rem;
}

.relation-group-toggle {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.35rem 0.25rem;
    background: none;
    border: 0;
    font: inherit;
    color: var(--text-secondary, #555);
    cursor: pointer;
}

.relation-loading {
    padding: 0.25rem 0 0.25rem 1.5rem;
}

/* Import picker */

.import-drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    padding: 2rem 1rem;
    border: 2px dashed var(--border-color, #d5d5d5);
    border-radius: var(--border-radius, 8px);
    color: var(--text-secondary, #666);
    cursor: pointer;
    text-align: center;
}

.import-drop i {
    font-size: 1.75rem;
}

.import-error {
    margin-top: 0.75rem;
    color: var(--danger-color, #b3261e);
    font-size: 0.875rem;
}

.import-success {
    color: var(--success-color, #2e7d32);
    font-weight: 500;
}

/* Import review */

.import-summary,
.import-bulk {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-bottom: 0.75rem;
}

.import-bulk {
    font-size: 0.875rem;
    color: var(--text-secondary, #555);
}

.import-chip {
    font-size: 0.78rem;
    border-radius: 999px;
    padding: 0.1rem 0.6rem;
    white-space: nowrap;
}

.import-chip.new       { background: rgba(46, 125, 50, 0.12);  color: #2e7d32; }
.import-chip.unchanged { background: rgba(0, 0, 0, 0.06);      color: #555; }
.import-chip.conflict  { background: rgba(237, 108, 2, 0.14);  color: #b35400; }
.import-chip.blocked   { background: rgba(179, 38, 30, 0.12);  color: #b3261e; }

.import-warning {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    font-size: 0.85rem;
    color: #b35400;
    margin: 0 0 0.5rem;
}

.import-group {
    border: 1px solid var(--border-color, #e3e3e3);
    border-radius: var(--border-radius, 8px);
    margin-bottom: 0.6rem;
    overflow: hidden;
}

.import-group-header {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    padding: 0.55rem 0.75rem;
    background: var(--surface-alt, rgba(0, 0, 0, 0.02));
    border: 0;
    font: inherit;
    cursor: pointer;
    text-align: left;
}

.import-group-name {
    font-weight: 500;
}

.import-group-counts {
    display: flex;
    gap: 0.35rem;
    margin-left: auto;
    flex-wrap: wrap;
}

.import-rows {
    list-style: none;
    margin: 0;
    padding: 0;
}

.import-row {
    border-top: 1px solid var(--border-color, #eee);
    padding: 0.4rem 0.75rem;
}

.import-row-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.import-row-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.import-row-reason {
    font-size: 0.8rem;
    color: #b3261e;
}

.import-row-toggle,
.import-row-bullet {
    width: 1.25rem;
    flex: 0 0 auto;
    background: none;
    border: 0;
    color: var(--text-secondary, #777);
    cursor: pointer;
}

.import-row-empty {
    padding: 0.6rem 0.75rem;
    font-size: 0.85rem;
    color: var(--text-secondary, #666);
}

.import-failures,
.import-stats {
    margin: 0.5rem 0 0;
    padding-left: 1.1rem;
    font-size: 0.875rem;
}

/* Conflict diff */

.conflict-diff {
    width: 100%;
    margin: 0.5rem 0 0.25rem;
    border-collapse: collapse;
    font-size: 0.85rem;
    /* Wide content scrolls inside its own box rather than pushing the dialog sideways. */
    display: block;
    overflow-x: auto;
}

.conflict-diff th,
.conflict-diff td {
    padding: 0.3rem 0.5rem;
    text-align: left;
    border-bottom: 1px solid var(--border-color, #eee);
    vertical-align: top;
}

.conflict-diff th {
    font-weight: 500;
    color: var(--text-secondary, #666);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.conflict-field  { width: 26%; }
.conflict-choice { width: 6rem; text-align: center; }

/* The struck-through side is the one being discarded, so the choice reads at a glance. */
.conflict-diff tr.takes-incoming .conflict-current {
    text-decoration: line-through;
    color: var(--text-secondary, #999);
}

.conflict-diff tr.keeps-current .conflict-incoming {
    text-decoration: line-through;
    color: var(--text-secondary, #999);
}

/* Settings blocks */

.settings-block {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    padding: 1rem 0;
    border-bottom: 1px solid var(--border-color, #eee);
}

.settings-block-text {
    flex: 1 1 auto;
}

.settings-block-text h3 {
    margin: 0 0 0.25rem;
    font-size: 0.95rem;
}

.settings-block-text p {
    margin: 0;
    font-size: 0.875rem;
    color: var(--text-secondary, #555);
    line-height: 1.5;
}
