/* =============================================================
   Charter adapter.

   This file used to define its own palette, including an --eona-*
   namespace that collided with the real design system on
   --eona-font-family and quietly replaced the charter's Poppins with
   Manrope. The palette now comes from eona-ui-toolkit, which mirrors
   eona-design-system's brand/tokens.css byte for byte (written to
   styles/vendor/ by build.rs, linked from index.html).

   What is left here is an adapter: the generic, shadcn-style names the
   app's own rules already use, bound to charter tokens. Two consequences
   worth knowing:

   - There are no light/dark blocks any more. These bind to the charter's
     semantic layer (--eona-surface-*, --eona-text-*, --eona-accent),
     which switches on [data-theme] in tokens.css, so both themes follow
     from one set of bindings. index.html sets data-theme="dark" to keep
     this app dark-first.
   - Nothing here defines an --eona-* name. That namespace belongs to the
     design system; app-local values use --eovoc-*.
   ============================================================= */

:root {
    /* App-local surfaces the charter has no token for: gradients, built
       from charter colours so they still move with the theme. */
    --eovoc-bg-page: linear-gradient(180deg, var(--eona-surface-page) 0%, var(--eona-surface-sunken) 100%);
    --eovoc-bg-header: linear-gradient(90deg, var(--eona-surface-raised) 0%, var(--eona-surface-sunken) 100%);
    --eovoc-shadow-md: var(--eona-shadow-surface);

    --background: var(--eona-surface-page);
    --foreground: var(--eona-text-primary);

    --card: var(--eona-surface-raised);
    --card-foreground: var(--eona-text-primary);

    --popover: var(--eona-surface-raised);
    --popover-foreground: var(--eona-text-primary);

    --primary: var(--eona-blue-action);
    --primary-foreground: var(--eona-white);

    --secondary: var(--eona-surface-sunken);
    --secondary-foreground: var(--eona-text-primary);

    --tertiary: var(--eona-surface-sunken);
    --tertiary-foreground: var(--eona-text-primary);

    --link: var(--eona-accent);
    --link-foreground: var(--eona-blue-royal);

    --muted: var(--eona-surface-sunken);
    --muted-foreground: var(--eona-text-muted);

    --accent: var(--eona-accent);
    --accent-foreground: var(--eona-accent-contrast);

    --destructive: var(--eona-error);
    --destructive-foreground: var(--eona-white);

    --border: var(--eona-border-subtle);
    --input: var(--eona-border-strong);
    --ring: var(--eona-accent);

    /* Diagram/badge hues. Mapped onto charter colours so a crosswalk
       diagram and a term badge draw from the same palette as everything
       else — see the toolkit's ontology.css for the badge half. */
    --chart-1: var(--eona-blue-action);
    --chart-2: var(--eona-blue-royal);
    --chart-3: var(--eona-lavender);
    --chart-4: var(--eona-lavender-deep);
    --chart-5: var(--eona-success);

    --radius: var(--eona-radius-sm);
}

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    min-height: 100vh;
    font-family: var(--eona-font-family);
    background: var(--eovoc-bg-page);
    background-attachment: fixed;
    color: var(--foreground);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

a {
    color: var(--link);
    text-decoration: none;
    transition: color 0.2s ease;
}

a:hover {
    color: var(--link-foreground);
}

.app-header {
    padding: 1rem;
    background: var(--eovoc-bg-header);
    box-shadow: var(--eovoc-shadow-md);
}

.term-header__title {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--foreground);
}

.term-header__title--deprecated {
    text-decoration: line-through;
    opacity: 0.7;
}

/* =============================================================
   ORGANISM · crosswalk-selector (ports CrosswalkSelector.vue)

   Reuses .ontology-selector's card/label/row/downloads/description rules
   (same component family); these two are crosswalk-specific: the plain-text
   title shown instead of a <select> when there's only one crosswalk, and the
   "SideA <-> SideB" line under the row.
   ============================================================= */

.crosswalk-selector__title {
    margin: 0;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--foreground);
}

@media (min-width: 640px) {
    .crosswalk-selector__title {
        flex: 1 1 auto;
    }
}

.crosswalk-selector__sides {
    margin: 0.75rem 0 0;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--muted-foreground);
}

/* =============================================================
   PAGE · ontologies (ports pages/ontologies.vue)

   The page shell: a centred, max-width column (`container mx-auto
   max-w-5xl px-4 py-8` in the Vue source), its intro header, the
   selector's own margin-bottom, and the shared loading/fatal-error
   "state" card reused for both the manifest and the per-document fetch.
   ============================================================= */

.eovoc-page {
    box-sizing: content-box;
    max-width: 64rem;
    margin: 0 auto;
    padding: 2rem 1rem;
}

/* =============================================================
   ORGANISM · Navbar (components::organisms::navbar)

   Shared top bar for every route — see main.rs's routing and each
   page's own top-level markup. Sits inside the page's own max-width
   column (same as the crosswalk-page__nav bare links it replaces), not
   full-bleed across the viewport.
   ============================================================= */

