Scope Visual Feedback System
============================

**CIELAB perceptual colors and scope-based visual styling.**

*Modules: pyqt_reactive.widgets.shared.scope_color_*, pyqt_reactive.widgets.shared.scope_visual_config*

Overview
--------

The scope visual feedback system provides color-coded visual feedback based on
hierarchical scope IDs. Each orchestrator (plate) gets a distinct base color,
and steps within that orchestrator inherit the base color with tint/pattern variations.

Color Strategy Architecture
---------------------------

Colors are generated via pluggable strategies:

.. list-table::
   :header-rows: 1

   * - Strategy
     - Description
   * - ``IndexBasedStrategy``
     - Primary palette (12 colors) assigned in discovery order
   * - ``MD5HashStrategy``
     - Fallback using distinctipy palette (50 colors)
   * - ``ManualColorStrategy``
     - User-selected colors with persistence

``ScopeColorService`` (singleton) manages strategy selection and caching.

CIELAB Perceptual Colors
------------------------

Border tints use CIELAB color space for perceptual uniformity. L* values
(lightness) are equidistant in perceptual space:

.. code-block:: python

   # Perceptually equidistant L* levels (0-100 scale)
   BORDER_LAB_LIGHTNESS = (30.0, 55.0, 80.0)  # dark, mid, light

The ``tint_color_perceptual()`` function converts RGB → LAB, adjusts L* to
target level while preserving a*/b* (chromatic channels), then converts back.

Scope Hierarchy
---------------

.. code-block:: text

   Orchestrator (plate_path)
   ├── Gets BASE color from palette (by discovery order)
   └── Steps (plate_path::functionstep_N)
       └── Inherit BASE color, vary by tint index + border pattern

Border patterns for steps: 3 tints × 4 patterns = 12 visual combinations.

ScopeColorScheme
----------------

Computed color scheme for a scope:

.. code-block:: python

   @dataclass
   class ScopeColorScheme:
       scope_id: Optional[str]
       hue: int
       orchestrator_item_bg_rgb: tuple[int, int, int]
       orchestrator_item_border_rgb: tuple[int, int, int]
       step_window_border_rgb: tuple[int, int, int]
       step_item_bg_rgb: Optional[tuple[int, int, int]]
       step_border_width: int
       step_border_layers: list  # [(width, tint_idx, pattern), ...]
       base_color_rgb: tuple[int, int, int]

Usage
-----

.. code-block:: python

   from pyqt_reactive.widgets.shared.scope_color_utils import get_scope_color_scheme

   # Get color scheme for a scope
   scheme = get_scope_color_scheme("plate_path::functionstep_0")

   # Access colors
   bg_color = scheme.to_qcolor_orchestrator_bg()
   border_color = scheme.to_qcolor_step_window_border()

   # Or with explicit step index (for list item position)
   scheme = get_scope_color_scheme("plate_path::functionstep_0", step_index=5)

ScopeVisualConfig
-----------------

Static configuration for visual parameters:

.. code-block:: python

   @dataclass
   class ScopeVisualConfig:
       ORCHESTRATOR_ITEM_BG_OPACITY: float = 0.10
       STEP_ITEM_BG_OPACITY: float = 0.10
       GROUPBOX_BG_OPACITY: float = 0.05
       STEP_WINDOW_BORDER_WIDTH_PX: int = 4
       FLASH_DURATION_MS: int = 300
       # ...

Access via ``get_scope_visual_config()`` singleton.

Integration with SccopedBorderMixin
-----------------------------------

Widgets use ``ScopedBorderMixin`` to apply scope-based border styling:

.. code-block:: python

   from pyqt_reactive.widgets.shared.scoped_border_mixin import ScopedBorderMixin

   class StepEditorDialog(QDialog, ScopedBorderMixin):
       def __init__(self, scope_id: str):
           super().__init__()
           self._apply_scope_border(scope_id)

See Also
--------

- :doc:`flash_animation_system` - Flash animations use scope colors
- :doc:`abstract_manager_widget` - List items use scope-based backgrounds

