/*********************************************************
 *      lib_graph.css
 *********************************************************/

/*
 *  Toolbar icon state colors — chosen for maximum distinction,
 *  including deuteranopia/protanopia/tritanopia safety.
 *
 *  Blue (#1890ff)   — create: primary action ("new")
 *  Orange (#fa8c16)  — save: "pending changes, needs attention"
 *  Violet (#722ed1) — undo/redo: history navigation
 *  Red (#f5222d)    — delete/cancel: destructive action
 */
.color_create_state {
    color: #1890ff !important;
}
/*  Orange is "needs attention, waiting on you". A toggle that is armed
 *  and waiting for its target is exactly that and not a submit, so it
 *  takes the same colour under a name that says so.  */
.color_submit_state,
.color_pending_state {
    color: #fa8c16 !important;
}
.color_active_state {
    color: #722ed1 !important;
}
.color_cancel_state {
    color: #f5222d !important;
}

/*
 *  G6 fullscreen: override G6's transparent ::backdrop and give the container
 *  a solid background so the page doesn't show through.
 *  G6 injects `:not(:root):fullscreen::backdrop { background: transparent }`
 *  with lower specificity, so class + pseudo-class wins without !important.
 */
/*
 *  A shift+click on a card must SELECT THE CARD, not smear a text
 *  selection across the graph.
 *
 *  Shift+click is the browser's own "extend the text selection"
 *  gesture, and the cards are DOM. So the moment shift+click came to
 *  mean something here, marking three nodes also painted their labels
 *  blue -- and the wash stayed until the next click somewhere else.
 *  The canvas is a canvas: nothing in it is prose.
 *
 *  Except what IS there to be read or typed into. The popovers this
 *  gclass appends to the same container carry record data an operator
 *  may well want to copy, and inputs need a caret. `g6-node-detail`
 *  above all: that one exists ONLY to be read.
 *
 *  The list is every class `create_popover_base()` is called with. A
 *  new popover that forgets to join it is unreadable in a way nothing
 *  reports -- so add it here in the same commit.
 */
.graph-container {
    -webkit-user-select: none;
    user-select: none;

    /*
     *  And the finger's half of the same problem: on a touch screen
     *  the browser owns the gesture until something says otherwise.
     *
     *  G6 puts `touch-action: none` on its CANVAS, so dragging from
     *  the background pans the graph -- and it puts nothing on the
     *  HTML nodes, which are ordinary DIVs layered on top of it. So
     *  a finger that lands on a CARD was making a page scroll:
     *  Chrome let two `pointermove`s through, decided the gesture
     *  was a scroll, and killed the pointer stream with a
     *  `pointercancel`.  The node followed the finger for about 20px
     *  and then stopped dead while the page slid -- which is what
     *  "I only want to move a node" looked like on a phone.
     *
     *  The whole container, not the cards: every layer G6 stacks in
     *  here (nodes, plugin containers, the ones it adds next) is a
     *  DIV over a canvas that already refuses the browser's
     *  gestures, and they have to agree.
     */
    touch-action: none;
}
.graph-container input,
.graph-container textarea,
.graph-container select,
.graph-container [contenteditable],
.graph-container .g6-confirm-popover,
.graph-container .g6-create-popover,
.graph-container .g6-node-popover,
.graph-container .g6-edge-popover,
.graph-container .g6-port-popover,
.graph-container .g6-node-detail {
    -webkit-user-select: text;
    user-select: text;
}

/*
 *  What a finger must still be able to SCROLL: the panels, and the
 *  context menu, which is as long as the impact of a delete.
 */
.graph-container .g6-confirm-popover,
.graph-container .g6-create-popover,
.graph-container .g6-node-popover,
.graph-container .g6-edge-popover,
.graph-container .g6-port-popover,
.graph-container .g6-node-detail,
.graph-container .g6-contextmenu {
    touch-action: auto;
}