.eovoc-navbar {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    margin: -0.5rem -1rem 1.5rem;
    padding: 0.75rem 1rem;
    border-bottom: 1px solid var(--border);
}

.eovoc-navbar__brand {
    font-weight: 700;
    font-size: 0.9375rem;
    color: var(--foreground);
    white-space: nowrap;
}

.eovoc-navbar__links {
    display: flex;
    flex: 1;
    gap: 1.25rem;
    font-size: 0.8125rem;
}

.eovoc-navbar__link {
    font-weight: 500;
    color: var(--muted-foreground);
}

.eovoc-navbar__link--active {
    color: var(--accent);
}

.eovoc-page__header {
    margin-bottom: 1.5rem;
}

.eovoc-page__title {
    margin: 0;
    font-size: 1.875rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--foreground);
}

.eovoc-page__lede {
    margin: 0.5rem 0 0;
    max-width: 42rem;
    font-size: 0.875rem;
    line-height: 1.625;
    color: var(--muted-foreground);
}

.eovoc-page__selector {
    margin-bottom: 1.5rem;
}

.eovoc-state {
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--card);
    padding: 2rem;
    text-align: center;
    font-size: 0.875rem;
    color: var(--muted-foreground);
}

.eovoc-state--error {
    border-color: color-mix(in srgb, var(--destructive) 40%, transparent);
    padding: 1.5rem;
    text-align: left;
}

.eovoc-state__title {
    margin: 0;
    font-weight: 600;
    color: var(--destructive);
}

.eovoc-state__message {
    margin: 0.25rem 0 0;
    color: var(--muted-foreground);
}

/* =============================================================
   MOLECULE · onto-uml-class (ports OntoUmlClass.vue)

   One UML class box inside the diagram SVG. The box fill is always one of
   the light per-prefix pastels computed by uml.rs, so its own text/divider
   stay a fixed dark colour (not theme tokens) to read on that fill in both
   the light and dark diagram canvas — only the interactive-state strokes
   (hover/active/focus outline, the expand badge) come from theme tokens, so
   they still track the app's accent colour in both themes. The source's
   scoped-style var(--color-accent)/var(--color-ring)/var(--color-card)
   are this app's existing --accent/--ring/--card tokens (see ground rules),
   not a second parallel set.
   ============================================================= */

.onto-uml-class {
    cursor: pointer;
}

.onto-uml-class .uml-box {
    stroke: rgba(15, 23, 42, 0.35);
    transition: stroke 0.1s ease, stroke-width 0.1s ease;
}

/* External reference node (see uml.rs's `external_reference`): a term this
   ontology relates to but doesn't define itself — e.g. the W3C ODRL
   vocabulary's own Action/LeftOperand classes a profile extends. Dashed
   border marks "defined elsewhere"; its "open" icon (below) links out to
   that vocabulary's own reference documentation instead of an in-page
   anchor, since there is none here. */
.onto-uml-class.is-external .uml-box {
    stroke-dasharray: 5 3;
    fill-opacity: 0.55;
}

.onto-uml-class .uml-name,
.onto-uml-class .uml-attr {
    fill: #1e293b;
}

.onto-uml-class .uml-divider {
    stroke: rgba(15, 23, 42, 0.2);
}

.onto-uml-class .uml-more {
    fill: rgba(30, 41, 59, 0.7);
    font-style: italic;
}

/* Member row (see uml.rs's membership pass): an individual belonging to
   this class without a diagram box of its own, e.g. one of a grouping
   class's grouped terms, or one instance of an external reference class.
   Styled distinctly from a plain attribute (no "name: type" shape, so
   italicising it signals "this is one of, not has one"). */
.onto-uml-class .uml-member {
    font-style: italic;
    opacity: 0.85;
}

.onto-uml-class:hover .uml-box,
.onto-uml-class.is-related .uml-box {
    stroke: var(--accent);
}

.onto-uml-class.is-active .uml-box {
    stroke: var(--accent);
    stroke-width: 2;
}

.onto-uml-class:focus-visible {
    outline: none;
}

.onto-uml-class:focus-visible .uml-box {
    stroke: var(--ring);
    stroke-width: 2;
}

.onto-uml-class.is-dimmed {
    opacity: 0.4;
}

.onto-uml-class .uml-open {
    color: rgba(15, 23, 42, 0.5);
    cursor: pointer;
}

.onto-uml-class .uml-open:hover {
    color: #1e293b;
}

.onto-uml-class .uml-expand {
    cursor: pointer;
}

.onto-uml-class .uml-expand-badge {
    fill: var(--card);
    stroke: var(--accent);
    stroke-width: 1.5;
    transition: fill 0.1s ease;
}

.onto-uml-class .uml-expand:hover .uml-expand-badge,
.onto-uml-class .uml-expand:focus-visible .uml-expand-badge {
    fill: var(--accent);
}

.onto-uml-class .uml-expand-label {
    fill: var(--accent);
    pointer-events: none;
    transition: fill 0.1s ease;
}

.onto-uml-class .uml-expand:hover .uml-expand-label,
.onto-uml-class .uml-expand:focus-visible .uml-expand-label {
    fill: var(--card);
}

