@layer infine.components {
/* ─────────────────────────────────────────────────────────────────────
   View 1 — structured ingredient list (read-only display)

   The GS1 ingredient text, structured by Infine AI into a nested tree, shown
   as a run-on comma+parenthesis list. Each compound ingredient carries an
   identity hue via `.connect-ing-fam-N`, which binds `--fam` to the matching
   `--infine-fam-N` token; that one variable then drives the name colour, the
   soft fill and the parentheses. A compound's children inherit its `--fam`
   (they are tinted the parent's hue); top-level leaves have no family and stay
   neutral. %/origin are muted, read-only reflections mirrored from the
   per-ingredient details table (View 2 is their sole editor).

   NOTE: color-mix percentages are literal — a CSS variable in the percentage
   slot silently fails (learned in the mockup session); only the colour slot
   takes a var.
   ──────────────────────────────────────────────────────────────────── */

/* The structured list lives in an input-like box so it reads as an editable
   field with richer-than-plaintext content — not a run of static text. Border /
   background / hover match the Connect text inputs exactly (see
   connect-mantine-form.css): blue-light-fade resting border on the blue-bg-tint
   ground, blue-light on hover. */
.connect-ing-box {
    border: 1px solid var(--infine-blue-light-fade);
    border-radius: var(--mantine-radius-default, 4px);
    background: var(--infine-blue-bg-tint);
    padding: 8px 12px;
    cursor: text;
    transition: border-color 120ms ease;
}

.connect-ing-box:hover {
    border-color: var(--infine-blue-light);
}

.connect-ing-flow {
    font-size: var(--mantine-font-size-sm);
    line-height: 2.4;
    color: var(--infine-text);
}

/* Each family class binds --fam once; everything below reads it. */
.connect-ing-fam-1 { --fam: var(--infine-fam-1); }
.connect-ing-fam-2 { --fam: var(--infine-fam-2); }
.connect-ing-fam-3 { --fam: var(--infine-fam-3); }
.connect-ing-fam-4 { --fam: var(--infine-fam-4); }
.connect-ing-fam-5 { --fam: var(--infine-fam-5); }
.connect-ing-fam-6 { --fam: var(--infine-fam-6); }
.connect-ing-fam-7 { --fam: var(--infine-fam-7); }
.connect-ing-fam-8 { --fam: var(--infine-fam-8); }

/* One ingredient chip: a barely-rounded soft fill. Neutral (top-level leaf)
   by default — a whisper-faint tint of the text colour. */
.connect-ing-tok {
    border-radius: 4px;
    padding: 0.08em 0.34em;
    background: color-mix(in srgb, currentColor 4%, transparent);
    white-space: nowrap;
}

/* A chip that belongs to a family (compound, or a compound's child) gets a
   light tint of its hue instead of the neutral wash. Higher specificity than
   the base rule via :is(...).connect-ing-tok, so it wins. */
:is(.connect-ing-fam-1, .connect-ing-fam-2, .connect-ing-fam-3, .connect-ing-fam-4,
    .connect-ing-fam-5, .connect-ing-fam-6, .connect-ing-fam-7,
    .connect-ing-fam-8).connect-ing-tok {
    background: color-mix(in srgb, var(--fam) 12%, transparent);
}

/* Name takes the family hue when one is set (inherited from the chip/branch),
   else the flow's text colour. Compounds read a touch heavier than leaves. */
/* Family hue pulled toward ink for AA contrast on the tinted chips (raw --fam
   pink/sapphire sit near 2.6–3:1 on white, worse under the branch-hover tint).
   Neutral leaves are unaffected: currentColor is already the flow text colour,
   so the mix is a no-op for them. */
.connect-ing-name {
    color: color-mix(in srgb, var(--fam, currentColor) 55%, var(--infine-text));
    font-weight: 450;
}

.connect-ing-compound .connect-ing-name {
    font-weight: 600;
}

.connect-ing-paren {
    color: var(--fam);
    opacity: 0.65;
    font-size: 1.2em;
    font-weight: 700;
    margin: 0 0.06em;
}

.connect-ing-sep {
    color: var(--mantine-color-gray-5);
}

/* A branch wraps a compound + its parenthesised group so a group scope-hover
   can gather it — the VS-Code active-scope cue, but read-only here. */
.connect-ing-branch {
    border-radius: 6px;
    padding: 0.04em 0.1em;
    transition: background 120ms ease;
}

/* 8%, not the token's 12%: this layer stacks *under* every token tint in the
   group, and 12%+12% pushed the name text below AA contrast. */
.connect-ing-branch:hover {
    background: color-mix(in srgb, var(--fam) 8%, transparent);
}

/* Read-only %/origin reflection, mirrored from the details table below. Muted
   italic, echoing the provenance/source-line voice — never an editable look. */
.connect-ing-refl {
    font-style: italic;
    font-size: 0.8em;
    color: var(--mantine-color-gray-6);
    margin-left: 0.28em;
    white-space: nowrap;
}

.connect-ing-refl > * + * {
    margin-left: 0.4em;
}

/* ─────────────────────────────────────────────────────────────────────
   View 1 — editing affordances

   Names are inline-contentEditable; each token carries a hover toolbar (nest /
   remove) and small muted `+` carets in the gaps insert siblings. The `+`
   affordances stay quiet so they never out-shout the ingredients — editing
   weight sits in the hover toolbar.
   ──────────────────────────────────────────────────────────────────── */

/* Tokens anchor the absolutely-positioned hover toolbar. */
.connect-ing-tok {
    position: relative;
}

.connect-ing-name {
    outline: none;
    cursor: text;
    /* Explicit dark caret: default `caret-color` follows `color`, which becomes the
       family hue on a tinted token. Dark reads on both the white focus chip and a
       plain token. */
    caret-color: var(--infine-text);
    /* Promote the editable host from bare `inline` to `inline-block`: Chromium can
       fail to paint the text caret in an inline contentEditable span (the caret
       has no layout box of its own). inline-block gives it one. min-width keeps a
       caret target when the name is momentarily empty (just-inserted). */
    display: inline-block;
    min-width: 0.5ch;
}

/* Actively-edited name: a strong blue ring marks edit mode. The fill is plain
   white (not a blue tint) so the dark text caret sits on maximum contrast — a
   thin caret on a tinted or light-blue ground was too easy to miss — and so a
   family-tinted token reverts to a neutral ground while being edited. */
.connect-ing-name:focus {
    color: var(--infine-text);
    background: var(--mantine-color-body);
    box-shadow: 0 0 0 2px var(--infine-blue-bright);
    border-radius: 4px;
    padding: 0.04em 0.32em;
    position: relative;
    z-index: 5;
}

/* A blank (just-inserted) name shows a muted placeholder from its data-ph. */
.connect-ing-name:empty::before {
    content: attr(data-ph);
    color: var(--mantine-color-gray-5);
    font-style: italic;
    font-weight: 400;
}

/* Hover toolbar, floating just above its token. */
.connect-ing-acts {
    position: absolute;
    bottom: calc(100% - 3px);
    left: 50%;
    transform: translateX(-50%) translateY(4px);
    display: flex;
    gap: 1px;
    padding: 2px;
    background: var(--mantine-color-body);
    border: 1px solid var(--mantine-color-default-border);
    border-radius: 7px;
    box-shadow: var(--mantine-shadow-md);
    opacity: 0;
    pointer-events: none;
    transition: opacity 120ms ease, transform 120ms ease;
    z-index: 6;
}

.connect-ing-tok:hover > .connect-ing-acts {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
    pointer-events: auto;
}

/* In edit mode (the token's name is focused) suppress the hover toolbar so the
   text edit has the user's full attention — nest/remove return the moment the
   name is unfocused. Higher specificity than the :hover rule above, so it wins
   even while the token is hovered. */
.connect-ing-tok:has(.connect-ing-name:focus) > .connect-ing-acts {
    opacity: 0;
    pointer-events: none;
}

.connect-ing-act {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    border-radius: 5px;
    background: none;
    color: var(--mantine-color-gray-6);
    cursor: pointer;
}

/* blue-light-fade, not the bg/bg-tint pales: those sit within a couple of
   levels of the toolbar's white body and read as no hover at all (red-bg gets
   away with it on the × only because red ink is perceptually loud). */
.connect-ing-act-nest:hover {
    background: var(--infine-blue-light-fade);
    color: var(--infine-blue);
}

/* The nest button's "(+)" — a plus inside parentheses, the view's own vocabulary
   for "add an ingredient inside this one's parens". Distinguished from the bare
   `+` carets exactly by the parens. */
.connect-ing-nest-glyph {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.03em;
    /* The flow's line-height (2.4) would give this span a 26px line box inside
       the 22px flex-centred button, shoving the glyph off-centre. */
    line-height: 1;
    /* Open Sans "(+)" ink sits ~1.4px below the button's centre at this size
       (baseline-probe + canvas actualBoundingBox measurement, not eyeballed —
       CSS-zoom eyeballing gave the opposite, wrong, answer). */
    transform: translateY(-1.5px);
}

.connect-ing-act-remove:hover {
    background: var(--infine-red-bg);
    color: var(--infine-red-bright);
}

/* Insert "+" affordances between ingredients. A constant soft blue at rest —
   present enough to read as an add-affordance, quiet enough not to out-shout the
   ingredients. `vertical-align: baseline` + a text-matched font-size sit the glyph
   on the text baseline (bare `middle` floated it off the row); `line-height: 1`
   hugs the glyph so its hover background isn't a pill taller than the row (the
   inline-block would otherwise inherit the flow's 2.4 line-height). */
.connect-ing-ins {
    display: inline-block;
    width: 0.7em;
    line-height: 1;
    text-align: center;
    color: color-mix(in srgb, var(--infine-blue-light) 65%, transparent);
    border-radius: 3px;
    cursor: pointer;
    user-select: none;
    vertical-align: baseline;
    transition: color 100ms ease, background 100ms ease;
}

.connect-ing-ins::before {
    content: "+";
    font-size: 0.9em;
    font-weight: 700;
}

/* Hover: white glyph on a half-strength navy wash. Translucent, not solid —
   a solid chip vanished on the tinted family-group backgrounds; a translucent
   layer darkens whatever it sits on, so the hover reads on white gaps and
   tinted groups alike. */
.connect-ing-ins:hover {
    color: var(--mantine-color-white);
    background: color-mix(in srgb, var(--infine-blue) 50%, transparent);
}

/* The end caret is the first-time "add" target — give it a little more body. */
.connect-ing-ins-end {
    width: auto;
    padding: 0 0.3em;
}

/* The `+` that adds a sub-ingredient inside a group — same quiet resting tone
   and soft hover as the sibling insert caret. */
.connect-ing-addchild {
    display: inline-block;
    margin-left: 0.1em;
    padding: 0 0.16em;
    line-height: 1;
    color: color-mix(in srgb, var(--infine-blue-light) 65%, transparent);
    font-size: 0.9em;
    font-weight: 700;
    border-radius: 3px;
    cursor: pointer;
    user-select: none;
    vertical-align: baseline;
    transition: color 100ms ease, background 100ms ease;
}

.connect-ing-addchild:hover {
    color: var(--mantine-color-white);
    background: color-mix(in srgb, var(--infine-blue) 50%, transparent);
}

/* Drag to move / re-nest. The grip drags its whole token (a compound's whole
   branch); valid destinations mark the insertion slot with a blue leading bar. */
.connect-ing-act-grip {
    cursor: grab;
}

.connect-ing-act-grip:hover {
    background: var(--infine-blue-light-fade);
    color: var(--infine-blue);
}

.connect-ing-act-grip:active {
    cursor: grabbing;
}

.connect-ing-dragging {
    opacity: 0.4;
}

/* The landing marker lives on the `+` affordance at the active slot — it sits
   exactly where the dropped node would be inserted, so it's the honest place
   to mark (not the neighbouring token). The glyph goes transparent (we're
   moving, not adding — a `+` would lie) and the chip fills solid blue, so the
   slot reads as a plain insertion bar. Transparent rather than hidden keeps
   the glyph sizing the chip — no layout shift when the marker appears. */
.connect-ing-ins.connect-ing-drop-slot,
.connect-ing-addchild.connect-ing-drop-slot {
    color: transparent;
    background: var(--infine-blue-bright);
}

/* Mid-drag, hover toolbars are noise over the drop targets — keep them down.
   Outranks the `.connect-ing-tok:hover` show rule by specificity. */
.connect-ing-flow-dragging .connect-ing-tok:hover > .connect-ing-acts {
    opacity: 0;
    pointer-events: none;
}

/* ─────────────────────────────────────────────────────────────────────
   View 2 — origin progress cue
   ──────────────────────────────────────────────────────────────────── */

/* Product-level origin progress cue under the details table. Same orange as
   the per-row required markers, prefixed with the required-marker glyph, so it
   reads as part of the form's "required" vocabulary (red stays for errors). */
.connect-ingredient-origin-progress {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
    font-size: var(--mantine-font-size-xs);
    line-height: 1;
    color: var(--infine-yellow-bright);
}

.connect-ingredient-origin-progress::before {
    content: "";
    flex: none;
    width: 14px;
    height: 14px;
    background-color: var(--infine-yellow-bright);
    -webkit-mask: url("/required-marker.svg") no-repeat center / contain;
    mask: url("/required-marker.svg") no-repeat center / contain;
}

/* ─────────────────────────────────────────────────────────────────────
   Source-row aside — the color-coding note
   ──────────────────────────────────────────────────────────────────── */

/* Right-aligned on the flex source row; inherits the row's muted gray. When
   the row runs out of width, only the aside wraps — without the flex:none the
   attribution spans shrink to min-content and break mid-phrase ("Pasted / by"). */
.connect-source-line > :not(.connect-source-aside) {
    flex: none;
}

.connect-source-aside {
    flex: 0 1 auto;
    margin-left: auto;
    padding-left: 12px;
    text-align: right;
    /* Must read over the muted attribution beside it: this is guidance the
       user needs (don't read the hues as analysis), not provenance metadata. */
    color: var(--mantine-color-gray-7);
    font-weight: 600;
}
}