.graph-container:fullscreen {
    background-color: var(--bulma-background, #fff);
}
.graph-container:fullscreen::backdrop {
    background-color: var(--bulma-background, #fff);
}

/*
 *  THE NODE DETAIL POPOVER.
 *
 *  Sizes live here and not in the inline styles that build it, for one
 *  reason: a media query cannot reach an inline style, and this box is
 *  read on a phone as much as on a desk.
 *
 *  12px was the size of the rows and 11px of the topic. That is caption
 *  size, and this popover exists ONLY to be read -- it is the record of
 *  a node, field by field. Same reasoning as the context menu below:
 *  12px is a caption, not content.
 */
.graph-container .g6-detail-head {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    /*  Sticky, so the ✕ never scrolls off a box that is most of the
     *  screen on a phone.  The 12px of side padding the popover used to
     *  carry moved to the rows, so the head reaches its edges. */
    position: sticky;
    top: 0;
    z-index: 1;
    padding: 12px 0 6px;
}

.graph-container .g6-detail-id {
    font-size: 15px;
    font-weight: 600;
    word-break: break-word;
}

.graph-container .g6-detail-topic {
    font-size: 12px;
    margin-top: 2px;
    word-break: break-word;
}

.graph-container .g6-detail-row {
    display: flex;
    gap: 10px;
    padding: 6px 0;
}

.graph-container .g6-detail-key,
.graph-container .g6-detail-val,
.graph-container .g6-detail-empty {
    font-size: 13px;
    word-break: break-word;
}

.graph-container .g6-detail-key {
    flex: 0 0 38%;
}

.graph-container .g6-detail-val {
    flex: 1 1 auto;
}

/*
 *  The close button was a 13x16 box: a glyph with 2px of side padding
 *  and nothing else. That is a mouse target on a good day and not a
 *  finger target on any. It is a real box now, and a square one, so the
 *  ✕ has somewhere to be.
 */
.graph-container .g6-detail-close {
    flex: 0 0 auto;
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    margin: -4px -4px 0 0;
    border-radius: 4px;
}

.graph-container .g6-detail-close:hover {
    background: var(--bulma-background, rgba(128, 128, 128, 0.15));
}

/*
 *  A finger, and the rule the context menu below already follows: the
 *  target grows to something a thumb hits, and the text to something
 *  read at arm's length.
 */
@media (pointer: coarse) {
    .graph-container .g6-detail-close {
        width: 44px;
        height: 44px;
        font-size: 22px;
        margin: -8px -8px 0 0;
    }
    .graph-container .g6-detail-id {
        font-size: 16px;
    }
    .graph-container .g6-detail-topic {
        font-size: 13px;
    }
    .graph-container .g6-detail-key,
    .graph-container .g6-detail-val,
    .graph-container .g6-detail-empty {
        font-size: 14px;
    }
    .graph-container .g6-detail-row {
        padding: 8px 0;
    }
}

/*
 *  G6 toolbar: larger icons (via className option)
 */
.g6-toolbar.g6-toolbar-large .g6-toolbar-item {
    width: 20px;
    height: 20px;
    padding: 6px;
}
.g6-toolbar.g6-toolbar-large .g6-toolbar-item svg {
    width: 20px;
    height: 20px;
    fill: currentColor;
}
/* Disabled toolbar item: visual feedback + block clicks */
.g6-toolbar-item[disabled] {
    opacity: 0.35;
    pointer-events: none;
}
/* Vertical toolbar (column): horizontal hairline between items */
.g6-toolbar.g6-toolbar-large[style*="flex-direction: column"] .g6-toolbar-item + .g6-toolbar-item {
    border-top: 1px solid var(--bulma-border-weak, #d9d9d9);
}
/* Horizontal toolbar (row): vertical hairline between items */
.g6-toolbar.g6-toolbar-large[style*="flex-direction: row"] .g6-toolbar-item + .g6-toolbar-item {
    border-left: 1px solid var(--bulma-border-weak, #d9d9d9);
}

/*
 *  Group divider. Every item already carries a hairline against its
 *  neighbour, so a group break has to be a GAP, not one more line --
 *  otherwise the camera controls and the window control read as one
 *  list of six equal things.
 */
.g6-toolbar.g6-toolbar-large .g6-toolbar-sep {
    background-color: var(--bulma-border, #cfcfcf);
    flex: none;
}
.g6-toolbar.g6-toolbar-large[style*="flex-direction: column"] .g6-toolbar-sep {
    width: 100%;
    height: 1px;
    margin: 5px 0;
}
.g6-toolbar.g6-toolbar-large[style*="flex-direction: row"] .g6-toolbar-sep {
    width: 1px;
    align-self: stretch;
    margin: 0 5px;
}

/*
 *  A toolbar item whose glyph is TEXT (`1:1`). There is no icon for
 *  actual size, so the item has to be as wide as its label instead
 *  of the square an icon gets.
 */
.g6-toolbar.g6-toolbar-large .g6-toolbar-item-text {
    width: auto;
    min-width: 20px;
}
.g6-toolbar.g6-toolbar-large .g6-toolbar-text {
    /*
     *  The click has to land on the ITEM, not on this.
     *
     *  G6's toolbar fires `onClick` only when the pressed element's own
     *  class list contains `g6-toolbar-item` -- which is why its CSS
     *  gives the icon `<svg>` `pointer-events: none`. A text glyph
     *  needs the same rule for the same reason: without it the press
     *  lands on this span, the class does not match, and the button is
     *  simply dead. `1:1` shipped that way.
     */
    pointer-events: none;
    display: block;
    line-height: 20px;
    font-size: 0.8125rem;
    font-weight: 600;
    text-align: center;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/*
 *  The zoom readout REPORTS, it does not act: no pointer, no hover,
 *  and quieter than the buttons it sits with. It carries no
 *  `g6-toolbar-item` class, which is also what keeps G6's own click
 *  handler from firing on it.
 */
.g6-toolbar.g6-toolbar-large .g6-toolbar-readout {
    padding: 2px 6px;
    font-size: 0.75rem;
    text-align: center;
    white-space: nowrap;
    cursor: default;
    opacity: 0.7;
    font-variant-numeric: tabular-nums;
}

/*
 *  Hover follows the theme too. G6 injects `.g6-toolbar-item:hover
 *  { background-color: #f0f0f0 }` at runtime, AFTER our bundled CSS,
 *  so an equal-specificity rule would lose -- the extra class on the
 *  toolbar itself is what wins it, with no !important.
 */
.g6-toolbar.g6-toolbar-large .g6-toolbar-item:hover {
    background-color: var(--bulma-background, #f0f0f0);
}

/*
 *  A TOGGLE that is on looks PRESSED, it does not take a colour from
 *  the palette at the top of this file: each of those names a kind of
 *  ACTION, and "the graph is listening for a selection" is not one --
 *  borrowing undo/redo's violet for it put the same colour on two
 *  neighbouring buttons for two different reasons.
 *
 *  Inverted neutral, and deliberately hue-free: it cannot be mistaken
 *  for the hover tint declared just above (a much lighter wash of the
 *  same surface), it reads at a glance even when the glyph is a thin
 *  outline rather than a solid silhouette, and it says what it says
 *  without a hue -- which is the same reason the palette above is
 *  documented as colour-blind safe.
 *
 *  The `:hover` half is not decoration: without it, hovering a pressed
 *  button would hand it back to the rule above (equal specificity,
 *  declared earlier, so this one wins only where both match) and the
 *  toggle would appear to switch off under the pointer.
 */
.g6-toolbar.g6-toolbar-large .g6-toolbar-item.pressed_state,
.g6-toolbar.g6-toolbar-large .g6-toolbar-item.pressed_state:hover {
    background-color: var(--bulma-text, #2e333d);
    color: var(--bulma-background, #ffffff);
    border-radius: 4px;
}

/*
 *  The same pressed look for a toggle in a DOM toolbar.
 *
 *  `pressed_state` was written for the G6 plugin toolbar and styled ONLY
 *  there, so the two toolbars built by `yui_graph_camera.js` were given
 *  the class and no rule: pressing the button changed nothing on screen,
 *  which from the outside is indistinguishable from a button that does
 *  not work.
 *
 *  `border-color` as well as the fill, or Bulma's own border stays and
 *  the button reads as outlined-and-filled, which is neither state.
 *  The `:hover` half is the same guard as above: without it, hovering a
 *  pressed button hands it back to Bulma's rule and the toggle appears
 *  to switch off under the pointer.
 */
/*
 *  THE OTHER TWO GRAPHS' MOUNTS.
 *
 *  They live here, unscoped, and that is the point: these rules first
 *  shipped inside `c_yui_json.css` under a `.C_YUI_JSON` ancestor, and
 *  a host that mounts `C_YUI_JSON_GRAPH` DIRECTLY -- which the demo
 *  does, and any host wanting only the graph -- has no such ancestor.
 *  Every one of them silently did nothing there: no crosshair while
 *  picking, no mark on the anchored card, and no `touch-action`, so a
 *  finger dragging a card made a page scroll. A gclass's stylesheet
 *  cannot assume the gclass that usually hosts it.
 *
 *  `lib_graph.css` is reached through `lib_graph.js`, which the camera
 *  module imports and every one of these graphs uses, so it is loaded
 *  wherever a graph is.
 */

/*
 *  The gobj tree's popover close button, for a FINGER.
 *
 *  It is an 18px glyph with 4px of padding either side -- about 26x18
 *  of target, which a mouse hits and a thumb does not. The popover
 *  opens on a tap, so the thing that closes it has to be reachable by
 *  the same thumb that opened it.
 *
 *  44px is the size every touch guideline settles on, and it is applied
 *  only under `(pointer: coarse)`: on a mouse the small button is fine
 *  and a 44px square in a 26px header row would not be.
 *
 *  `!important` because the button carries an inline `cssText` -- a
 *  media query has no way to win against one, and the size of a target
 *  is not a preference.
 */
@media (pointer: coarse) {
    .GOBJ_TREE_POPOVER_CLOSE {
        min-width: 44px !important;
        min-height: 44px !important;
        font-size: 26px !important;
        padding: 0 !important;
        display: inline-flex !important;
        align-items: center !important;
        justify-content: center !important;
    }
}

/*
 *  Picking the node to anchor: the pointer says a click is expected,
 *  which is otherwise announced only by a button at the far end of a
 *  toolbar. `!important` because G6 writes `cursor` onto its own
 *  elements from its style props, and a mode has to win over a default.
 */
.json-graph-container.JSON_GRAPH_PICKING,
.json-graph-container.JSON_GRAPH_PICKING *,
.gobj-tree-container.GOBJ_TREE_PICKING,
.gobj-tree-container.GOBJ_TREE_PICKING * {
    cursor: crosshair !important;
}

/*
 *  The card the camera is holding.
 *
 *  It has to say so ON THE CARD: the toolbar knows an anchor is set,
 *  and knowing WHICH node it is was left to the reader remembering
 *  what they clicked -- not a mark, a memory test.
 *
 *  An outline and not a fill: the card's own colours already mean
 *  things (a dashed border is a dict, a solid one a list, the header
 *  carries the group's hue), so repainting any of them would say two
 *  things down one channel. `outline` draws OUTSIDE the box, so it
 *  cannot move a single row.
 *
 *  DASHED, and the same dash the treedb graph draws inline for its own
 *  cards: there, amber already means a find match or a focused topic
 *  and blue means the selection, so a third SOLID ring would be a third
 *  thing to learn in one channel. A dash is a different channel, and it
 *  reads as the provisional thing a camera state is. One feature, one
 *  drawing, in all three viewers.
 *
 *  Amber, the same "this one, right now" the toolbar's arming state
 *  uses, and not one of the palette's action colours -- it is not an
 *  action.
 */
.JSON_CARD_ANCHORED,
.GOBJ_CARD_ANCHORED {
    outline: 3px dashed #fa8c16;
    outline-offset: 2px;
}

/*
 *  Once the cards can be DRAGGED. Two problems, both the browser's:
 *  G6 puts `touch-action: none` on its CANVAS and nothing on the html
 *  nodes, which are ordinary DIVs layered over it, so a finger landing
 *  on a card made a page scroll -- two `pointermove`s, then
 *  `pointercancel`, the card dead after ~20px while the page slid. And
 *  a card is text, so a mouse drag started a selection.
 */
.json-graph-container,
.gobj-tree-container {
    touch-action: none;
    -webkit-user-select: none;
    user-select: none;
}

/*  The DOM toolbar's `pressed_state` lives in `yui_toolbar.css` now, and
 *  not here: a pressed toggle is a TOOLBAR thing, and while the rule sat
 *  in this file it reached an app only if the app mounted a graph -- the
 *  stylesheet rides the JS import. yunovatios copied the rule into its
 *  own `app.css` under a third name for exactly that reason. The G6
 *  plugin toolbar's own item keeps its rule above, where the plugin is.  */

/*
 *  Touch: the context menu is a LIST OF COMMANDS, and G6 sizes its
 *  rows with `padding: 8px 12px` -- 34px, which is a mouse row. The
 *  menu is now reachable by a long press (g6_touch_gestures.js), so
 *  its rows have to be reachable by the same finger that opened it.
 *  `font-size` with it: 12px is a caption, not a command.
 */
@media (pointer: coarse) {
    .g6-contextmenu {
        font-size: 14px;
    }
    .g6-contextmenu-li {
        min-height: 44px;
        display: flex;
        align-items: center;
        padding: 8px 16px;
    }
}

/* No transient effects: open/move popups immediately (user pref).
 * G6 injects its own .g6-contextmenu / .g6-tooltip CSS with a
 * left/top/visibility transition that makes them glide into place.
 * Override with !important so it wins regardless of <style> order. */
.g6-contextmenu,
.g6-tooltip {
    transition: none !important;
}

/* Theme-aware context menu. G6 injects its own .g6-contextmenu rule
 * (white-ish bg, dark text) at runtime, AFTER our bundled CSS, so an
 * equal-specificity rule would lose. Keying off [data-theme] on
 * <html> raises specificity above G6's single-class rule — wins in
 * both themes without !important. Icons use currentColor → they
 * follow the text colour automatically. */
[data-theme="light"] .g6-contextmenu {
    background-color: #ffffff;
    color: #1f2933;
}
[data-theme="light"] .g6-contextmenu-li:hover {
    background-color: #f0f0f0;
}
[data-theme="dark"] .g6-contextmenu {
    background-color: #232a33;
    color: #e8eaed;
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.5);
}
[data-theme="dark"] .g6-contextmenu-li:hover {
    background-color: #2f3742;
}