.onto-uml-class .uml-expand:focus-visible {
    outline: none;
}

/* =============================================================
   MOLECULE · onto-uml-edge (ports OntoUmlEdge.vue)

   One relationship line (generalization/association) in the diagram SVG,
   plus its optional centred label. `.edge-label-bg` stands in for the
   source's `fill-background` Tailwind utility on the label's backing rect
   (a plain background-token fill keeping the label legible over crossing
   lines) — this build has no Tailwind pipeline to interpret that utility.
   ============================================================= */

.onto-uml-edge .edge-line {
    stroke: var(--muted-foreground);
    transition: stroke 0.1s ease, stroke-width 0.1s ease;
}

.onto-uml-edge .edge-label {
    fill: var(--muted-foreground);
}

.onto-uml-edge .edge-label-bg {
    fill: var(--background);
}

.onto-uml-edge:hover .edge-line,
.onto-uml-edge.is-active .edge-line {
    stroke: var(--accent);
    stroke-width: 2;
}

.onto-uml-edge:hover .edge-label,
.onto-uml-edge.is-active .edge-label {
    fill: var(--accent);
}

.onto-uml-edge.is-dimmed {
    opacity: 0.22;
}

/* =============================================================
   MOLECULE · onto-uml-card (ports OntoUmlCard.vue)

   The info panel shown while a class is selected in the diagram, merged
   into the selected class's own box: a fixed header and a scrollable body
   of foldable sections, sized to fill its container (`--fill`, the only
   presentation this port has — see uml_card.rs's module doc comment for the
   floating/draggable variant this app used to also have).
   ============================================================= */

.onto-uml-card {
    width: 41.6em;
    max-width: calc(100vw - 2rem);
    overflow: hidden;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--popover);
    color: var(--popover-foreground);
    text-align: left;
    /* Tailwind's `shadow-xl` doesn't vary by theme either (no `dark:` swap
       in the source) — a fixed, fairly dark shadow reads fine as an
       elevated surface against either canvas. */
    box-shadow: 0 16px 40px rgba(2, 6, 23, 0.45);
}

.onto-uml-card--fill {
    display: flex;
    width: 100%;
    height: 100%;
    flex-direction: column;
}

.onto-uml-card__header {
    padding: 0.75rem 0.75rem 0.5rem;
}

.onto-uml-card__header-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.onto-uml-card__swatch {
    height: 0.75rem;
    width: 0.75rem;
    flex-shrink: 0;
    border-radius: 0.15rem;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
}

.onto-uml-card__name {
    margin: 0;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--foreground);
}

.onto-uml-card__iri {
    margin: 0.125rem 0 0;
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.6875rem;
    color: var(--muted-foreground);
    overflow-wrap: anywhere;
}

.onto-uml-card__external {
    display: inline-block;
    margin: 0.25rem 0 0;
    font-size: 0.6875rem;
    color: var(--accent);
    text-decoration: none;
}

.onto-uml-card__external:hover {
    text-decoration: underline;
}

.onto-uml-card__body {
    overflow-y: auto;
    padding: 0 0.75rem 0.75rem;
    /* Fallback for browsers without the `lh` unit; the literal 13-line-height
       cap below overrides it wherever `lh` is supported. */
    max-height: 20rem;
    max-height: 13lh;
}

.onto-uml-card__body--fill {
    min-height: 0;
    max-height: none;
    flex: 1;
}

.onto-uml-card__description {
    margin: 0;
    white-space: pre-line;
    font-size: 0.75rem;
    line-height: 1.625;
    color: var(--foreground);
    opacity: 0.9;
}

.onto-uml-card__section {
    margin-top: 0.5rem;
    border-top: 1px solid var(--border);
    padding-top: 0.5rem;
}

.onto-uml-card__section-toggle {
    display: flex;
    width: 100%;
    align-items: center;
    gap: 0.25rem;
    border: none;
    background: none;
    padding: 0;
    font: inherit;
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--muted-foreground);
    cursor: pointer;
}

.onto-uml-card__section-toggle:hover {
    color: var(--foreground);
}

.onto-uml-card__section-toggle:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--ring);
}

.onto-uml-card__chevron {
    flex-shrink: 0;
    transition: transform 0.2s ease;
}

.onto-uml-card__chevron--open {
    transform: rotate(90deg);
}

.onto-uml-card__tags {
    margin-top: 0.375rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
}

.onto-uml-card__tag {
    display: inline-flex;
    align-items: center;
    border-radius: 9999px;
    border: 1px solid var(--border);
    background: none;
    padding: 0.125rem 0.5rem;
    font: inherit;
    font-size: 0.6875rem;
    color: var(--link);
    cursor: pointer;
}

.onto-uml-card__tag:hover {
    border-color: var(--accent);
    color: var(--link-foreground);
}

.onto-uml-card__tag:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--ring);
}

.onto-uml-card__list {
    margin: 0.25rem 0 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}

.onto-uml-card__list-item {
    font-size: 0.75rem;
    color: var(--foreground);
    opacity: 0.9;
}

.onto-uml-card__list-name {
    font-weight: 500;
}

.onto-uml-card__list-muted {
    color: var(--muted-foreground);
}

.onto-uml-card__empty {
    margin: 0;
    border-top: 1px solid var(--border);
    padding-top: 0.5rem;
    font-size: 0.75rem;
    font-style: italic;
    color: var(--muted-foreground);
}

/* =============================================================
   ORGANISM · onto-uml-diagram (ports OntoUmlDiagram.vue)

   The class-diagram card: a collapsible header (chevron + title + count
   pill, same visual language as onto-section), a static line-style legend,
   an optional prefix colour legend, group-hierarchy/zoom view controls, and
   the scrollable SVG canvas itself. Full-bleed like onto-header/onto-filter
   are NOT — this one deliberately breaks out to the viewport's full width
   (mirrors the source's `w-screen` + negative-margin trick), since large
   diagrams benefit from more room than the page's own reading-width column.
   Composes onto-uml-class/-edge/-card (see their own blocks above).
   ============================================================= */

.onto-uml-diagram {
    position: relative;
    width: 100vw;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--card);
    padding: 1rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
}

@media (min-width: 640px) {
    .onto-uml-diagram {
        padding: 1.25rem;
    }
}

/* Full-viewport mode: the whole card becomes a fixed, full-window overlay
   instead of the page's normal content width. `.onto-uml-diagram`'s own
   background (--card) is a deliberately near-transparent tint meant to sit
   as a subtle card ON TOP of the page's own gradient — correct for the
   normal in-flow card, but wrong here: this element leaves normal flow
   (position: fixed), so the rest of the page reflows underneath it and its
   own header/filter/TOC controls end up occupying the same screen area,
   ghosting through the ~5%-opaque card background. Fullscreen needs to
   fully occlude the page it's covering, so it gets its own opaque
   background instead — the same page-level gradient `body` itself uses
   (--eona-bg-page), so the diagram reads as if it temporarily became the
   whole page, not a translucent panel floating over a jumbled page behind it. */
.onto-uml-diagram--fullscreen {
    position: fixed;
    inset: 0;
    z-index: 50;
    width: 100vw;
    height: 100vh;
    margin: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--eovoc-bg-page);
}

.onto-uml-diagram__toggle {
    display: flex;
    width: 100%;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    border: none;
    background: none;
    padding: 0;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
}

.onto-uml-diagram__toggle:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--ring);
}

.onto-uml-diagram__toggle-left {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.onto-uml-diagram__chevron {
    flex-shrink: 0;
    color: var(--muted-foreground);
    transition: transform 0.2s ease;
}

.onto-uml-diagram__chevron--open {
    transform: rotate(90deg);
}

.onto-uml-diagram__title {
    margin: 0;
    font-size: 1.125rem;
    font-weight: 600;
    color: var(--foreground);
}

.onto-uml-diagram__count {
    flex-shrink: 0;
    border-radius: 9999px;
    background: var(--muted);
    padding: 0.125rem 0.5rem;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--muted-foreground);
}

.onto-uml-diagram__body {
    margin-top: 0.75rem;
}

.onto-uml-diagram__body--fullscreen {
    display: flex;
    min-height: 0;
    flex: 1 1 auto;
    flex-direction: column;
}

/* ---- legend --------------------------------------------------------- */

.onto-uml-diagram__legend {
    margin-bottom: 0.75rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 1.25rem;
    font-size: 0.75rem;
    color: var(--muted-foreground);
}

.onto-uml-diagram__legend-item {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
}

.onto-uml-diagram__legend-line {
    stroke: var(--muted-foreground);
}

.onto-uml-diagram__legend-tri {
    fill: var(--card);
    stroke: var(--muted-foreground);
}

.onto-uml-diagram__legend-muted {
    color: color-mix(in srgb, var(--muted-foreground) 70%, transparent);
}

/* ---- prefix colour legend --------------------------------------------- */

.onto-uml-diagram__prefixes {
    margin-bottom: 0.75rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 1rem;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    padding: 0.5rem 0.75rem;
    font-size: 0.75rem;
    color: var(--muted-foreground);
}

.onto-uml-diagram__prefixes-label {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}

.onto-uml-diagram__prefixes-title {
    font-weight: 500;
}

.onto-uml-diagram__prefix {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
}

.onto-uml-diagram__prefix-swatch {
    display: inline-block;
    height: 0.75rem;
    width: 0.75rem;
    flex-shrink: 0;
    border-radius: 0.15rem;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
}

.onto-uml-diagram__prefix-link {
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.75rem;
    color: var(--link);
}

.onto-uml-diagram__prefix-link:hover {
    color: var(--link-foreground);
}

/* ---- help tooltip circle, shared by every "?" affordance -------------- */

.onto-uml-diagram__help {
    display: inline-flex;
    height: 1rem;
    width: 1rem;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    border-radius: 9999px;
    border: 1px solid var(--border);
    font-size: 0.625rem;
    line-height: 1;
    color: var(--muted-foreground);
    cursor: help;
    user-select: none;
}

/* ---- view controls: group-hierarchy/expand-all switches, zoom --------- */

.onto-uml-diagram__controls {
    margin-bottom: 0.75rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}

.onto-uml-diagram__controls-left {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
}

.onto-uml-diagram__controls-right {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.onto-uml-diagram__switch-label {
    display: inline-flex;
    cursor: pointer;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.75rem;
    color: var(--muted-foreground);
}

.onto-uml-diagram__switch {
    position: relative;
    display: inline-flex;
    height: 1.25rem;
    width: 2.25rem;
    flex-shrink: 0;
    align-items: center;
    border-radius: 9999px;
    border: none;
    background: var(--muted);
    padding: 0;
    cursor: pointer;
    transition: background-color 0.15s ease;
}

.onto-uml-diagram__switch--on {
    background: var(--accent);
}

.onto-uml-diagram__switch:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--ring);
}

.onto-uml-diagram__switch-thumb {
    margin-left: 0.125rem;
    height: 0.875rem;
    width: 0.875rem;
    border-radius: 9999px;
    background: var(--card);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
    transform: translateX(0);
    transition: transform 0.15s ease;
}

.onto-uml-diagram__switch--on .onto-uml-diagram__switch-thumb {
    transform: translateX(1rem);
}

.onto-uml-diagram__zoom {
    display: inline-flex;
    align-items: center;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    font-size: 0.75rem;
    color: var(--muted-foreground);
}

.onto-uml-diagram__zoom-btn {
    border: none;
    background: none;
    padding: 0.25rem 0.5rem;
    font: inherit;
    color: inherit;
    cursor: pointer;
}

.onto-uml-diagram__zoom-btn--minus {
    border-radius: calc(var(--radius) - 2px) 0 0 calc(var(--radius) - 2px);
}

.onto-uml-diagram__zoom-btn--plus {
    border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;
}

.onto-uml-diagram__zoom-btn:hover {
    background: var(--muted);
}

.onto-uml-diagram__zoom-btn:disabled {
    cursor: not-allowed;
    opacity: 0.4;
}

.onto-uml-diagram__zoom-pct {
    width: 2.75rem;
    border: none;
    border-left: 1px solid var(--border);
    border-right: 1px solid var(--border);
    padding: 0.25rem 0;
    text-align: center;
    font: inherit;
    font-variant-numeric: tabular-nums;
    color: inherit;
    background: none;
    cursor: pointer;
}

.onto-uml-diagram__zoom-pct:hover {
    background: var(--muted);
}

.onto-uml-diagram__fullscreen-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    padding: 0.25rem 0.5rem;
    font-size: 0.75rem;
    color: var(--muted-foreground);
    background: none;
    cursor: pointer;
}

.onto-uml-diagram__fullscreen-btn:hover {
    border-color: var(--accent);
    color: var(--accent);
}

/* ---- class tree sidebar + scrollable canvas ---------------------------- */

.onto-uml-diagram__row {
    display: flex;
    gap: 0.75rem;
}

.onto-uml-diagram__row--fullscreen {
    min-height: 0;
    flex: 1 1 auto;
}

.onto-uml-diagram__tree {
    width: 16rem;
    flex-shrink: 0;
    max-height: 85vh;
    overflow-y: auto;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--background);
    padding: 0.5rem;
}

.onto-uml-diagram__tree--fullscreen {
    max-height: none;
    min-height: 0;
}

.onto-uml-tree-node__row {
    display: flex;
    cursor: pointer;
    align-items: center;
    gap: 0.25rem;
    border-radius: calc(var(--radius) * 0.4);
    padding: 0.125rem 0.25rem;
    font-size: 0.75rem;
    color: color-mix(in srgb, var(--foreground) 90%, transparent);
}

.onto-uml-tree-node__row:hover {
    background: var(--muted);
}

.onto-uml-tree-node__row--selected {
    background: color-mix(in srgb, var(--accent) 15%, transparent);
    font-weight: 600;
    color: var(--accent);
}

.onto-uml-tree-node__toggle {
    display: flex;
    height: 1rem;
    width: 1rem;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    border: none;
    background: none;
    padding: 0;
    color: var(--muted-foreground);
    cursor: pointer;
}

.onto-uml-tree-node__toggle:hover {
    color: var(--foreground);
}

.onto-uml-tree-node__toggle-spacer {
    display: block;
    width: 1rem;
    flex-shrink: 0;
}

.onto-uml-tree-node__chevron {
    transition: transform 0.1s ease;
}

.onto-uml-tree-node__chevron--open {
    transform: rotate(90deg);
}

.onto-uml-tree-node__label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.onto-uml-tree-node__count {
    flex-shrink: 0;
    color: var(--muted-foreground);
}

.onto-uml-tree-node__children {
    margin-left: 0.5rem;
    border-left: 1px solid var(--border);
    padding-left: 0.5rem;
}

/* ---- scrollable canvas + SVG defs -------------------------------------- */

.onto-uml-diagram__scroll {
    /* flex-grow so this pane always fills `.onto-uml-diagram__row`'s width,
       even when the SVG content itself is narrower than the row (e.g. a
       small ontology) — without it, a flex item with no flex-grow just
       shrinks to its content size, leaving the row's own already-100vw-wide
       card looking narrow because its background only shows through where
       this pane happens to reach. min-width: 0 lets it still shrink below
       that content size when the content is WIDER than the row, so
       overflow: auto scrolling keeps working for large diagrams. */
    flex: 1 1 auto;
    min-width: 0;
    max-height: 85vh;
    overflow: auto;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--background);
    padding: 0.5rem;
}

.onto-uml-diagram__scroll--fullscreen {
    max-height: none;
    min-height: 0;
    flex: 1 1 auto;
}

.onto-uml-diagram__stage {
    position: relative;
}

.onto-uml-diagram__svg {
    display: block;
    max-width: none;
}

.onto-uml-diagram__cluster {
    fill: color-mix(in srgb, var(--muted) 40%, transparent);
    stroke: var(--border);
    pointer-events: none;
}

.onto-uml-diagram__marker-inherit {
    fill: var(--background);
    stroke: var(--muted-foreground);
}

.onto-uml-diagram__marker-inherit-active {
    fill: var(--background);
    stroke: var(--accent);
}

.onto-uml-diagram__marker-arrow {
    stroke: var(--muted-foreground);
}

.onto-uml-diagram__marker-arrow-active {
    stroke: var(--accent);
}

/* The selected class's box smoothly resizes/moves to card size (or
   shrinks back) and the rest of the layout reflows around it — x/y/width/
   height are plain SVG geometry attributes here, but modern browsers animate
   them via a CSS transition the same as any other animatable property. */
.onto-uml-diagram__expanded-card {
    transition: x 0.25s ease, y 0.25s ease, width 0.25s ease, height 0.25s ease;
}

/* =============================================================
   ORGANISM · crosswalk-scene (ports OntoCrosswalkScene.vue)

   Legend/stats bar, the two filter-toggle switches (same pill-switch shape
   as the UML diagram's own toggles), the WebGL canvas + its hover tooltip
   and click inspector panel, and the always-rendered accessible mapping
   table below it (the deliberate no-WebGL/assistive-tech fallback).
   ============================================================= */

.crosswalk-scene {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.crosswalk-scene__legend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem 1.25rem;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--card);
    padding: 0.75rem 1rem;
    font-size: 0.75rem;
    color: var(--muted-foreground);
}

.crosswalk-scene__legend-item {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
}

.crosswalk-scene__swatch {
    display: inline-block;
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 0.15rem;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
}

.crosswalk-scene__swatch--mapping {
    width: 1.25rem;
    height: 0.375rem;
    border-radius: 0.15rem;
    background: #f5d0fe;
    box-shadow: none;
}

.crosswalk-scene__swatch--edge {
    width: 1.25rem;
    height: 0.25rem;
    border-radius: 0.15rem;
    background: color-mix(in srgb, var(--muted-foreground) 50%, transparent);
    box-shadow: none;
}

.crosswalk-scene__coverage {
    margin-left: auto;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--foreground);
}

.crosswalk-scene__controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
}

.crosswalk-scene__switch-label {
    display: inline-flex;
    cursor: pointer;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.75rem;
    color: var(--muted-foreground);
}

.crosswalk-scene__switch {
    position: relative;
    display: inline-flex;
    align-items: center;
    width: 2.25rem;
    height: 1.25rem;
    flex-shrink: 0;
    border-radius: 9999px;
    border: none;
    background: var(--muted);
    cursor: pointer;
    transition: background-color 0.15s ease;
}

.crosswalk-scene__switch--on {
    background: var(--accent);
}

.crosswalk-scene__switch-knob {
    position: absolute;
    left: 0.125rem;
    display: inline-block;
    width: 0.875rem;
    height: 0.875rem;
    border-radius: 9999px;
    background: var(--card-foreground);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
    transition: transform 0.15s ease;
}

.crosswalk-scene__switch--on .crosswalk-scene__switch-knob {
    transform: translateX(1.125rem);
}

.crosswalk-scene__hint {
    font-size: 0.75rem;
    color: var(--muted-foreground);
}

.crosswalk-scene__canvas-wrap {
    position: relative;
    overflow: hidden;
    height: 560px;
    border-radius: var(--radius);
    border: 1px solid var(--border);
}

.crosswalk-scene__canvas {
    display: block;
    width: 100%;
    height: 100%;
    touch-action: none;
}

.crosswalk-scene__no-webgl {
    display: flex;
    height: 100%;
    align-items: center;
    justify-content: center;
    background: var(--card);
    padding: 2rem;
    text-align: center;
    font-size: 0.875rem;
    color: var(--muted-foreground);
}

.crosswalk-scene__tooltip {
    position: absolute;
    z-index: 20;
    max-width: 18em;
    pointer-events: none;
    border-radius: calc(var(--radius) * 0.65);
    border: 1px solid var(--border);
    background: var(--popover);
    padding: 0.375rem 0.625rem;
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
}

.crosswalk-scene__tooltip-title {
    margin: 0;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--popover-foreground);
}

.crosswalk-scene__tooltip-curie {
    margin: 0.125rem 0 0;
    overflow-wrap: break-word;
    font-family: "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.625rem;
    color: var(--muted-foreground);
}

.crosswalk-scene__tooltip-body {
    margin: 0.125rem 0 0;
    font-size: 0.6875rem;
    line-height: 1.4;
    color: var(--popover-foreground);
    opacity: 0.85;
}

.crosswalk-scene__inspector {
    position: absolute;
    top: 0.75rem;
    right: 0.75rem;
    width: 22em;
    max-width: calc(100% - 1.5rem);
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--popover);
    padding: 0.75rem;
    text-align: left;
    box-shadow: 0 12px 30px rgba(0, 0, 0, 0.4);
}

.crosswalk-scene__inspector-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.5rem;
}

.crosswalk-scene__inspector-title {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.crosswalk-scene__inspector-title h4 {
    margin: 0;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--popover-foreground);
}

.crosswalk-scene__inspector-close {
    border: none;
    background: transparent;
    color: var(--muted-foreground);
    cursor: pointer;
    line-height: 1;
    font-size: 1rem;
}

.crosswalk-scene__inspector-close:hover {
    color: var(--popover-foreground);
}

.crosswalk-scene__inspector-curie {
    margin: 0.125rem 0 0;
    overflow-wrap: break-word;
    font-family: "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.6875rem;
    color: var(--muted-foreground);
}

.crosswalk-scene__inspector-desc {
    margin: 0.5rem 0 0;
    font-size: 0.75rem;
    line-height: 1.625;
    color: var(--popover-foreground);
    opacity: 0.9;
}

.crosswalk-scene__inspector-mappings {
    margin-top: 0.5rem;
    border-top: 1px solid var(--border);
    padding-top: 0.5rem;
}

.crosswalk-scene__inspector-mappings-title {
    margin: 0;
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--muted-foreground);
}

.crosswalk-scene__inspector-mappings ul {
    margin: 0.25rem 0 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.crosswalk-scene__inspector-mappings li {
    font-size: 0.75rem;
}

.crosswalk-scene__inspector-link {
    border: none;
    background: transparent;
    padding: 0;
    font-weight: 500;
    color: var(--link);
    cursor: pointer;
}

.crosswalk-scene__inspector-link:hover {
    color: var(--link-foreground);
}

.crosswalk-scene__inspector-match {
    color: var(--muted-foreground);
}

.crosswalk-scene__inspector-rationale {
    margin: 0.125rem 0 0;
    color: var(--muted-foreground);
}

.crosswalk-scene__table-details {
    border-radius: var(--radius);
    border: 1px solid var(--border);
}

.crosswalk-scene__table-details summary {
    cursor: pointer;
    user-select: none;
    padding: 0.625rem 1rem;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--foreground);
}

.crosswalk-scene__table-wrap {
    overflow-x: auto;
    border-top: 1px solid var(--border);
}

.crosswalk-scene__table {
    width: 100%;
    border-collapse: collapse;
    text-align: left;
    font-size: 0.75rem;
}

.crosswalk-scene__table th {
    padding: 0.5rem 1rem;
    font-weight: 500;
    color: var(--muted-foreground);
}

.crosswalk-scene__table td {
    border-top: 1px solid var(--border);
    padding: 0.5rem 1rem;
    vertical-align: top;
}

/* =============================================================
   PAGE · crosswalk (ports pages/crosswalk.vue)

   Same page-shell shape as .eovoc-page, just a touch wider (max-w-6xl in
   the Vue source, vs. max-w-5xl for the ontology browser) since the 3D
   canvas benefits from the extra room.
   ============================================================= */

.crosswalk-page {
    box-sizing: content-box;
    max-width: 72rem;
    margin: 0 auto;
    padding: 2rem 1rem;
}

.crosswalk-page__form-wrap {
    margin-top: 1.5rem;
}

/* =============================================================
   ORGANISM · crosswalk-form (CrosswalkTransformForm — new functionality,
   no Vue source to port)

   The interactive "try the crosswalk" section: directional switch +
   confidence selector, an input textarea, a Transform action, and a
   two-column result (rewritten JSON-LD / interpretation report).
   ============================================================= */

.crosswalk-form {
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--card);
    padding: 1.25rem;
}

.crosswalk-form__title {
    margin: 0;
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--foreground);
}

.crosswalk-form__lede {
    margin: 0.375rem 0 1rem;
    max-width: 48rem;
    font-size: 0.8125rem;
    line-height: 1.625;
    color: var(--muted-foreground);
}

.crosswalk-form__controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
    margin-bottom: 0.75rem;
}

.crosswalk-form__switch {
    display: inline-flex;
    border-radius: calc(var(--radius) * 0.65);
    border: 1px solid var(--border);
    overflow: hidden;
}

.crosswalk-form__switch-option {
    border: none;
    background: transparent;
    padding: 0.5rem 0.875rem;
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--muted-foreground);
    cursor: pointer;
}

.crosswalk-form__switch-option + .crosswalk-form__switch-option {
    border-left: 1px solid var(--border);
}

.crosswalk-form__switch-option--active {
    background: var(--accent);
    color: var(--accent-foreground);
}

.crosswalk-form__confidence {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8125rem;
    color: var(--muted-foreground);
}

.crosswalk-form__confidence select {
    border-radius: calc(var(--radius) * 0.65);
    border: 1px solid var(--input);
    background: var(--background);
    padding: 0.375rem 0.5rem;
    font: inherit;
    color: var(--foreground);
}

.crosswalk-form__field {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    margin-bottom: 0.75rem;
}

.crosswalk-form__field > span {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--muted-foreground);
}

.crosswalk-form__textarea {
    box-sizing: border-box;
    width: 100%;
    min-height: 10rem;
    resize: vertical;
    border-radius: calc(var(--radius) * 0.65);
    border: 1px solid var(--input);
    background: var(--background);
    padding: 0.625rem 0.75rem;
    font-family: "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.75rem;
    line-height: 1.5;
    color: var(--foreground);
}

.crosswalk-form__textarea:focus-visible {
    outline: none;
    border-color: var(--ring);
    box-shadow: 0 0 0 2px var(--ring);
}

.crosswalk-form__submit {
    border-radius: calc(var(--radius) * 0.65);
    border: 1px solid var(--primary);
    background: var(--primary);
    padding: 0.5rem 1.125rem;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--primary-foreground);
    cursor: pointer;
}

.crosswalk-form__submit:hover {
    filter: brightness(1.08);
}

.crosswalk-form__result {
    margin-top: 1.25rem;
    border-top: 1px solid var(--border);
    padding-top: 1.25rem;
}

.crosswalk-form__output-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.25rem;
}

@media (min-width: 900px) {
    .crosswalk-form__output-grid {
        grid-template-columns: 1fr 1fr;
    }
}

.crosswalk-form__subheading {
    margin: 0 0 0.5rem;
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--muted-foreground);
}

.crosswalk-form__subheading:not(:first-child) {
    margin-top: 1rem;
}

.crosswalk-form__pre {
    margin: 0;
    max-height: 28rem;
    overflow: auto;
    border-radius: calc(var(--radius) * 0.65);
    border: 1px solid var(--border);
    background: var(--background);
    padding: 0.75rem;
    font-family: "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.6875rem;
    line-height: 1.5;
    color: var(--foreground);
}

.crosswalk-form__empty {
    margin: 0;
    font-size: 0.75rem;
    color: var(--muted-foreground);
}

.crosswalk-form__report-list {
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    max-height: 12rem;
    overflow: auto;
}

.crosswalk-form__report-list li {
    font-size: 0.75rem;
    line-height: 1.5;
    color: var(--foreground);
}

.crosswalk-form__report-list code {
    font-family: "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.6875rem;
    color: var(--foreground);
    overflow-wrap: anywhere;
}

.crosswalk-form__report-list--plain code {
    color: var(--muted-foreground);
}

.crosswalk-form__match-tag {
    display: inline-block;
    margin-left: 0.375rem;
    border-radius: 9999px;
    border: 1px solid var(--border);
    padding: 0 0.375rem;
    font-size: 0.625rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: var(--muted-foreground);
}

/* =============================================================
   PAGE · catalog (ports pages/catalog.rs — no Vue original; wraps
   edc_web_components::DatasetCard, an external dependency we don't
   patch/fork)

   A thin positioning wrapper around each DatasetCard so we can float our
   own dcterms:type badge (OWL/SHACL/Crosswalk) over the top-left corner of
   its hero image, mirroring DatasetCard's own "vX.Y.Z" badge, which its
   internal CSS pins top-right of the same image. A solid --card fill plus
   --border keep the label readable over both the default owl illustration
   and a photographic upstream logo.

   Each kind gets its own existing chart-token color (not just its own text,
   which would make the three read identically at a glance) so the catalog
   grid is scannable by color, not just by reading each label.
   ============================================================= */

.eovoc-catalog__grid {
    /* Replaces patternfly's <Gallery gutter=true>. The cards stretch to the
       tallest in their row on their own — DatasetCard is a flex column, so it
       needs no height:100% wrapper and no companion rule, which is what the
       PatternFly version needed one for. */
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 1.5rem;
    padding: 0 1rem 2rem;
}

/* The type badge is DatasetCard's own `badge` slot now, positioned by the
   toolkit; only its per-kind colour is this app's business. Each kind keeps a
   distinct hue so the grid stays scannable by colour, not just by reading
   every label. */
.dataset-card__badge--ontology { background: var(--chart-1); color: var(--eona-navy); }
.dataset-card__badge--shape { background: var(--chart-4); color: var(--eona-white); }
.dataset-card__badge--crosswalk { background: var(--chart-5); color: var(--eona-white); }
.dataset-card__badge--vocabulary { background: var(--chart-2); color: var(--eona-white); }
.dataset-card__badge--codelist { background: var(--chart-3); color: var(--eona-navy); }


