diff --git a/changedetectionio/blueprint/tags/__init__.py b/changedetectionio/blueprint/tags/__init__.py
index 8cc689b2a..2fa5bdb0d 100644
--- a/changedetectionio/blueprint/tags/__init__.py
+++ b/changedetectionio/blueprint/tags/__init__.py
@@ -189,6 +189,9 @@ def construct_blueprint(datastore: ChangeDetectionStore):
'watch': default,
'extra_notification_token_placeholder_info': datastore.get_unique_notification_token_placeholders_available(),
'llm_configured': bool(_get_llm_config(datastore)),
+ # Tells the shared AI include it is rendering a group, not a watch — `watch` above
+ # is this tag, so it cannot be used to tell the two apart.
+ 'llm_group_edit': True,
}
included_content = {}
diff --git a/changedetectionio/blueprint/tags/form.py b/changedetectionio/blueprint/tags/form.py
index ecfcbc8ea..483146fb6 100644
--- a/changedetectionio/blueprint/tags/form.py
+++ b/changedetectionio/blueprint/tags/form.py
@@ -10,6 +10,7 @@ from wtforms.fields.simple import BooleanField
from flask_babel import lazy_gettext as _l
from changedetectionio.blueprint.tags.colour import CSS_HEX_COLOUR_REGEX
+from changedetectionio.widgets.ternary_boolean import TernaryNoneBooleanField
from changedetectionio.processors.restock_diff.forms import processor_settings_form as restock_settings_form
from changedetectionio.llm.ui_strings import LLM_INTENT_TAG_PLACEHOLDER
from changedetectionio.llm.evaluator import (
@@ -28,7 +29,22 @@ class group_restock_settings_form(restock_settings_form):
validators=[validators.Optional(),
validators.Regexp(CSS_HEX_COLOUR_REGEX,
message=_l('Must be a hex colour, for example #4f8ef7'))])
- llm_intent = TextAreaField('AI Change Intent',
+ # The group's one and only AI control. Same key as the per-watch switch (see forms.py) but
+ # ternary, because a group has a third useful answer: "no opinion, leave it to each watch"
+ # (the default). See tag_llm_decision() for the semantics of each state — #4204.
+ # @NOTE! In the near future this stops being a ternary bool and becomes a *profile*
+ # selector — pick one of the configured LLM profiles, or 'off', or inherit. The
+ # field name is already the future one; only the widget and the True/False/None
+ # value space need to change, so keep reads going through tag_llm_decision().
+ llm_backend_profile = TernaryNoneBooleanField(
+ _l('AI for watches in this group'),
+ default=None,
+ yes_text=_l('On, use the settings below'),
+ no_text=_l('Off for every watch'),
+ none_text=_l('Leave it to each watch'),
+ )
+
+ llm_intent = TextAreaField('AI Change Intent - Notify me when..',
validators=[validators.Optional(), validators.Length(max=2000)],
render_kw={"rows": "5", "placeholder": LLM_INTENT_TAG_PLACEHOLDER})
@@ -38,7 +54,7 @@ class group_restock_settings_form(restock_settings_form):
default='')
llm_change_summary_mode = RadioField(
- _l('How this prompt combines with the inherited one'),
+ _l('Change Summary prompt - Append or Replace the default?'),
choices=[
(LLM_PROMPT_MODE_REPLACE, _l('Replace the inherited prompt')),
(LLM_PROMPT_MODE_APPEND, _l('Append to the inherited prompt')),
diff --git a/changedetectionio/blueprint/tags/templates/edit-tag.html b/changedetectionio/blueprint/tags/templates/edit-tag.html
index 64d4e569e..82e8aca8d 100644
--- a/changedetectionio/blueprint/tags/templates/edit-tag.html
+++ b/changedetectionio/blueprint/tags/templates/edit-tag.html
@@ -27,9 +27,9 @@
- {% if llm_configured %}
+ {# Always shown, like the watch edit page: with no provider configured the pane
+ explains how to set one up (and carries the AI switches through on save). #}
{# TRANSLATORS: CJK fonts lack native italics; allow substitution with conventional local styling. dennis-ignore: W303 #}
diff --git a/changedetectionio/blueprint/ui/edit.py b/changedetectionio/blueprint/ui/edit.py
index 9ba78ed87..27f4ec71c 100644
--- a/changedetectionio/blueprint/ui/edit.py
+++ b/changedetectionio/blueprint/ui/edit.py
@@ -15,23 +15,50 @@ from changedetectionio.llm.evaluator import get_llm_config as _get_llm_config
def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMetaData):
edit_blueprint = Blueprint('ui_edit', __name__, template_folder="../ui/templates")
+ def _watch_tags(watch):
+ """(uuid, tag) for this watch's tags, in its own tag order, skipping UUIDs we don't know."""
+ tags = datastore.data['settings']['application'].get('tags', {})
+ return [(tag_uuid, tags[tag_uuid]) for tag_uuid in watch.get('tags', []) if tag_uuid in tags]
+
def _resolve_llm_group_overrides(watch, datastore) -> dict:
"""
For each LLM field (llm_intent, llm_change_summary): if the watch has no own
- value but a linked tag does, return {'value': ..., 'group_name': ...} so the
- edit template can render the textarea as readonly with a group-sourced placeholder.
- Returns None for each field when the watch has its own value (editable).
+ value but a linked group does, return {'value': ..., 'group_name': ..., 'group_uuid': ...}
+ so the edit template can show the inherited value as the textarea placeholder and link
+ back to the group that supplied it.
+ Returns None for each field when the watch has its own value (nothing inherited).
+
+ Only groups whose AI setting is "On" lend their prompts — the same gate the evaluator
+ applies via resolve_llm_field(), so the placeholder always reflects what will actually
+ run. See llm/evaluator.py:tag_llm_decision().
"""
- result = {'llm_intent': None, 'llm_change_summary': None}
+ from changedetectionio.llm.evaluator import tag_llm_applies_to_watches, tag_llm_decision
+
+ result = {'llm_intent': None, 'llm_change_summary': None, 'llm_backend_profile': None}
+
+ # AI on/off is not a "fill in the blank" field: a group that has taken the decision
+ # (On or Off, i.e. not "leave it to each watch") decides for every watch in it (#4204),
+ # so report it and let the template show the watch's own checkbox as overridden.
+ for tag_uuid, tag in _watch_tags(watch):
+ if tag_llm_decision(tag) is not None:
+ result['llm_backend_profile'] = {
+ 'value': tag_llm_decision(tag),
+ 'group_name': tag.get('title', 'tag'),
+ 'group_uuid': tag_uuid,
+ }
+ break
+
for field in ('llm_intent', 'llm_change_summary'):
if (watch.get(field) or '').strip():
continue # watch has its own value — editable, no group override
- for tag_uuid in watch.get('tags', []):
- tag = datastore.data['settings']['application'].get('tags', {}).get(tag_uuid)
- if tag and (tag.get(field) or '').strip():
+ for tag_uuid, tag in _watch_tags(watch):
+ if not tag_llm_applies_to_watches(tag):
+ continue
+ if (tag.get(field) or '').strip():
result[field] = {
'value': tag.get(field).strip(),
'group_name': tag.get('title', 'tag'),
+ 'group_uuid': tag_uuid,
}
break
return result
@@ -211,6 +238,16 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe
extra_update_obj['filter_text_replaced'] = True
extra_update_obj['filter_text_removed'] = True
+ # A group that has taken the AI on/off decision owns that control, so the edit page
+ # renders it disabled (see include_llm_intent.html). A disabled checkbox isn't
+ # submitted at all, and for a checkbox "not submitted" is indistinguishable from
+ # "unticked" — so don't take this field from the form while a group decides. The
+ # watch keeps its own preference untouched, ready for when the group stops deciding.
+ # Resolved against the watch's *stored* tags — i.e. what the page was rendered from,
+ # so attaching or detaching a group in this same save is still honoured correctly.
+ if _resolve_llm_group_overrides(datastore.data['watching'][uuid], datastore).get('llm_backend_profile'):
+ extra_update_obj['llm_backend_profile'] = datastore.data['watching'][uuid].get('llm_backend_profile', True)
+
# Because wtforms doesn't support accessing other data in process_ , but we convert the CSV list of tags back to a list of UUIDs
tag_uuids = []
if form.data.get('tags'):
diff --git a/changedetectionio/forms.py b/changedetectionio/forms.py
index facb8de4b..6a7364da9 100644
--- a/changedetectionio/forms.py
+++ b/changedetectionio/forms.py
@@ -953,7 +953,7 @@ class processor_text_json_diff_form(commonSettingsForm):
time_between_check_use_default = BooleanField(_l('Use global settings for time between check and scheduler.'), default=False)
- llm_intent = TextAreaField(_l('AI Change Intent'), validators=[validators.Optional(), validators.Length(max=2000)],
+ llm_intent = TextAreaField(_l('AI Change Intent - Notify me when..'), validators=[validators.Optional(), validators.Length(max=2000)],
render_kw={"rows": "5", "placeholder": LLM_INTENT_WATCH_PLACEHOLDER})
llm_change_summary = TextAreaField(_l('AI Change Summary'), validators=[validators.Optional(), validators.Length(max=2000)],
@@ -961,13 +961,16 @@ class processor_text_json_diff_form(commonSettingsForm):
default='')
llm_change_summary_mode = RadioField(
- _l('How this prompt combines with the inherited one'),
+ _l('Change Summary prompt - Append or Replace the default?'),
choices=[
(LLM_PROMPT_MODE_REPLACE, _l('Replace the inherited prompt')),
(LLM_PROMPT_MODE_APPEND, _l('Append to the inherited prompt')),
],
default=LLM_PROMPT_MODE_REPLACE,
)
+ # @NOTE! In the near future you should be able to select which LLM profile *OR* "off"/None for this watch/group
+ # For now we use the 'future' field naming but keep the functionality simple.
+ llm_backend_profile = BooleanField(_l('AI enabled for this watch?'), default=True)
include_filters = StringListField(_l('CSS/JSONPath/JQ/XPath Filters'), [ValidateCSSJSONXPATHInput()], default='')
diff --git a/changedetectionio/llm/evaluator.py b/changedetectionio/llm/evaluator.py
index 210db66ea..fefb5c2a4 100644
--- a/changedetectionio/llm/evaluator.py
+++ b/changedetectionio/llm/evaluator.py
@@ -5,7 +5,8 @@ Two public entry points:
- run_setup(watch, datastore) — one-time: decide if pre-filter needed
- evaluate_change(watch, datastore, diff, current_snapshot) — per-change evaluation
-Intent resolution: watch.llm_intent → first tag with llm_intent → None (no evaluation)
+Intent resolution: watch.llm_intent → first tag with llm_intent whose AI switch is "on"
+ (see tag_llm_applies_to_watches) → None (no evaluation)
Cache: each (intent, diff) pair is evaluated exactly once, result stored in watch.
Environment variable overrides (take priority over datastore settings):
@@ -244,6 +245,70 @@ def resolve_llm_timeout(llm_cfg: dict) -> int:
# Intent resolution
# ---------------------------------------------------------------------------
+# A group/tag has exactly one AI control (`llm_backend_profile`), and it is ternary:
+#
+# True — AI on for every watch in the group, using the group's AI settings
+# (llm_intent / llm_change_summary cascade down to the watches)
+# False — AI off for every watch in the group; its prompts are stored but never used
+# None — the group has no opinion: each watch's own AI switch and prompts apply
+#
+# On a *watch* the same key is a plain bool (on/off, default on). One control per level, so
+# there is nothing to reconcile between an "override?" flag and an "enabled?" flag.
+def tag_llm_decision(tag):
+ """This group's AI decision: True (on, use its settings), False (off), or None (no opinion)."""
+ if not tag:
+ return None
+ value = tag.get('llm_backend_profile')
+ return None if value is None else bool(value)
+
+
+def tag_llm_applies_to_watches(tag) -> bool:
+ """True when this group hands its AI settings down to its watches.
+
+ Only the "on" state does that: a group set to "off" suppresses AI for its watches rather
+ than lending them prompts, and a group with no opinion leaves them alone entirely. This is
+ the single gate behind both the evaluator cascade and the watch edit page's
+ "From group ..." placeholder.
+ """
+ return tag_llm_decision(tag) is True
+
+
+def _watch_tags(watch, datastore):
+ """Yield this watch's tag dicts, in the watch's own tag order, skipping unknown UUIDs."""
+ for tag_uuid in watch.get('tags', []):
+ tag = datastore.data['settings']['application'].get('tags', {}).get(tag_uuid)
+ if tag:
+ yield tag
+
+
+def _tags_applying_llm(watch, datastore):
+ """Yield this watch's groups, in order, that hand their AI settings to their watches."""
+ for tag in _watch_tags(watch, datastore):
+ if tag_llm_applies_to_watches(tag):
+ yield tag
+
+
+def llm_enabled_for_watch(watch, datastore) -> tuple[bool, str]:
+ """Is automatic AI evaluation switched on for this watch? Returns (enabled, source).
+
+ See #4204 — users with hundreds of watches want AI on only a select few.
+
+ A group with an opinion decides for all of its watches ("the group setting overrides any
+ watch on/off"), so the first such group wins over the watch's own switch; groups set to
+ "leave it to each watch" are skipped. With no group deciding, the watch decides — and a
+ missing key means on, so watches predating this switch keep working.
+
+ Only gates *automatic* spend (the worker's intent/summary passes and the restock AI
+ plugin). Explicit user actions — the diff page "Summary" button, the intent preview —
+ stay available, since those cost tokens only when someone deliberately clicks.
+ """
+ for tag in _watch_tags(watch, datastore):
+ decision = tag_llm_decision(tag)
+ if decision is not None:
+ return decision, tag.get('title', 'tag')
+ return bool(watch.get('llm_backend_profile', True)), 'watch'
+
+
def resolve_llm_field(watch, datastore, field: str) -> tuple[str, str]:
"""
Generic cascade resolver for any LLM per-watch field.
@@ -254,12 +319,10 @@ def resolve_llm_field(watch, datastore, field: str) -> tuple[str, str]:
if value:
return value, 'watch'
- for tag_uuid in watch.get('tags', []):
- tag = datastore.data['settings']['application'].get('tags', {}).get(tag_uuid)
- if tag:
- tag_value = (tag.get(field) or '').strip()
- if tag_value:
- return tag_value, tag.get('title', 'tag')
+ for tag in _tags_applying_llm(watch, datastore):
+ tag_value = (tag.get(field) or '').strip()
+ if tag_value:
+ return tag_value, tag.get('title', 'tag')
return '', ''
@@ -273,12 +336,10 @@ def resolve_intent(watch, datastore) -> tuple[str, str]:
if intent:
return intent, 'watch'
- for tag_uuid in watch.get('tags', []):
- tag = datastore.data['settings']['application'].get('tags', {}).get(tag_uuid)
- if tag:
- tag_intent = (tag.get('llm_intent') or '').strip()
- if tag_intent:
- return tag_intent, tag.get('title', 'tag')
+ for tag in _tags_applying_llm(watch, datastore):
+ tag_intent = (tag.get('llm_intent') or '').strip()
+ if tag_intent:
+ return tag_intent, tag.get('title', 'tag')
return '', ''
@@ -549,15 +610,14 @@ def run_setup(watch, datastore, snapshot_text: str) -> None:
def _first_tag_with_field(watch, datastore, field: str):
"""Return (value, tag) for the first linked tag with a non-empty `field`, else ('', None).
- Same first-match-wins order as resolve_llm_field(); this variant also hands back the
- tag itself so the caller can read sibling keys such as the prompt mode.
+ Same first-match-wins order as resolve_llm_field() (so only groups opted in via
+ tag_llm_applies_to_watches() count); this variant also hands back the tag itself so
+ the caller can read sibling keys such as the prompt mode.
"""
- for tag_uuid in watch.get('tags', []):
- tag = datastore.data['settings']['application'].get('tags', {}).get(tag_uuid)
- if tag:
- value = (tag.get(field) or '').strip()
- if value:
- return value, tag
+ for tag in _tags_applying_llm(watch, datastore):
+ value = (tag.get(field) or '').strip()
+ if value:
+ return value, tag
return '', None
diff --git a/changedetectionio/model/Tag.py b/changedetectionio/model/Tag.py
index 58154425d..81208c25b 100644
--- a/changedetectionio/model/Tag.py
+++ b/changedetectionio/model/Tag.py
@@ -46,6 +46,10 @@ class model(EntityPersistenceMixin, watch_base):
super(model, self).__init__(*arg, **kw)
self['overrides_watch'] = kw.get('default', {}).get('overrides_watch')
+ # Ternary on a tag, unlike the plain bool on a watch: None ("leave it to each watch")
+ # is the default, so a group never touches its watches' AI until explicitly set.
+ # See llm/evaluator.py:tag_llm_decision().
+ self['llm_backend_profile'] = kw.get('default', {}).get('llm_backend_profile', None)
self['url_match_pattern'] = kw.get('default', {}).get('url_match_pattern', '')
if kw.get('default'):
diff --git a/changedetectionio/model/__init__.py b/changedetectionio/model/__init__.py
index 0fde60429..2d6684656 100644
--- a/changedetectionio/model/__init__.py
+++ b/changedetectionio/model/__init__.py
@@ -184,16 +184,11 @@ class watch_base(dict):
'check_count': 0,
'check_unique_lines': False, # On change-detected, compare against all history if its something new
'consecutive_filter_failures': 0, # Every time the CSS/xPath filter cannot be located, reset when all is fine.
+ # LLM intent-based evaluation
'content-type': None,
'date_created': None,
'extract_lines_containing': [], # Keep only lines containing these substrings (plain text, case-insensitive)
'extract_text': [], # Extract text by regex after filters
- # LLM intent-based evaluation
- 'llm_intent': '', # Plain-English description of what the user cares about (change filter)
- 'llm_change_summary': '', # Prompt for AI Change Summary — replaces {{ diff }} in notifications
- 'llm_change_summary_mode': 'replace', # 'replace' the inherited prompt, or 'append' to it
- 'llm_prefilter': None, # CSS selector derived at setup time (semantic only, e.g. "footer")
- 'llm_evaluation_cache': {}, # {sha256(intent+diff): {important, summary}} - evaluated once, cached
'fetch_backend': 'system', # plaintext, playwright etc
'fetch_time': 0.0,
'filter_failure_notification_send': strtobool(os.getenv('FILTER_FAILURE_NOTIFICATION_SEND_DEFAULT', 'True')),
@@ -202,16 +197,22 @@ class watch_base(dict):
'filter_text_replaced': True,
'follow_price_changes': True,
'has_ldjson_price_data': None,
- 'history_snapshot_max_length': None,
'headers': {}, # Extra headers to send
- 'ignore_text': [], # List of text to ignore when calculating the comparison checksum
+ 'history_snapshot_max_length': None,
'ignore_status_codes': None,
+ 'ignore_text': [], # List of text to ignore when calculating the comparison checksum
'in_stock_only': True, # Only trigger change on going to instock from out-of-stock
'include_filters': [],
'last_checked': 0,
'last_error': False,
'last_notification_error': None,
'last_viewed': 0, # history key value of the last viewed via the [diff] link
+ 'llm_backend_profile': True, # @note - now its just a bool but in the near future we can select a LLM profile or 'off'/false
+ 'llm_change_summary': '', # Prompt for AI Change Summary — replaces {{ diff }} in notifications
+ 'llm_change_summary_mode': 'replace', # 'replace' the inherited prompt, or 'append' to it
+ 'llm_evaluation_cache': {}, # {sha256(intent+diff): {important, summary}} - evaluated once, cached
+ 'llm_intent': '', # Plain-English description of what the user cares about (change filter)
+ 'llm_prefilter': None, # CSS selector derived at setup time (semantic only, e.g. "footer")
'method': 'GET',
'notification_alert_count': 0,
'notification_body': None,
diff --git a/changedetectionio/processors/restock_diff/processor.py b/changedetectionio/processors/restock_diff/processor.py
index e428e336b..5299511fe 100644
--- a/changedetectionio/processors/restock_diff/processor.py
+++ b/changedetectionio/processors/restock_diff/processor.py
@@ -506,8 +506,11 @@ class perform_site_check(difference_detection_processor):
# Try plugin override - plugins can decide if they support this fetcher
if fetcher_name:
logger.debug(f"Calling extra plugins for getting item price/availability (fetcher: {fetcher_name})")
- from changedetectionio.llm.evaluator import resolve_intent
- _llm_intent, _ = resolve_intent(watch, self.datastore)
+ from changedetectionio.llm.evaluator import llm_enabled_for_watch, resolve_intent
+ # AI off for this watch (or for its group) means no intent is handed to the
+ # LLM restock plugin, so it doesn't spend tokens here either — #4204.
+ _llm_on, _ = llm_enabled_for_watch(watch, self.datastore)
+ _llm_intent, _ = resolve_intent(watch, self.datastore) if _llm_on else ('', '')
plugin_availability = get_itemprop_availability_from_plugin(self.fetcher.content, fetcher_name, self.fetcher, watch.link, llm_intent=_llm_intent or None)
if plugin_availability:
diff --git a/changedetectionio/static/js/plugins.js b/changedetectionio/static/js/plugins.js
index 5191c1189..d0d99a284 100644
--- a/changedetectionio/static/js/plugins.js
+++ b/changedetectionio/static/js/plugins.js
@@ -179,6 +179,25 @@ function toggleOpacity(checkboxSelector, fieldSelector, inverted) {
checkbox.addEventListener('change', updateOpacity);
}
+// Radio-group counterpart of toggleOpacity: fields are full opacity only while the named
+// radio group sits on activeValue, otherwise greyed out. Used by the tag AI/LLM tab, where a
+// ternary (On / Off / Leave it to each watch) decides whether the prompts below apply.
+function toggleOpacityByRadioValue(radioName, activeValue, fieldSelector) {
+ const radios = document.querySelectorAll(`input[type="radio"][name="${radioName}"]`);
+ const fields = document.querySelectorAll(fieldSelector);
+
+ function updateOpacity() {
+ const active = Array.from(radios).some(radio => radio.checked && radio.value === activeValue);
+ fields.forEach(field => {
+ field.style.opacity = active ? 1 : 0.6;
+ });
+ }
+
+ // Initial setup
+ updateOpacity();
+ radios.forEach(radio => radio.addEventListener('change', updateOpacity));
+}
+
function toggleVisibility(checkboxSelector, fieldSelector, inverted) {
const checkbox = document.querySelector(checkboxSelector);
const fields = document.querySelectorAll(fieldSelector);
diff --git a/changedetectionio/static/styles/scss/styles.scss b/changedetectionio/static/styles/scss/styles.scss
index b1d1d1b55..f0e3baff1 100644
--- a/changedetectionio/static/styles/scss/styles.scss
+++ b/changedetectionio/static/styles/scss/styles.scss
@@ -1152,4 +1152,8 @@ header {
cursor: pointer;
width: 1.4rem; /*it's slightly more wider than square so default auto will trim it slightly */
}
+}
+
+textarea::placeholder {
+ white-space: pre-wrap;
}
\ No newline at end of file
diff --git a/changedetectionio/static/styles/styles.css b/changedetectionio/static/styles/styles.css
index 92ab9546b..7dec70064 100644
--- a/changedetectionio/static/styles/styles.css
+++ b/changedetectionio/static/styles/styles.css
@@ -1 +1 @@
-.header{color:var(--color-text-menu-heading)}.header a{color:var(--color-text-menu-heading)}.header::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;pointer-events:none;background:linear-gradient(to right, rgba(200, 200, 200, 0.02), rgba(200, 200, 200, 0.6))}ul#top-right-menu{list-style:none;margin-left:auto;padding:0;margin-top:0;margin-right:0;margin-bottom:0;display:grid;gap:1.1rem;grid-auto-flow:column;grid-auto-columns:max-content;align-items:center}ul#top-right-menu .toggle-button{padding:0}.current-diff-url{flex:1 1 auto;min-width:0;overflow:hidden;white-space:nowrap;text-align:left;margin:0 .55rem;-webkit-mask-image:linear-gradient(to right, #000 calc(100% - 2.5em), transparent);mask-image:linear-gradient(to right, #000 calc(100% - 2.5em), transparent)}.current-diff-url span{overflow:visible;white-space:nowrap}.pure-menu-horizontal{padding:.55rem;display:flex;justify-content:space-between;align-items:center}.pure-menu-horizontal svg{height:1.3rem}.fi{height:1.3rem;cursor:pointer}#pure-menu-horizontal-spinner{height:2px;background:linear-gradient(-75deg, #ff6000, #ff8f00, #ffdd00, #ed0000);background-size:400% 400%;animation:gradient 200s ease infinite;opacity:.8;position:fixed;bottom:0;left:0;width:100%;z-index:100;pointer-events:none}.status-pill{display:inline-flex;align-items:center;gap:8px;height:30px;padding:0 12px;border-radius:var(--common-round-border);border:1px solid hsla(0,0%,100%,.25);background:hsla(0,0%,100%,.1);color:var(--color-text-menu-heading);font-size:.78rem;font-weight:600;white-space:nowrap;text-decoration:none}.status-pill:hover{background:hsla(0,0%,100%,.18)}.status-pill .live-dot{width:8px;height:8px;border-radius:50%;background:#42dd53;box-shadow:0 0 0 3px rgba(66,221,83,.3);animation:status-pill-pulse 2s infinite}.status-pill.paused .live-dot{background:#e8a33d;box-shadow:0 0 0 3px rgba(232,163,61,.3);animation:none}.status-pill .action-icon{width:16px;height:16px}.status-pill.muted{opacity:.8}.status-pill.muted .action-icon{color:#e8a33d}@keyframes status-pill-pulse{0%,100%{opacity:1}50%{opacity:.4}}.menu-pop-wrap{position:relative}.icon-btn{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border:0;border-radius:var(--common-round-border);background:rgba(0,0,0,0);color:var(--color-text-menu-heading);cursor:pointer}.icon-btn svg{width:18px;height:18px;fill:none;stroke:currentColor}.icon-btn:hover{background:hsla(0,0%,100%,.14)}.menu-pop{display:none;position:absolute;top:calc(100% + 8px);right:0;min-width:220px;padding:6px;border:1px solid var(--color-border-table-cell);border-radius:12px;background:var(--color-background);box-shadow:0 18px 50px rgba(0,0,0,.18);z-index:80}.menu-pop.open{display:block}.menu-pop .mi{display:flex;align-items:center;gap:10px;width:100%;padding:9px 10px;border:0;border-radius:8px;background:rgba(0,0,0,0);color:var(--color-text);font-size:.85rem;text-align:left;text-decoration:none;white-space:nowrap;cursor:pointer}.menu-pop .mi:hover{background:var(--color-background-menu-link-hover)}.menu-pop .mi .ico{display:inline-flex;color:var(--color-text-input-description)}.menu-pop .mi .ico svg{width:16px;height:16px;fill:none;stroke:currentColor}.menu-pop .mi .right{margin-left:auto;font-size:.75rem;color:var(--color-text-input-description)}.top-menu-list .action-label{color:var(--color-text-menu-heading)}@media only screen and (max-width: 768px){.top-menu-list .action-label{display:none}}#inline-menu-extras-group{list-style:none;margin:0;padding:0;display:grid;gap:.55rem;grid-auto-flow:column;grid-auto-columns:max-content;align-items:center}@media only screen and (max-width: 980px){#nav-menu #menu-settings span{display:none}}:root{--body-main-text-size: 0.9rem;--color-white: #fff;--color-grey-50: #111;--color-grey-100: #262626;--color-grey-200: #333;--color-grey-300: #444;--color-grey-325: #555;--color-grey-350: #565d64;--color-grey-400: #666;--color-grey-500: #777;--color-grey-600: #999;--color-grey-700: #cbcbcb;--color-grey-750: #ddd;--color-grey-800: #e0e0e0;--color-grey-850: #eee;--color-grey-900: #f2f2f2;--color-black: #000;--color-dark-red: #a00;--color-light-red: #dd0000;--color-background-page: var(--color-grey-100);--color-background-gradient-first: #5ad8f7;--color-background-gradient-second: #2f50af;--color-background-gradient-third: #9150bf;--color-background: var(--color-white);--color-text: var(--color-grey-200);--color-link: #1b98f8;--color-menu-accent: #ed5900;--color-background-code: var(--color-grey-850);--color-error: var(--color-dark-red);--color-error-input: #ffebeb;--color-error-list: var(--color-light-red);--color-table-background: var(--color-background);--color-table-stripe: var(--color-grey-900);--watchlist-row-selected: #e3f0ff;--watchlist-row-hover: #f2f2f2;--color-text-tab: var(--color-white);--color-background-tab: rgba(255, 255, 255, 0.2);--color-background-tab-hover: rgba(255, 255, 255, 0.5);--color-text-tab-active: #222;--color-api-key: #0078e7;--color-background-button-primary: #0078e7;--color-background-button-green: #42dd53;--color-background-button-red: #dd4242;--color-background-button-success: rgb(28, 184, 65);--color-background-button-error: rgb(202, 60, 60);--color-text-button-error: var(--color-white);--color-background-button-warning: rgb(202, 60, 60);--color-text-button-warning: var(--color-white);--color-background-button-secondary: rgb(66, 184, 221);--color-background-button-cancel: rgb(200, 200, 200);--color-text-button: var(--color-white);--color-background-button-tag: rgb(99, 99, 99);--color-background-snapshot-age: #dfdfdf;--color-error-text-snapshot-age: var(--color-white);--color-error-background-snapshot-age: #ff0000;--color-background-button-tag-active: #9c9c9c;--color-text-messages: var(--color-white);--color-background-messages-message: rgba(255, 255, 255, .2);--color-background-messages-error: rgba(255, 1, 1, .5);--color-background-messages-notice: rgba(255, 255, 255, .5);--color-border-notification: #ccc;--color-background-checkbox-operations: rgba(0, 0, 0, 0.05);--color-warning: #ff3300;--color-border-warning: var(--color-warning);--color-text-legend: var(--color-white);--color-link-new-version: #e07171;--color-last-checked: #bbb;--color-text-footer: #444;--color-border-watch-table-cell: #eee;--color-text-watch-tag-list: rgba(231, 0, 105, 0.4);--color-background-new-watch-form: rgba(0, 0, 0, 0.05);--color-background-new-watch-input: var(--color-white);--color-background-new-watch-input-transparent: rgba(255, 255, 255, 0.1);--color-text-new-watch-input: var(--color-text);--color-border-input: var(--color-grey-500);--color-shadow-input: var(--color-grey-400);--color-background-input: var(--color-white);--color-text-input: var(--color-text);--color-text-input-description: var(--color-grey-500);--color-text-input-placeholder: var(--color-grey-600);--color-background-table-thead: var(--color-grey-800);--color-border-table-cell: var(--color-grey-700);--color-text-menu-heading: var(--color-white);--color-text-menu-link: var(--color-grey-500);--color-background-menu-link-hover: var(--color-grey-850);--color-text-menu-link-hover: var(--color-grey-300);--color-shadow-jump: var(--color-grey-500);--color-icon-github: var(--color-black);--color-watch-table-error: var(--color-dark-red);--color-watch-table-row-text: var(--color-grey-100);--color-table-line: #e7e9ee;--highlight-trigger-text-bg-color: #1b98f8;--highlight-ignored-text-bg-color: var(--color-grey-700);--highlight-blocked-text-bg-color: rgb(202, 60, 60);--color-sidebar-bg: rgba(255, 255, 255, 0.97);--color-sidebar-text: var(--color-text);--color-sidebar-shadow: 6px 0 28px rgba(0, 0, 0, 0.18);--color-sidebar-item-hover-bg: rgba(0, 0, 0, 0.06);--color-sidebar-item-active-bg: rgba(0, 0, 0, 0.10);--common-round-border: 8px}html[data-darkmode=true]{--color-link: #59bdfb;--color-text: var(--color-white);--color-table-line: #262c37;--color-background-gradient-first: #3f90a5;--color-background-gradient-second: #1e316c;--color-background-gradient-third: #4d2c64;--color-background-new-watch-input: var(--color-grey-100);--color-background-new-watch-input-transparent: var(--color-grey-100);--color-text-new-watch-input: var(--color-text);--color-background-table-thead: var(--color-grey-200);--color-table-background: var(--color-grey-300);--color-table-stripe: var(--color-grey-325);--watchlist-row-selected: #1e3a5f;--watchlist-row-hover: #2a2a2a;--color-background: var(--color-grey-300);--color-text-menu-heading: var(--color-grey-850);--color-text-menu-link: var(--color-grey-800);--color-border-table-cell: var(--color-grey-400);--color-text-tab-active: var(--color-text);--color-border-input: var(--color-grey-400);--color-shadow-input: var(--color-grey-50);--color-background-input: var(--color-grey-350);--color-text-input-description: var(--color-grey-600);--color-text-input-placeholder: var(--color-grey-600);--color-text-watch-tag-list: rgba(250, 62, 146, 0.4);--color-background-code: var(--color-grey-200);--color-background-tab: rgba(0, 0, 0, 0.2);--color-background-tab-hover: rgba(0, 0, 0, 0.5);--color-background-snapshot-age: var(--color-grey-200);--color-shadow-jump: var(--color-grey-200);--color-icon-github: var(--color-white);--color-watch-table-error: var(--color-light-red);--color-watch-table-row-text: var(--color-grey-800);--color-sidebar-bg: rgba(8, 10, 14, 0.97);--color-sidebar-text: var(--color-white);--color-sidebar-shadow: 6px 0 28px rgba(0, 0, 0, 0.45);--color-sidebar-item-hover-bg: rgba(255, 255, 255, 0.06);--color-sidebar-item-active-bg: rgba(255, 255, 255, 0.10)}html[data-darkmode=true] .icon-spread{filter:hue-rotate(-10deg) brightness(1.5)}html[data-darkmode=true] .watch-table .title-col a[target=_blank]::after,html[data-darkmode=true] .watch-table .current-diff-url::after{filter:invert(0.5) hue-rotate(10deg) brightness(2)}html[data-darkmode=true] .watch-table .status-browsersteps{filter:invert(0.5) hue-rotate(10deg) brightness(1.5)}html[data-darkmode=true] .watch-table .watch-controls .state-off svg{opacity:.3}html[data-darkmode=true] .watch-table .watch-controls .state-on svg{opacity:1}html[data-darkmode=true] .watch-table .unviewed{color:#fff}html[data-darkmode=true] .watch-table .unviewed.error{color:var(--color-watch-table-error)}.arrow{border:solid var(--color-border-input);border-width:0 2px 2px 0;display:inline-block;padding:3px}.arrow.right{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}.arrow.left{transform:rotate(135deg);-webkit-transform:rotate(135deg)}.arrow.up,.arrow.asc{transform:rotate(-135deg);-webkit-transform:rotate(-135deg)}.arrow.down,.arrow.desc{transform:rotate(45deg);-webkit-transform:rotate(45deg)}#browser_steps th{display:none}#browser_steps li{list-style:decimal;padding:5px}#browser_steps li.browser-step-with-error{background-color:#ffd6d6;border-radius:4px}#browser_steps li:not(:first-child):hover{opacity:1}#browser_steps li .control{padding-left:5px;padding-right:5px}#browser_steps li .control a{font-size:70%}#browser_steps li.empty{padding:0px;opacity:.35}#browser_steps li.empty .control{display:none}#browser_steps li:hover{background:#eee}#browser_steps li>label{display:none}@media only screen and (min-width: 760px){#browser-steps .flex-wrapper{display:flex;flex-flow:row;height:70vh;font-size:80%}#browser-steps .flex-wrapper #browser-steps-ui{flex-grow:1;flex-shrink:1;flex-basis:0;background-color:#eee;border-radius:5px}#browser-steps-fieldlist{flex-grow:0;flex-shrink:0;flex-basis:auto;max-width:400px;padding-left:1rem;overflow-y:scroll}#browsersteps-selector-wrapper{height:100% !important}}#browsersteps-selector-wrapper{width:100%;overflow-y:scroll;position:relative;height:80vh}#browsersteps-selector-wrapper>img{position:absolute;max-width:100%}#browsersteps-selector-wrapper>canvas{position:relative;max-width:100%}#browsersteps-selector-wrapper>canvas:hover{cursor:pointer}#browsersteps-selector-wrapper .loader{position:absolute;left:50%;top:50%;transform:translate(-50%, -50%);z-index:100;max-width:350px;text-align:center}#browsersteps-selector-wrapper .spinner,#browsersteps-selector-wrapper .spinner:after{width:80px;height:80px;font-size:3px}#browsersteps-selector-wrapper #browsersteps-click-start{color:var(--color-grey-400)}#browsersteps-selector-wrapper #browsersteps-click-start:hover{cursor:pointer}ul#requests-extra_proxies{list-style:none}ul#requests-extra_proxies li>label{display:none}ul#requests-extra_proxies table tr{display:table-row}ul#requests-extra_proxies table tr input[type=text]{width:100%}@media only screen and (min-width: 1024px){ul#requests-extra_proxies table tr{display:inline}}#request label[for=proxy]{display:inline-block}body.proxy-check-active #request .proxy-check-details{font-size:80%;color:#555;display:block;padding-left:2em;max-width:500px}body.proxy-check-active #request .proxy-timing{font-size:80%;padding-left:1rem;color:var(--color-link)}#recommended-proxy{display:grid;gap:2rem;padding-bottom:1em}@media(min-width: 991px){#recommended-proxy{grid-template-columns:repeat(2, 1fr)}}#recommended-proxy>div{border:1px #aaa solid;border-radius:4px;padding:1em}#extra-proxies-setting{border:1px solid var(--color-grey-800);border-radius:4px;margin:1em;padding:1em}ul#requests-extra_browsers{list-style:none}ul#requests-extra_browsers li>label{display:none}ul#requests-extra_browsers table tr{display:table-row}ul#requests-extra_browsers table tr input[type=text]{width:100%}@media only screen and (min-width: 1280px){ul#requests-extra_browsers table tr{display:inline}ul#requests-extra_browsers table tr input[type=text]{width:100%}}#extra-browsers-setting{border:1px solid var(--color-grey-800);border-radius:4px;margin:1em;padding:1em}.pagination-page-info{text-transform:capitalize}.pagination.menu>*{display:inline-block}.pagination.menu li{display:inline-block}.pagination.menu a{padding:.65rem;margin:3px;border:none;background:#444;border-radius:2px;color:var(--color-text-button)}.pagination.menu a.disabled{display:none}.pagination.menu a.active{font-weight:bold;background:#888}.pagination.menu a:hover{background:#999}.spinner,.spinner:after{border-radius:50%;width:10px;height:10px}.spinner{margin:0px auto;font-size:3px;vertical-align:middle;display:inline-block;text-indent:-9999em;border-top:1.1em solid rgba(38,104,237,.2);border-right:1.1em solid rgba(38,104,237,.2);border-bottom:1.1em solid rgba(38,104,237,.2);border-left:1.1em solid #2668ed;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);-webkit-animation:load8 1.1s infinite linear;animation:load8 1.1s infinite linear}@-webkit-keyframes load8{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes load8{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.toggle-light-mode .icon-dark{display:none}html[data-darkmode=true] .toggle-light-mode .icon-light{display:none}html[data-darkmode=true] .toggle-light-mode .icon-dark{display:block}.pure-menu-link{padding:.5rem 1em;line-height:1.2rem}#menu-mute img,#menu-pause img{height:1.2rem}.pure-menu-item{height:initial}.pure-menu-item svg{height:1.2rem}.pure-menu-item *{vertical-align:middle}.pure-menu-item .bi-heart:hover{cursor:pointer}.pure-menu-item.active .pure-menu-link{background-color:var(--color-background-menu-link-hover);color:var(--color-text-menu-heading)}.pure-menu-item .action-icon{stroke:var(--color-text-menu-heading)}#overlay{opacity:.95;position:fixed;width:350px;max-width:100%;height:100%;top:50px;right:-350px;background-color:var(--color-table-stripe);border:1px solid #aaa;z-index:2;transform:translateX(0);transition:transform .5s ease}#overlay.visible{transform:translateX(-100%)}#overlay .content{font-size:.875rem;padding:1.1rem;max-width:400px;color:var(--color-watch-table-row-text)}#heartpath{height:1.6rem;width:1.6rem;z-index:100;transition:all ease .3s !important}#heartpath:hover{fill:red !important;transition:all ease .3s !important}.minitabs-wrapper{width:100%}.minitabs-wrapper>div[id]{padding:20px;border:1px solid #ccc;border-top:none}.minitabs-wrapper .minitabs-content{width:100%;display:flex}.minitabs-wrapper .minitabs-content>div{flex:1 1 auto;min-width:0;overflow:scroll}.minitabs-wrapper .minitabs{display:flex;border-bottom:1px solid #ccc}.minitabs-wrapper .minitab{flex:1;text-align:center;padding:12px 0;text-decoration:none;color:#333;background-color:#f1f1f1;border:1px solid #ccc;border-bottom:none;cursor:pointer;transition:background-color .3s}.minitabs-wrapper .minitab:hover{background-color:#ddd}.minitabs-wrapper .minitab.active{background-color:#fff;font-weight:bold}@media(min-width: 800px){body.preview-text-enabled #filters-and-triggers>div{display:flex;gap:20px;position:relative}}body.preview-text-enabled #edit-text-filter,body.preview-text-enabled #text-preview{flex:1;align-self:flex-start}body.preview-text-enabled #edit-text-filter #pro-tips{display:none}body.preview-text-enabled #text-preview{position:sticky;top:20px;padding-top:1rem;padding-bottom:1rem;display:block !important}body.preview-text-enabled #activate-text-preview{background-color:var(--color-grey-500)}body.preview-text-enabled .monospace-preview{background:var(--color-background-input);border:1px solid var(--color-grey-600);padding:1rem;color:var(--color-text-input);font-family:"Courier New",Courier,monospace;font-size:70%;word-break:break-word;white-space:pre-wrap}#activate-text-preview{right:0;position:absolute;z-index:3;box-shadow:1px 1px 4px var(--color-shadow-jump)}.cdio-table{width:100%;font-size:var(--body-main-text-size)}.cdio-table thead{text-transform:uppercase}.cdio-table thead a{color:var(--color-text)}.cdio-table td,.cdio-table th{vertical-align:middle;border:none}.cdio-table tbody tr{color:var(--color-watch-table-row-text);border-bottom:1px solid var(--color-table-line);background-color:var(--color-table-background)}.cdio-table tbody tr td{background-color:var(--color-table-background)}.cdio-table tbody tr:hover>td{background-color:var(--watchlist-row-hover)}.cdio-table-clip{border-radius:var(--common-round-border);overflow:hidden;margin-bottom:1.1rem}.seg{display:inline-flex;align-items:center;gap:2px;padding:2px;border:1px solid var(--color-border-table-cell);border-radius:var(--common-round-border);background:var(--color-background-table-thead)}.seg a,.seg button{display:inline-flex;align-items:center;gap:6px;border:0;background:rgba(0,0,0,0);color:var(--color-text-input-description);font-size:.8rem;font-weight:600;line-height:1.4;padding:5px 11px;border-radius:calc(var(--common-round-border) - 1px);text-decoration:none;cursor:pointer;white-space:nowrap}.seg a:hover,.seg button:hover{color:var(--color-text)}.seg a.active,.seg a:hover,.seg button.active,.seg button:hover{background:var(--color-background);color:var(--color-text);box-shadow:0 1px 2px rgba(0,0,0,.15)}html[data-darkmode=true] .seg a.active,html[data-darkmode=true] .seg button.active{background:var(--color-grey-400);box-shadow:0 1px 2px rgba(0,0,0,.5)}.seg-count{font-size:.62rem;line-height:1;font-weight:700;padding:2px 6px;border-radius:999px;background:var(--color-background-button-tag);color:var(--color-white)}.seg-count--unread{background:#3e95bb}.seg-count--error{background:var(--color-background-button-error)}.seg-count--deal{background:var(--color-background-button-success)}.cdio-btn{display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 12px;border-radius:var(--common-round-border);border:1px solid var(--color-border-table-cell);background:var(--color-background);color:var(--color-text-input-description);font-family:inherit;font-size:.8rem;font-weight:600;line-height:1;white-space:nowrap;text-decoration:none;cursor:pointer;transition:border-color .12s ease,color .12s ease,background-color .12s ease}.cdio-btn:hover{border-color:var(--color-border-input);color:var(--color-text)}.cdio-btn:focus-visible{outline:2px solid var(--color-link);outline-offset:2px}.cdio-btn svg{width:15px;height:15px;stroke:currentColor}.cdio-btn img{height:15px;display:block}.cdio-btn--icon{width:32px;padding:0;justify-content:center}.cdio-btn--sm{height:26px;padding:0 9px;font-size:.72rem;gap:5px}.cdio-btn--sm svg{width:13px;height:13px}.cdio-btn--primary{background:var(--color-background-button-primary);border-color:var(--color-background-button-primary);color:var(--color-text-button)}.cdio-btn--primary:hover{background:var(--color-link);border-color:var(--color-link);color:var(--color-text-button)}.cdio-btn--danger{color:var(--color-background-button-error)}.cdio-btn--danger:hover{color:var(--color-background-button-error);border-color:var(--color-background-button-error)}.cdio-btn--warning{color:#d68a00}.cdio-btn--warning:hover{color:#d68a00;border-color:#d68a00}.watch-table tr:has(input[name=uuids]:checked)>td{background-color:var(--watchlist-row-selected)}.watch-table tbody tr:hover td.buttons *,.watch-table tbody tr:focus-within td.buttons *,.watch-table tbody tr:has(input[name=uuids]:checked) td.buttons *{opacity:1}.select-all-banner{margin:.4rem 0;padding:.5rem .75rem;border-radius:var(--common-round-border);background:var(--watchlist-row-selected);color:var(--color-watch-table-row-text);font-size:var(--body-main-text-size)}.select-all-banner button{margin-left:.5rem;vertical-align:baseline}.watch-controls svg{width:18px;height:18px;stroke:currentColor;fill:none;vertical-align:middle}#stats_row{display:flex;align-items:center;width:100%;color:#fff;font-size:.85rem}#stats_row>*{padding-bottom:.5rem}#stats_row .left{text-align:left}#stats_row .left .records-selected{margin-top:.25rem;opacity:.9}#stats_row .right{opacity:.5;transition:opacity .6s ease;margin-left:auto;text-align:right}body.has-queue #stats_row .right{opacity:1}#checkbox-operations{margin-bottom:.55rem;background:var(--color-background-new-watch-form);padding:1em;border-radius:10px;max-width:100%;position:sticky;top:20px;display:none}#checkbox-operations button{margin-bottom:3px;margin-top:3px;display:inline-flex;align-items:center}#checkbox-operations i,#checkbox-operations svg{width:14px;height:14px;stroke:#fff}body.watch-selection-active #checkbox-operations{display:block}.watch-table .checkbox-uuid{text-align:center}.watch-table .checkbox-uuid>*{vertical-align:middle}@media only screen and (max-width: 1200px){.watch-table .last-checked,.watch-table .last-changed{text-align:center}}.watch-table #th-webpage{text-align:center}.watch-table tbody tr.unviewed{font-weight:bold}.watch-table tbody tr td.inline.title-col{width:100%}.watch-table tbody tr td.inline.title-col .grid-wrapper{display:grid;grid-template-columns:auto minmax(0, 1fr) auto;grid-auto-columns:auto;align-items:center;gap:.55rem}.watch-table tbody tr td.inline.title-col .grid-wrapper>.favicon{grid-column:1}.watch-table tbody tr td.inline.title-col .grid-wrapper>.watch-text-info{grid-column:2}.watch-table tbody tr td.inline.title-col .grid-wrapper>.status-icons{grid-column:3}.watch-table tbody tr td.inline.title-col .grid-wrapper>.restock-info-wrap{grid-column:4}@media only screen and (max-width: 1200px){.watch-table tbody tr td.inline.title-col .grid-wrapper>.restock-info-wrap{grid-column:1/-1;justify-self:center}}.watch-table tbody tr .watch-text-info{line-height:1.5}.watch-table tbody tr.checking-now td:first-child{position:relative}.watch-table tbody tr.checking-now td:first-child::before{content:"";position:absolute;top:0;bottom:0;left:0;width:3px;background-color:#293eff}.watch-table tbody tr.checking-now td.last-checked .spinner-wrapper{display:inline-block !important;white-space:nowrap !important}.watch-table tbody tr.checking-now td.last-checked .spinner{margin-right:.275rem}.watch-table tbody tr.checking-now td.last-checked .innertext{display:none !important}.watch-table tbody tr.queued a.recheck{display:none !important}.watch-table tbody tr.queued a.already-in-queue-button{display:inline-flex !important;opacity:.8}.watch-table tbody tr.paused a.pause-toggle.state-on{display:inline !important}.watch-table tbody tr.paused a.pause-toggle.state-off{display:none !important}.watch-table tbody tr.notification_muted a.mute-toggle.state-on{display:inline !important}.watch-table tbody tr.notification_muted a.mute-toggle.state-off{display:none !important}.watch-table tbody tr.has-error .error-text{display:block !important;color:var(--color-watch-table-error)}.watch-table tbody tr.single-history a.preview-link{display:inherit !important}.watch-table tbody tr.multiple-history a.history-link{display:inherit !important}.watch-table tbody tr.has-favicon.unviewed img.favicon{opacity:1 !important;border-radius:4px}.watch-table td.buttons{font-size:12px;white-space:nowrap}.watch-table td.buttons>div{display:inline-flex;align-items:center;gap:6px}.watch-table td.buttons>div>*{opacity:0;transition:opacity .12s ease}.watch-table td.title-col{word-break:break-all;white-space:normal}.watch-table td a.external::after{content:"";display:inline-block;width:var(--body-main-text-size);height:var(--body-main-text-size);vertical-align:-0.1em;background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23777' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E") no-repeat center/contain;margin:0 3px 0 5px}.watch-table td.watch-controls>div{display:flex;justify-content:space-between;align-items:center}@media(min-width: 1020px){.watch-table th{white-space:nowrap}}.watch-table th#h-lastchanged,.watch-table th#h-lastchecked{text-align:center}.watch-table th a{font-weight:normal}.watch-table th a.active{font-weight:bolder}.watch-table th a.inactive .arrow{display:none}.watch-table th#mute-pause{white-space:nowrap}.watch-table th#mute-pause>div{display:flex;justify-content:space-between;align-items:center}.watch-table.favicon-not-enabled tr .favicon{display:none}.watch-table .status-icons{white-space:nowrap;display:flex;align-items:center;gap:4px}.watch-table .status-icons>*{vertical-align:middle}.watch-table .title-wrapper{display:flex;align-items:center;gap:10px}.watch-table .title-col-inner{display:inline-block;vertical-align:middle}.watch-table img.favicon{vertical-align:middle;max-width:36px;max-height:36px;height:36px;border-radius:var(--common-round-border)}.watch-table img.favicon:hover{outline:2px solid color-mix(in srgb, var(--color-watch-table-row-text) 50%, transparent);outline-offset:1px}body.watch-selection-active #buttons-for-all-watches{display:none !important}#buttons-for-all-watches{display:flex;align-items:center;justify-content:flex-start;flex-wrap:wrap;gap:.55rem;margin:0}#buttons-for-all-watches #post-list-mark-views{display:none}body.has-any-unviewed #post-list-mark-views{display:inline-flex !important}#watch-table-wrapper{display:inline-block;width:100%}#watch-table-wrapper #list-related-buttons{display:flex;align-items:center;justify-content:flex-start;flex-wrap:wrap;gap:.55rem;margin:0;padding:1.1rem 0 .55rem 0}#watch-table-wrapper.has-error #list-related-buttons #post-list-with-errors{display:inline-flex !important}#watch-table-wrapper.has-unread-changes #list-related-buttons #post-list-unread{display:inline-flex !important}#watch-table-wrapper #tag-lister #tag-all{opacity:1}#watch-table-wrapper #tag-lister.active-tag .button-tag{opacity:.35}#watch-table-wrapper #tag-lister.active-tag .button-tag.active,#watch-table-wrapper #tag-lister.active-tag .button-tag:hover{opacity:1}.content .group-overview-table{width:100%}.content .group-overview-table .pure-button{margin-top:.3rem;margin-bottom:.3rem}.content .group-overview-table .watch-controls,.content .group-overview-table .watch-count{text-align:center}.content .group-overview-table td{padding:5px !important;color:var(--color-watch-table-row-text)}.pure-button{border-radius:var(--common-round-border)}body.blueprint-watchlist #add-watch-ui{margin-bottom:1.1rem;padding:0}body.blueprint-watchlist #add-watch-url-row{margin-bottom:0 !important}body.blueprint-watchlist #quick-watch-llm-intent{margin-top:.55rem}body.blueprint-watchlist #url{width:auto}@media only screen and (min-width: 980px){body.blueprint-watchlist #url{min-width:32rem;max-width:min(80%,80vw);box-sizing:border-box}}body.blueprint-watchlist #quick-watch-llm-intent,body.blueprint-watchlist #quick-watch-processor-type{display:none}body.blueprint-watchlist #new-watch-form:has(#url:not(:placeholder-shown)) #quick-watch-llm-intent,body.blueprint-watchlist #new-watch-form:has(#url:not(:placeholder-shown)) #quick-watch-processor-type{display:block}@media(max-width: 767px){.watch-table thead{display:block}.watch-table thead tr th{display:inline-block}.watch-table thead tr th .hide-on-mobile{display:none}.watch-table thead .empty-cell{display:none}.watch-table .last-checked::before{color:var(--color-text);content:attr(data-label) " "}.watch-table .last-changed::before{color:var(--color-text);content:attr(data-label) " "}.watch-table td.inline{display:inline-block}.watch-table .pure-table td,.watch-table .pure-table th{border:none}.watch-table td{border:none;border-bottom:1px solid var(--color-border-watch-table-cell);vertical-align:middle}.watch-table td:before{top:6px;left:6px;width:45%;padding-right:10px;white-space:nowrap}.watch-table.pure-table-striped tr{background-color:var(--color-table-background)}.watch-table.pure-table-striped tr:nth-child(2n-1){background-color:var(--color-table-stripe)}.watch-table.pure-table-striped tr:nth-child(2n-1) td{background-color:inherit}}@media(max-width: 767px){.watch-table tbody tr{padding-bottom:10px;padding-top:10px;display:grid;grid-template-columns:40px 1fr 100px;grid-template-rows:auto auto auto auto;gap:.5rem}.watch-table tbody tr .counter-i{display:none}.watch-table tbody tr>td{border-bottom:none}.watch-table tbody tr>td[colspan]{grid-column:1/-1}.watch-table tbody tr>td.title-col{grid-column:1/-1;grid-row:1}.watch-table tbody tr>td.title-col .watch-title{font-size:.92rem}.watch-table tbody tr>td.title-col .link-spread{display:none}.watch-table tbody tr>td.last-checked{grid-column:1/-1;grid-row:2}.watch-table tbody tr>td.last-changed{grid-column:1/-1;grid-row:3}.watch-table tbody tr>td.checkbox-uuid{grid-column:1;grid-row:4}.watch-table tbody tr>td.buttons{grid-column:2;grid-row:4;display:flex;align-items:center;justify-content:flex-start}.watch-table tbody tr>td.watch-controls{grid-column:3;grid-row:4;display:grid;place-items:center}.watch-table tbody tr>td.watch-controls a img{padding:10px}.pure-table td{padding:0 !important}}@media(min-width: 768px){.watch-table thead tr th .hide-on-desktop{display:none}.watch-table td.last-checked .innertext,.watch-table td.last-changed .innertext{white-space:nowrap}}#llm-intent-section textarea{white-space:normal;overflow-wrap:break-word;overflow-x:hidden;overflow-y:auto;resize:vertical;font-family:inherit}ul#conditions_match_logic{list-style:none}ul#conditions_match_logic input,ul#conditions_match_logic label,ul#conditions_match_logic li{display:inline-block}ul#conditions_match_logic li{padding-right:1em}.fieldlist_formfields{width:100%;background-color:var(--color-background, #fff);border-radius:4px;border:1px solid var(--color-border-table-cell, #cbcbcb)}.fieldlist_formfields .fieldlist-header{display:flex;background-color:var(--color-background-table-thead, #e0e0e0);font-weight:bold;border-bottom:1px solid var(--color-border-table-cell, #cbcbcb)}.fieldlist_formfields .fieldlist-header-cell{flex:1;padding:.5em 1em;text-align:left}.fieldlist_formfields .fieldlist-header-cell:last-child{flex:0 0 120px}.fieldlist_formfields .fieldlist-body{display:flex;flex-direction:column}.fieldlist_formfields .fieldlist-row{display:flex;border-bottom:1px solid var(--color-border-table-cell, #cbcbcb)}.fieldlist_formfields .fieldlist-row:last-child{border-bottom:none}.fieldlist_formfields .fieldlist-row:nth-child(2n-1){background-color:var(--color-table-stripe, #f2f2f2)}.fieldlist_formfields .fieldlist-row.error-row{background-color:var(--color-error-input, #ffdddd)}.fieldlist_formfields .fieldlist-cell{flex:1;padding:.5em 1em;display:flex;flex-direction:column;justify-content:center}.fieldlist_formfields .fieldlist-cell input,.fieldlist_formfields .fieldlist-cell select{width:100%}.fieldlist_formfields .fieldlist-cell.fieldlist-actions{flex:0 0 120px;display:flex;flex-direction:row;align-items:center;gap:4px}.fieldlist_formfields ul.errors{margin-top:.5em;margin-bottom:0;padding:.5em;background-color:var(--color-error-background-snapshot-age, #ffdddd);border-radius:4px;list-style-position:inside}@media only screen and (max-width: 760px){.fieldlist_formfields .fieldlist-header,.fieldlist_formfields .fieldlist-row{flex-direction:column}.fieldlist_formfields .fieldlist-header-cell{display:none}.fieldlist_formfields .fieldlist-row{padding:.5em 0;border-bottom:2px solid var(--color-border-table-cell, #cbcbcb)}.fieldlist_formfields .fieldlist-cell{padding:.25em .5em}.fieldlist_formfields .fieldlist-cell.fieldlist-actions{flex:1;justify-content:flex-start;padding-top:.5em}.fieldlist_formfields .fieldlist-cell:not(:last-child){margin-bottom:.5em}.fieldlist_formfields .fieldlist-cell::before{content:attr(data-label);font-weight:bold;margin-bottom:.25em}}.fieldlist_formfields .addRuleRow,.fieldlist_formfields .removeRuleRow,.fieldlist_formfields .verifyRuleRow{cursor:pointer;border:none;padding:4px 8px;border-radius:3px;font-weight:bold;background-color:#aaa;color:var(--color-foreground-text, #fff)}.fieldlist_formfields .addRuleRow:hover,.fieldlist_formfields .removeRuleRow:hover,.fieldlist_formfields .verifyRuleRow:hover{background-color:#999}body.checking-now #checking-now-fixed-tab{display:block !important}#checking-now-fixed-tab{background:#ccc;border-radius:5px;bottom:0;color:var(--color-text);display:none;font-size:.8rem;left:0;padding:5px;position:fixed}#selector-wrapper{height:100%;text-align:center;max-height:70vh;overflow-y:scroll;position:relative}#selector-wrapper>img{position:absolute;z-index:4;max-width:100%}#selector-wrapper>canvas{position:relative;z-index:5;max-width:100%}#selector-wrapper>canvas:hover{cursor:pointer}#selector-current-xpath{font-size:80%}@media(min-width: 901px){body.blueprint-add_watch_ui #add-watch-ui{width:80%}}#add-watch-ui{padding:0}#add-watch-ui #add-watch-url-row{display:flex;gap:.5rem;align-items:stretch;margin-bottom:1rem}#add-watch-ui #add-watch-url-row>span{flex:1 1 auto;min-width:0}#add-watch-ui #add-watch-url-row>span input{width:100%}#add-watch-ui #add-watch-url-row #add-watch-go{flex:0 0 auto;white-space:nowrap}#add-watch-ui #add-watch-panes{display:flex;gap:.55rem;align-items:stretch}@media(max-width: 900px){#add-watch-ui #add-watch-panes{flex-direction:column}}#add-watch-ui #add-watch-selector-pane{flex:1 1 62%;min-width:0;min-height:380px;position:relative;display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--color-background-tab);border-radius:6px;background:rgba(0,0,0,.15);padding:.75rem}#add-watch-ui #add-watch-selector-pane #add-watch-empty-state,#add-watch-ui #add-watch-selector-pane #add-watch-spinner,#add-watch-ui #add-watch-selector-pane #add-watch-error{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.6rem;text-align:center;padding:2rem 1rem}#add-watch-ui #add-watch-selector-pane #add-watch-empty-state{opacity:.8}#add-watch-ui #add-watch-selector-pane #add-watch-empty-state svg{opacity:.55}#add-watch-ui #add-watch-selector-pane #add-watch-empty-state strong{font-size:1.05rem}#add-watch-ui #add-watch-selector-pane #add-watch-empty-state span{font-size:.85rem;opacity:.8;max-width:32ch}#add-watch-ui #add-watch-selector-pane #add-watch-spinner{gap:1.2rem}#add-watch-ui #add-watch-selector-pane #add-watch-spinner .spinner{font-size:5px}#add-watch-ui #add-watch-selector-pane #add-watch-spinner .fetching-update-notice{font-size:.85rem;opacity:.85}#add-watch-ui #add-watch-selector-pane #add-watch-error{color:#ffb4b4;font-size:.9rem;word-break:break-word}#add-watch-ui #add-watch-selector-pane #selector-wrapper{position:relative;display:block;width:100%;flex:1 1 auto;min-height:0;max-height:none;overflow-y:auto;overflow-x:hidden;text-align:left}#add-watch-ui #add-watch-selector-pane #selector-wrapper>img{position:relative;display:block;max-width:100%;height:auto;z-index:4}#add-watch-ui #add-watch-selector-pane #selector-wrapper>canvas{position:absolute;top:0;left:0;max-width:none;z-index:5}#add-watch-ui #add-watch-options-pane{flex:0 0 34%;min-width:0;display:flex;flex-direction:column;gap:1.1rem}@media(max-width: 900px){#add-watch-ui #add-watch-options-pane{flex:1 1 auto}}#add-watch-ui #add-watch-options-pane .add-watch-option-group label{display:inline-block}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend ul{margin:.35rem 0 0 0;padding:0;list-style:none}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend li{display:flex;align-items:flex-start;gap:.5em;padding:.15rem 0}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend li input[type=radio]{flex:0 0 auto;margin-top:.2em}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend li label{display:block;min-width:0;overflow-wrap:anywhere;font-size:.85rem;line-height:1.35}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend li.unusable{opacity:.55;cursor:not-allowed}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend li.unusable label{cursor:not-allowed}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend .pure-form-message-inline{display:block;margin-top:.35rem;font-size:.8rem;opacity:.8}#add-watch-ui #add-watch-options-pane #by-element-toggle-group .pure-form-message-inline{display:block;margin-top:.25rem;font-size:.8rem;opacity:.8}#add-watch-ui #add-watch-options-pane #by-element-toggle-group #clear-selector{margin-top:.5rem}#add-watch-ui #add-watch-options-pane #quick-watch-llm-intent label{display:block;margin-bottom:.35rem}#add-watch-ui #add-watch-options-pane #add-watch-submit-row{display:flex;flex-wrap:wrap;gap:.5rem}body.blueprint-add_watch_ui #add-watch-ui{height:90vh;display:flex;flex-direction:column}body.blueprint-add_watch_ui #add-watch-fieldset{flex:1 1 auto;min-height:0;min-width:0;display:flex;flex-direction:column;border:0;margin:0;padding:0}body.blueprint-add_watch_ui #add-watch-legend{margin:0 0 .75rem;font-size:1.1rem;font-weight:600}body.blueprint-add_watch_ui #new-watch-form{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}@media(min-width: 901px){body.blueprint-add_watch_ui #add-watch-panes{flex:1 1 auto;min-height:0}body.blueprint-add_watch_ui #add-watch-selector-pane{min-height:0}}@media(max-width: 900px){body.blueprint-add_watch_ui #add-watch-ui{height:auto}body.blueprint-add_watch_ui #add-watch-panes{flex:0 0 auto}}.ternary-radio-group{display:flex;gap:0;border:1px solid var(--color-grey-750);border-radius:4px;overflow:hidden;width:fit-content;background:var(--color-background)}.ternary-radio-group .ternary-radio-option{position:relative;cursor:pointer;margin:0;display:flex;align-items:center}.ternary-radio-group .ternary-radio-option input[type=radio]{position:absolute;opacity:0;width:0;height:0}.ternary-radio-group .ternary-radio-option .ternary-radio-label{padding:8px 16px;background:var(--color-grey-900);border:none;border-right:1px solid var(--color-grey-750);font-size:13px;font-weight:500;color:var(--color-text);transition:all .2s ease;cursor:pointer;display:block;text-align:center}.ternary-radio-group .ternary-radio-option:last-child .ternary-radio-label{border-right:none}.ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label{background:var(--color-link);color:var(--color-text-button);font-weight:600}.ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label.ternary-default{background:var(--color-grey-600);color:var(--color-text-button)}.ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label:hover{background:#1a7bc4}.ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label:hover.ternary-default{background:var(--color-grey-500)}.ternary-radio-group .ternary-radio-option:hover .ternary-radio-label{background:var(--color-grey-800)}@media(max-width: 480px){.ternary-radio-group{width:100%}.ternary-radio-group .ternary-radio-label{flex:1;min-width:auto}}input[type=radio].pure-radio:checked+label,input[type=radio].pure-radio:checked{background:var(--color-link);color:var(--color-text-button)}html[data-darkmode=true] .ternary-radio-group .ternary-radio-option .ternary-radio-label{background:var(--color-grey-350)}html[data-darkmode=true] .ternary-radio-group .ternary-radio-option:hover .ternary-radio-label{background:var(--color-grey-400)}html[data-darkmode=true] .ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label{background:var(--color-link);color:var(--color-text-button)}html[data-darkmode=true] .ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label.ternary-default{background:var(--color-grey-600)}html[data-darkmode=true] .ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label:hover{background:#1a7bc4}html[data-darkmode=true] .ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label:hover.ternary-default{background:var(--color-grey-500)}body.processor-image_ssim_diff #edit-text-filter .text-filtering{display:none}body.processor-image_ssim_diff #conditions-tab{display:none}.modal-dialog{border:none;border-radius:10px;padding:0;background:var(--color-background);color:var(--color-text);box-shadow:0 5px 20px rgba(0,0,0,.3);max-width:500px;width:90%}.modal-dialog::backdrop{background:rgba(0,0,0,.6);backdrop-filter:blur(3px);animation:fadeIn .2s ease-out}.modal-dialog[open]{animation:slideIn .25s ease-out}.modal-dialog .modal-header{padding:1.5rem;border-bottom:1px solid var(--color-border-table-cell);display:flex;align-items:center;gap:1rem}.modal-dialog .modal-header .modal-icon{font-size:2rem;line-height:1;flex-shrink:0}.modal-dialog .modal-header .modal-icon.warning{color:var(--color-warning)}.modal-dialog .modal-header .modal-icon.danger{color:var(--color-background-button-error)}.modal-dialog .modal-header .modal-icon.info{color:var(--color-background-button-primary)}.modal-dialog .modal-header .modal-title{font-size:1.3rem;font-weight:bold;margin:0;color:var(--color-text)}.modal-dialog .modal-body{padding:1.5rem;line-height:1.6}.modal-dialog .modal-body p{margin:0 0 1rem 0}.modal-dialog .modal-body p:last-child{margin-bottom:0}.modal-dialog .modal-body strong{color:var(--color-text);font-weight:600}.modal-dialog .modal-footer{padding:1rem 1.5rem;border-top:1px solid var(--color-border-table-cell);display:flex;gap:.75rem;justify-content:flex-end;background:var(--color-grey-900)}.modal-dialog .modal-footer button{padding:.6rem 1.5rem;border:none;border-radius:4px;cursor:pointer;font-weight:500;transition:all .2s ease;font-size:.95rem}.modal-dialog .modal-footer button:hover{transform:translateY(-1px);box-shadow:0 2px 8px rgba(0,0,0,.15)}.modal-dialog .modal-footer button:active{transform:translateY(0)}.modal-dialog .modal-footer button.modal-btn-cancel{background:var(--color-background-button-cancel);color:var(--color-grey-200)}.modal-dialog .modal-footer button.modal-btn-cancel:hover{background:var(--color-grey-700)}.modal-dialog .modal-footer button.modal-btn-confirm{background:var(--color-background-button-primary);color:var(--color-white)}.modal-dialog .modal-footer button.modal-btn-confirm:hover{opacity:.9}.modal-dialog .modal-footer button.modal-btn-danger{background:var(--color-background-button-error);color:var(--color-white)}.modal-dialog .modal-footer button.modal-btn-danger:hover{background:var(--color-dark-red)}.modal-dialog .modal-footer button.modal-btn-warning{background:var(--color-background-button-warning);color:var(--color-white)}.modal-dialog .modal-footer button.modal-btn-warning:hover{opacity:.9}html[data-darkmode=true] .modal-dialog{box-shadow:0 5px 30px rgba(0,0,0,.7)}html[data-darkmode=true] .modal-dialog .modal-footer{background:var(--color-grey-200)}@keyframes fadeIn{from{opacity:0}to{opacity:1}}@keyframes slideIn{from{opacity:0;transform:translateY(-20px) scale(0.95)}to{opacity:1;transform:translateY(0) scale(1)}}@media only screen and (max-width: 760px){.modal-dialog{width:95%;max-width:none}.modal-dialog .modal-header{padding:1rem}.modal-dialog .modal-header .modal-title{font-size:1.1rem}.modal-dialog .modal-body{padding:1rem;font-size:.95rem}.modal-dialog .modal-footer{padding:.75rem 1rem;flex-wrap:wrap}.modal-dialog .modal-footer button{flex:1;min-width:120px}}.bulk-choice-list{display:flex;flex-direction:column;gap:2px;max-height:50vh;overflow-y:auto;text-align:left}.bulk-choice-list .bulk-choice-row{display:block;padding:6px 8px;cursor:pointer;border-radius:4px}.bulk-choice-list .bulk-choice-row input[type=radio]{margin-right:8px}.bulk-choice-list .bulk-choice-row:hover{background:rgba(127,127,127,.15)}.bulk-choice-list .bulk-choice-row em{opacity:.7;font-size:.9em}#language-selector-flag{display:inline-block;width:1.2em;height:1.2em;vertical-align:middle;border-radius:50%;overflow:hidden;opacity:.6}#language-selector-flag:hover{opacity:1}.language-list{display:flex;flex-direction:column;gap:.5rem;padding:.5rem 0}.language-option{display:flex;align-items:center;gap:1rem;padding:.25rem;border-radius:4px;transition:background-color .2s ease;text-decoration:none;color:var(--color-text);border:1px solid rgba(0,0,0,0)}.language-option:hover{background-color:var(--color-background-menu-link-hover);border-color:var(--color-border-table-cell)}.language-option.active{background-color:var(--color-link);color:var(--color-text-button);font-weight:600}.language-option .flag{font-size:1.5rem;flex-shrink:0}.language-option .language-name{flex-grow:1;font-size:1rem}#language-modal .language-list .lang-option{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;margin-right:.5em;border-radius:50%;overflow:hidden}.action-sidebar{display:flex;flex-direction:column;align-items:center;align-self:flex-start;position:sticky;top:0;height:100vh;background:hsla(0,0%,100%,.05);z-index:60;pointer-events:none}@media only screen and (max-width: 980px){.action-sidebar{display:none}}.action-sidebar-inner{pointer-events:auto;width:64px;overflow:hidden;flex:1 1 auto;display:flex;flex-direction:column;transition:width .08s ease-out;padding-left:.55rem;padding-right:.55rem}body.actionsidebar-minimal .action-sidebar-inner:hover,body.actionsidebar-minimal .action-sidebar-inner:focus-within{width:200px;transition:width .22s cubic-bezier(0.2, 0.7, 0.2, 1)}body.actionside-bar-on .action-sidebar-inner{width:200px;transition:none}ul.action-sidebar-list{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:0}.action-sidebar-list.action-sidebar-list--bottom{margin-top:auto}.action-sidebar-li{list-style:none;margin:0;position:relative;color:var(--color-white)}.action-sidebar-li:nth-child(2){padding-top:2rem}.action-sidebar-li button{padding:0;margin:0}.action-sidebar-li a{color:var(--color-white)}.action-sidebar-li--spark .queue-spark{display:block;width:100%;height:15px;box-sizing:border-box;padding:0;margin:0;border-radius:3px;background:hsla(0,0%,100%,.05);box-shadow:inset 0 0 0 1px hsla(0,0%,100%,.06)}.action-sidebar-divider{height:1px;background:hsla(0,0%,100%,.18);margin:6px 16px;list-style:none}.action-sidebar .action-sidebar-item{position:relative;display:flex;align-items:center;padding:.55rem;font-size:var(--body-main-text-size);border-radius:var(--common-round-border);color:var(--color-white);text-decoration:none;white-space:nowrap;background:rgba(0,0,0,0);border:0;text-align:left;cursor:pointer;transition:background-color .12s ease,color .12s ease}.action-sidebar .action-sidebar-item:hover{background-color:var(--color-sidebar-item-hover-bg)}.action-sidebar .action-sidebar-item:hover .action-icon{stroke-width:2.3}.action-sidebar .action-sidebar-item:focus-visible{outline:2px solid var(--color-link);outline-offset:2px}.action-sidebar .action-sidebar-item.active .action-icon{stroke-width:2.4;filter:drop-shadow(0 0 0.4px currentColor)}.action-sidebar .action-sidebar-item.active .action-label{font-weight:700}.action-sidebar .action-sidebar-item.is-disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.action-sidebar .action-sidebar-item.action-sidebar-item--accent .action-icon{stroke:#fff;stroke-width:2.6}.action-sidebar .action-sidebar-item .action-label{flex:0 0 auto;margin-left:14px;font-family:inherit;font-weight:400;letter-spacing:0;text-transform:none;color:inherit;opacity:0;transform:translateX(-4px);transition:opacity .05s ease-out,transform .05s ease-out}.action-sidebar .action-sidebar-item .action-badge{margin-left:10px;font-size:.7rem;line-height:1;padding:3px 7px;border-radius:999px;background:hsla(0,0%,100%,.14);color:hsla(0,0%,100%,.85);text-transform:uppercase;letter-spacing:.06em;font-weight:700;pointer-events:none;opacity:0;transition:opacity .05s ease-out}.action-sidebar .action-sidebar-item .action-badge.action-badge--count{margin-left:auto;text-transform:none;letter-spacing:0;background:#3e95bb;color:var(--color-white);font-variant-numeric:tabular-nums}body.actionsidebar-minimal .action-sidebar-inner:hover .action-sidebar-item .action-label,body.actionsidebar-minimal .action-sidebar-inner:focus-within .action-sidebar-item .action-label{opacity:1;transform:translateX(0);transition:opacity .18s ease .05s,transform .18s cubic-bezier(0.2, 0.7, 0.2, 1) .05s}body.actionsidebar-minimal .action-sidebar-inner:hover .action-sidebar-item .action-badge,body.actionsidebar-minimal .action-sidebar-inner:focus-within .action-sidebar-item .action-badge{opacity:1;transition:opacity .18s ease .05s}body.actionside-bar-on .action-sidebar .action-sidebar-item .action-label{opacity:1;transform:translateX(0);transition:none}body.actionside-bar-on .action-sidebar .action-sidebar-item .action-badge{opacity:1;transition:none}.action-icon{flex:0 0 auto;width:24px;height:24px;stroke:currentColor;stroke-width:1.9;fill:none;stroke-linecap:round;stroke-linejoin:round}.action-badge{flex:0 0 auto;font-size:.62rem;text-transform:uppercase;letter-spacing:.08em;padding:2px 6px;border-radius:999px;background:hsla(0,0%,100%,.15);color:hsla(0,0%,100%,.85);font-weight:700}.mobile-menu-section{padding:.5rem .75rem;border-bottom:1px solid var(--color-border-table-cell)}.mobile-menu-section ul.action-sidebar-list{gap:1px}.mobile-menu-section ul.action-sidebar-list .action-sidebar-li a,.mobile-menu-section ul.action-sidebar-list .action-sidebar-li button{padding:.55rem}.mobile-menu-section .action-sidebar-item{position:relative;display:flex;align-items:center;justify-content:flex-start;gap:.55rem;padding:.55rem .55rem;border-radius:var(--common-round-border);color:var(--color-text)}.mobile-menu-section .action-sidebar-item:hover{background-color:var(--color-background-menu-link-hover);color:var(--color-text)}.mobile-menu-section .action-sidebar-item.active{background-color:var(--color-background-menu-link-hover);color:var(--color-text)}.mobile-menu-section .action-sidebar-item .action-label{position:static;transform:none;background:rgba(0,0,0,0);box-shadow:none;color:inherit;opacity:1;pointer-events:auto;padding:0;font-weight:500}.mobile-menu-section .action-sidebar-item .action-label::before{display:none}.mobile-menu-section .action-sidebar-item .action-badge{position:static;margin-left:auto;font-size:.62rem;background:rgba(0,0,0,.08);color:var(--color-text)}.mobile-menu-section .action-sidebar-item.action-sidebar-item--accent{background:var(--color-background-menu-link-hover);box-shadow:inset 0 0 0 1px var(--color-border-table-cell);color:var(--color-text)}.mobile-menu-section .action-sidebar-item.action-sidebar-item--accent .action-label{color:inherit}.mobile-menu-section .action-sidebar-item--button{background:rgba(0,0,0,0);border:none;cursor:pointer;text-align:left;font:inherit}#add-watch-live-info{width:100%;margin-top:1rem}#add-watch-live-info .add-watch-live-placeholder{border:1px dashed hsla(0,0%,100%,.25);background:hsla(0,0%,100%,.04);border-radius:10px;padding:1.25rem;color:var(--color-white)}#add-watch-live-info .add-watch-live-placeholder h3{margin:0 0 .4rem 0;font-size:1rem;letter-spacing:.02em}#add-watch-live-info .add-watch-live-placeholder .muted{opacity:.7;margin:0 0 .75rem 0;font-size:.85rem}#add-watch-live-info .add-watch-live-placeholder .add-watch-live-stream{font-size:.85rem;opacity:.6;padding:.6rem 0}.mobile-menu-drawer .action-sidebar-list{padding:0}.mobile-menu-drawer .mobile-menu-section .action-sidebar-item{padding-left:0}#action-sidebar-logo{padding-top:1.1rem;padding-left:.55rem}.actionsidebar-minimal #checking-now-stats-sidebar{display:none}.actionsidebar-minimal #cdio-logo #logo-expanded{display:none}.actionsidebar-minimal.action-side-bar-expanded #checking-now-stats-sidebar{display:block}.actionsidebar-minimal.action-side-bar-expanded #cdio-logo #logo-expanded{display:inline-block}.action-side-bar-expanded #cdio-logo #logo-short{display:none}#queue-page{width:100%;color:var(--color-white)}#queue-page h2,#queue-page h3{color:var(--color-white)}#queue-page .queue-panel{background:var(--color-background-new-watch-form);padding:1em;border-radius:10px;margin-bottom:1em;width:100%;box-sizing:border-box;color:var(--color-white)}#queue-page .queue-stats{display:grid;grid-template-columns:repeat(auto-fit, minmax(160px, 1fr));gap:.75rem}#queue-page .queue-stat .label{font-size:.7rem;text-transform:uppercase;letter-spacing:.06em;opacity:.7}#queue-page .queue-stat .value{font-size:1.6rem;font-weight:700;color:var(--color-white)}#queue-page .queue-stat.queue-stat--action{display:flex;align-items:center;justify-content:flex-start}#queue-page .queue-stat.queue-stat--action .pure-button{white-space:nowrap}#queue-page table.pure-table{width:100%;background:rgba(0,0,0,0);color:var(--color-white);font-size:80%}#queue-page table.pure-table thead th{background:rgba(0,0,0,0);color:var(--color-white);border-bottom:1px solid hsla(0,0%,100%,.18);font-weight:700;white-space:nowrap}#queue-page table.pure-table td{color:var(--color-white);border-color:hsla(0,0%,100%,.08);white-space:nowrap}#queue-page table.pure-table td.title-col,#queue-page table.pure-table td.watch-cell{white-space:normal;word-break:break-all}#queue-page table.pure-table td.time-cell{font-variant-numeric:tabular-nums;color:hsla(0,0%,100%,.75);font-size:.95em}#queue-page table.pure-table code,#queue-page table.pure-table small,#queue-page table.pure-table em,#queue-page table.pure-table strong{color:var(--color-white)}#queue-page table.pure-table code{background:rgba(0,0,0,.18)}#queue-page table.pure-table small{opacity:.7}#queue-page table.pure-table-striped tr:nth-child(2n-1) td{background:hsla(0,0%,100%,.04)}#queue-page tr.is-completed td{opacity:.45;transition:opacity .4s ease}#queue-page tbody[data-section=workers]{border-bottom:1px solid hsla(0,0%,100%,.18)}#queue-page tr.worker-slot td{border-color:hsla(0,0%,100%,.05)}#queue-page tr.worker-idle td{background:hsla(0,0%,100%,.02)}#queue-page .inline-tag,#queue-page .processor-badge,#queue-page .watch-tag-list,#queue-page .tracking-ldjson-price-data,#queue-page .restock-label{background:hsla(0,0%,100%,.14);color:var(--color-white)}#queue-page .inline-tag--running{background:rgba(28,184,65,.45)}#queue-page .inline-tag--idle{background:hsla(0,0%,100%,.08);color:hsla(0,0%,100%,.6)}#queue-page .inline-tag--done{background:hsla(0,0%,100%,.1);color:hsla(0,0%,100%,.7)}#queue-page a.queue-cancel{display:inline-block;margin-left:8px;font-size:.75rem;color:hsla(0,0%,100%,.65);text-decoration:underline;text-decoration-style:dotted;text-underline-offset:2px}#queue-page a.queue-cancel:hover{color:var(--color-white);text-decoration-style:solid}#queue-page a.queue-cancel.is-busy{pointer-events:none;opacity:.5}#queue-page tr.is-new td{animation:queue-row-in .45s ease}@keyframes queue-row-in{from{background-color:rgba(28,184,65,.18)}to{background-color:rgba(0,0,0,0)}}#queue-page .queue-waiting{display:none;align-items:center;gap:.5rem;margin-top:1rem;padding:.5rem 0;color:hsla(0,0%,100%,.7);font-size:.85rem}#queue-page .queue-waiting[data-show=true]{display:flex}#queue-page .queue-waiting .spinner{margin:0;flex:0 0 auto;border-top-color:hsla(0,0%,100%,.18);border-right-color:hsla(0,0%,100%,.18);border-bottom-color:hsla(0,0%,100%,.18);border-left-color:var(--color-white)}.hamburger-menu{display:none;background:rgba(0,0,0,0);border:none;cursor:pointer;padding:.55rem;z-index:10001;position:relative}@media only screen and (max-width: 980px){.hamburger-menu{display:flex;flex-direction:column;justify-content:center;align-items:center}}.hamburger-icon{width:24px;height:20px;position:relative;display:flex;flex-direction:column;justify-content:space-between}.hamburger-icon span{display:block;height:3px;width:100%;background:var(--color-white);border-radius:2px;transition:all .3s cubic-bezier(0.68, -0.55, 0.265, 1.55);transform-origin:center}.hamburger-menu.active .hamburger-icon span{background-color:var(--color-text)}.hamburger-menu.active .hamburger-icon span:nth-child(1){transform:translateY(8.5px) rotate(45deg)}.hamburger-menu.active .hamburger-icon span:nth-child(2){opacity:0;transform:translateX(-10px)}.hamburger-menu.active .hamburger-icon span:nth-child(3){transform:translateY(-8.5px) rotate(-45deg)}.mobile-menu-overlay{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.5);z-index:9999;opacity:0;transition:opacity .3s ease}.mobile-menu-overlay.active{display:block;opacity:1}.mobile-menu-drawer{position:fixed;top:0;right:-280px;width:280px;height:100%;background:var(--color-background);opacity:1;box-shadow:-2px 0 8px rgba(0,0,0,.15);z-index:10000;transition:right .3s cubic-bezier(0.68, -0.55, 0.265, 1.55);overflow-y:auto;padding-top:60px}.mobile-menu-drawer #cdio-logo{color:var(--color-text)}.mobile-menu-drawer #cdio-logo #logo-short{display:none}.mobile-menu-drawer #cdio-logo #logo-expanded{display:inline-block}.mobile-menu-drawer .action-icon{stroke:var(--color-text)}.mobile-menu-drawer.active{right:0}.mobile-menu-drawer .mobile-menu-items{list-style:none;padding:1rem 0;margin:0}.mobile-menu-drawer .mobile-menu-items li{border-bottom:1px solid var(--color-border-table-cell)}.mobile-menu-drawer .mobile-menu-items li>*{display:block;padding:1rem 1.5rem;color:var(--color-text);text-decoration:none;font-weight:500;transition:background .2s ease}.mobile-menu-drawer .mobile-menu-items li>*:hover{background:var(--color-background-menu-link-hover)}.mobile-menu-drawer .mobile-menu-items li#menu-pause,.mobile-menu-drawer .mobile-menu-items li#menu-mute{display:none}.logo-cdio{font-weight:bold;font-size:1.1rem}.logo-cdio .logo-cd{color:var(--color-grey-500)}.logo-cdio .logo-io{color:var(--color-text)}.menu-always-visible{display:flex;align-items:center;gap:.5rem;margin-left:auto}@media only screen and (max-width: 980px){#top-right-menu .menu-collapsible{display:none !important}.pure-menu-horizontal{overflow-x:visible !important}#nav-menu{overflow-x:visible !important}}@media only screen and (min-width: 1025px){.hamburger-menu,.mobile-menu-drawer,.mobile-menu-overlay{display:none !important}}html[data-darkmode=true] .mobile-menu-drawer{box-shadow:-2px 0 8px rgba(0,0,0,.4)}#search-modal .modal-body{padding:2rem 1.5rem}#search-modal .modal-body .pure-control-group{padding-bottom:0}#search-modal .modal-body .pure-control-group label{display:block;margin-bottom:.5rem;font-size:.9rem;font-weight:600;color:var(--color-text)}#search-modal .modal-body .pure-control-group #search-modal-input{width:100%;max-width:100%;box-sizing:border-box;padding:.6rem .8rem;font-size:1rem;border:1px solid var(--color-border-input);border-radius:4px;background-color:var(--color-background-input);color:var(--color-text-input);box-shadow:inset 0 1px 3px var(--color-shadow-input);transition:border-color .2s ease,box-shadow .2s ease}#search-modal .modal-body .pure-control-group #search-modal-input:focus{outline:none;border-color:var(--color-link);box-shadow:0 0 0 3px rgba(27,152,248,.1)}#search-modal .modal-body .pure-control-group #search-modal-input::placeholder{color:var(--color-text-input-placeholder);opacity:.7}html[data-darkmode=true] #search-modal #search-modal-input:focus{box-shadow:0 0 0 3px rgba(89,189,251,.15)}#llm-diff-summary-area{margin:.6rem 0 .4rem;padding:.65rem .9rem;background:linear-gradient(135deg, rgba(120, 80, 200, 0.18), rgba(80, 160, 220, 0.14));border-left:3px solid rgba(140,90,220,.8);border-radius:0 4px 4px 0;min-width:0;max-width:100%;box-sizing:border-box;overflow:hidden}#llm-diff-summary-area .llm-diff-summary-label{display:block;font-size:.7rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;opacity:.55;margin-bottom:.25rem}#llm-diff-summary-area .llm-diff-summary-text{margin:0;font-size:.9rem;line-height:1.5;white-space:pre-wrap;overflow-wrap:break-word;word-break:break-word}.llm-diff-summary-prompt{margin:.4em 0 0;font-size:.78rem;font-style:italic;overflow:hidden;max-height:3.8em;animation:llm-prompt-reveal .7s ease-out both}.llm-diff-summary-prompt .llm-diff-summary-prompt-text{display:block;opacity:.55;mask-image:linear-gradient(to bottom, rgba(0, 0, 0, 0.85) 30%, rgba(0, 0, 0, 0) 100%);-webkit-mask-image:linear-gradient(to bottom, rgba(0, 0, 0, 0.85) 30%, rgba(0, 0, 0, 0) 100%);white-space:pre-wrap;overflow-wrap:break-word;line-height:1.45}@keyframes llm-prompt-reveal{from{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:translateY(0)}}.llm-diff-summary-loading{opacity:.5;font-style:italic;animation:llm-pulse 1.4s ease-in-out infinite;font-weight:bold}@keyframes llm-pulse{0%,100%{opacity:.5}50%{opacity:.2}}.llm-budget-exceeded,.llm-error{color:#c0392b;font-weight:600;font-style:normal;opacity:1}.toggle-ai-mode{opacity:.4;transition:opacity .2s ease,filter .2s ease;display:inline-flex;align-items:center;color:var(--color-text-menu-link)}.toggle-ai-mode svg{height:1.2rem;width:1.2rem}.toggle-ai-mode .ai-mode-label{font-size:.75rem;font-weight:600;letter-spacing:.04em;line-height:1}html[data-ai-mode=true] .toggle-ai-mode{opacity:1;filter:drop-shadow(0 0 4px rgba(160, 100, 255, 0.7))}.btn-label-summary{display:none}html[data-ai-mode=true] body.llm-configured .btn-label-history{display:none}html[data-ai-mode=true] body.llm-configured .btn-label-summary{display:inline}.ai-inline-summary-row td{white-space:normal !important;word-break:break-word;padding:.5rem 1rem .6rem 1.4rem !important;background:linear-gradient(135deg, #f0ebff, #eaf0ff) !important;border-top:1px solid #c4b5fd !important;border-left:3px solid #8b5cf6 !important;color:#1a0640 !important;line-height:1.5}html[data-darkmode=true] .ai-inline-summary-row td{background:linear-gradient(135deg, #1c0d35, #0d1535) !important;border-top:1px solid #3b1f6e !important;border-left-color:#8b5cf6 !important;color:#e9d5ff !important}.ai-inline-summary-row .ai-inline-summary-content{display:flex;gap:.5rem;align-items:flex-start}.ai-inline-summary-row .ai-inline-summary-content .ai-inline-spinner{flex-shrink:0;animation:llm-pulse 1.4s ease-in-out infinite}.ai-inline-summary-row .ai-inline-summary-content .ai-inline-body{display:flex;flex-direction:column;min-width:0}.ai-inline-summary-row .ai-inline-summary-content .ai-inline-text{font-style:italic;opacity:.75;white-space:pre-wrap}.ai-inline-summary-row .ai-inline-summary-content.loaded .ai-inline-spinner{animation:none}.ai-inline-summary-row .ai-inline-summary-content.loaded .ai-inline-text{font-style:normal;opacity:1}.ai-inline-summary-row .ai-inline-history-link{display:inline-block;margin-top:.4rem;font-size:.78rem;font-weight:700;opacity:.7;white-space:nowrap}.ai-inline-summary-row .ai-inline-history-link:hover{opacity:1}.ai-inline-summary-row .ai-inline-error{color:#c0392b}.ai-inline-summary-row .ai-inline-prompt{display:block;margin-top:.3em;font-size:.75rem;font-style:italic;overflow:hidden;max-height:3.6em;animation:llm-prompt-reveal .6s ease-out both;mask-image:linear-gradient(to bottom, rgba(0, 0, 0, 0.7) 30%, rgba(0, 0, 0, 0) 100%);-webkit-mask-image:linear-gradient(to bottom, rgba(0, 0, 0, 0.7) 30%, rgba(0, 0, 0, 0) 100%);opacity:.55;line-height:1.4;white-space:pre-wrap;overflow-wrap:break-word}.action-sidebar-item{position:relative}.action-sidebar-item .notification-bubble{position:absolute;top:8px;left:8px;min-width:18px;height:18px;background:#f44;color:#fff;font-size:10px;font-weight:700;line-height:18px;text-align:center;border-radius:9px;padding:0 2px;box-shadow:0 2px 4px rgba(0,0,0,.3);pointer-events:none;transition:all .2s ease;display:none}.action-sidebar-item .notification-bubble.red-bubble{background:#f44}.action-sidebar-item .notification-bubble.blue-bubble{background:#4a9eff;color:#fff}.action-sidebar-item .notification-bubble.visible{display:block}.action-sidebar-item .notification-bubble.pulse{animation:bubblePulse .4s ease-out}.action-sidebar-item .notification-bubble.large-number{font-size:8px;min-width:20px;height:20px;line-height:20px;border-radius:10px}@keyframes bubblePulse{0%{transform:scale(1)}50%{transform:scale(1.3)}100%{transform:scale(1)}}html[data-darkmode=true] .notification-bubble{box-shadow:0 2px 6px rgba(0,0,0,.6)}.notification-add-buttons{margin-bottom:.5rem;display:flex;align-items:center;flex-wrap:wrap;gap:.4rem}.notification-add-buttons .add-destination-inline{display:inline-flex;align-items:center;gap:.3rem}.notification-add-buttons .add-destination-inline input[type=email]{margin:0;min-width:16rem}#notification-add-email-preset{padding-top:.55rem;padding-bottom:.55rem}#notification-recipients{padding-bottom:.55rem}.notification-recipients{display:flex;flex-wrap:wrap;gap:.4rem;margin-bottom:.5rem}.notification-recipients .notification-chip{display:inline-flex;align-items:center;gap:.35rem;padding:.2rem .5rem;line-height:1.4;border:1px solid var(--color-border-notification);border-radius:var(--common-round-border);max-width:100%}.notification-recipients .notification-chip .notification-chip-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:22rem}.notification-recipients .notification-chip .notification-chip-remove{cursor:pointer;font-weight:bold;opacity:.55;text-decoration:none}.notification-recipients .notification-chip .notification-chip-remove:hover{opacity:1}.notifications-wrapper{padding-top:.5rem}.notifications-wrapper #notification-test-log{margin-top:1rem;padding:1rem;white-space:pre-wrap;word-break:break-word;overflow-wrap:break-word;max-width:100%;box-sizing:border-box;max-height:12rem;overflow-y:scroll;border:1px solid var(--color-border-notification);border-radius:5px}#notification-error-log{border:1px solid var(--color-border-notification);padding:1rem;border-radius:5px;overflow-wrap:break-word}#restock-diff{width:100%;box-sizing:border-box}#restock-diff-summary{margin-top:.6rem;display:flex;align-items:center;gap:.6rem;flex-wrap:wrap}.restock-latest-price{font-size:1.4rem;font-weight:700}.restock-badge{display:inline-block;padding:.15rem .55rem;border-radius:1rem;font-size:.8rem;font-weight:600;white-space:nowrap}.restock-badge.in-stock{background:rgba(31,164,99,.15);color:#1fa463}.restock-badge.out-of-stock{background:rgba(231,76,60,.15);color:#e74c3c}#restock-tabs{background:var(--color-background);color:var(--color-text);padding:1rem;border-radius:5px}#restock-tabs .tab-pane-inner#screenshot{text-align:center}#restock-tabs .tab-pane-inner#screenshot img{max-width:99%}#restock-graph{margin-top:1rem;box-sizing:border-box}@media(min-width: 1200px){#restock-graph{max-width:80%;margin-left:auto;margin-right:auto}}.js-restock-graph{position:relative;width:100%}.js-restock-graph svg{display:block;max-width:100%}.js-restock-graph .rg-axis{stroke:var(--color-border-notification, rgba(127, 127, 127, 0.4));stroke-width:1}.js-restock-graph .rg-label{fill:currentColor;opacity:.7;font-size:12px}.js-restock-graph .rg-line{stroke:currentColor;opacity:.55}.js-restock-graph .rg-dot{stroke:var(--color-background, #fff);stroke-width:1.5}.js-restock-graph .rg-legend{display:flex;justify-content:center;gap:1.1rem;margin-top:.4rem;font-size:.78rem;opacity:.85}.js-restock-graph .rg-legend .rg-legend-item{display:inline-flex;align-items:center;gap:.35rem}.js-restock-graph .rg-legend .rg-legend-dot{width:9px;height:9px;border-radius:50%;display:inline-block}.js-restock-graph .rg-legend .rg-legend-dot.in{background:#1fa463}.js-restock-graph .rg-legend .rg-legend-dot.out{background:#e74c3c}.js-restock-graph .rg-header{display:flex;align-items:center;justify-content:space-between;gap:.5rem;flex-wrap:wrap;margin-bottom:.5rem}.js-restock-graph .rg-stats{font-size:.8rem;opacity:.7;text-align:right;margin-left:auto}.js-restock-graph .rg-band{fill:rgba(120,130,150,.14)}.js-restock-graph .rg-avg-line{stroke:var(--color-text, #555);opacity:.45;stroke-width:1}.js-restock-graph .rg-avg-text{opacity:.5}.rg-status{display:inline-flex;align-items:baseline;gap:.4rem;padding:.2rem .6rem;border-radius:1rem;font-size:.85rem}.rg-status .rg-status-label{font-weight:700}.rg-status .rg-status-sub{font-size:.78rem;opacity:.8}.rg-status.rg-status-low{background:rgba(31,164,99,.16);color:#1fa463}.rg-status.rg-status-typical{background:rgba(120,130,150,.16);color:var(--color-text, #555)}.rg-status.rg-status-high{background:rgba(231,76,60,.16);color:#e74c3c}.rg-tooltip{position:absolute;display:none;pointer-events:none;z-index:5;transform:translateY(-50%);white-space:nowrap;background:var(--color-background, #fff);color:var(--color-text, #222);border:1px solid var(--color-border-notification, rgba(127, 127, 127, 0.4));border-radius:5px;padding:4px 8px;font-size:12px;line-height:1.4;box-shadow:0 2px 6px rgba(0,0,0,.15)}#restock-history-table{margin-left:auto;margin-right:auto}#restock-history-table td,#restock-history-table th{text-align:left}.restock-table-toolbar{display:flex;align-items:center;justify-content:center;gap:.75rem;margin-bottom:.5rem}html[data-ai-mode=true] body.llm-configured tr.processor-restock_diff .btn-label-history{display:inline}html[data-ai-mode=true] body.llm-configured tr.processor-restock_diff .btn-label-summary{display:none}.restock-inline-row td{white-space:normal !important;word-break:break-word;padding:.6rem 1rem .8rem 1.4rem !important;background:linear-gradient(135deg, #e6fbf0, #e8f6ff) !important;border-top:1px solid #9fe3c2 !important;border-left:3px solid #1fa463 !important;color:#06281a !important;line-height:1.5}html[data-darkmode=true] .restock-inline-row td{background:linear-gradient(135deg, #0c2a1c, #0d2230) !important;border-top:1px solid #1f5e40 !important;border-left-color:#1fa463 !important;color:#d6ffe9 !important}.restock-inline-row .restock-inline-graph{width:100%;min-height:40px}.restock-inline-row .restock-inline-history-link{display:inline-block;margin-top:.5rem;font-size:.78rem;font-weight:700;opacity:.75;white-space:nowrap}.restock-inline-row .restock-inline-history-link:hover{opacity:1}.restock-inline-row .restock-inline-error{color:#c0392b}.toast-container{position:fixed;display:flex;flex-direction:column;gap:.75rem;pointer-events:none;z-index:10000}.toast-container.toast-top-right{top:20px;right:20px}.toast-container.toast-top-center{top:100px;left:50%;transform:translateX(-50%)}.toast-container.toast-top-left{top:20px;left:20px}.toast-container.toast-bottom-right{bottom:20px;right:20px}.toast-container.toast-bottom-center{bottom:20px;left:50%;transform:translateX(-50%)}.toast-container.toast-bottom-left{bottom:20px;left:20px}.toast{position:relative;display:flex;align-items:center;gap:.75rem;min-width:300px;max-width:500px;padding:1rem 1.25rem;background:var(--color-background);border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.15),0 0 0 1px rgba(0,0,0,.05);pointer-events:auto;overflow:hidden;opacity:0;transform:translateY(-50px);transition:all .3s cubic-bezier(0.68, -0.55, 0.265, 1.55);font-family:inherit}.toast.toast-show{opacity:1;transform:translateY(0)}.toast.toast-hide{opacity:0;transform:translateY(-50px) scale(0.95)}.toast.toast-success{border-left:4px solid #10b981}.toast.toast-success .toast-icon{color:#10b981}.toast.toast-error{border-left:4px solid #ef4444}.toast.toast-error .toast-icon{color:#ef4444}.toast.toast-warning{border-left:4px solid #f59e0b}.toast.toast-warning .toast-icon{color:#f59e0b}.toast.toast-info{border-left:4px solid #3b82f6}.toast.toast-info .toast-icon{color:#3b82f6}.toast.toast-default{border-left:4px solid var(--color-grey-500)}.toast-icon{flex-shrink:0;width:24px;height:24px}.toast-icon svg{width:100%;height:100%}.toast-message{flex:1;font-size:.875rem;line-height:1.5;color:var(--color-text);word-break:break-word;font-family:inherit}.toast-close{flex-shrink:0;width:24px;height:24px;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0);border:none;border-radius:4px;color:var(--color-grey-500);font-size:1.5rem;line-height:1;cursor:pointer;transition:all .2s ease;padding:0;margin-left:.25rem}.toast-close:hover{background:var(--color-grey-800);color:var(--color-text)}.toast-close:active{transform:scale(0.95)}.toast-progress{position:absolute;bottom:0;left:0;right:0;height:3px;background:currentColor;opacity:.3;transform-origin:left;transition:transform linear}html[data-darkmode=true] .toast{background:var(--color-grey-300);box-shadow:0 4px 12px rgba(0,0,0,.4),0 0 0 1px hsla(0,0%,100%,.05)}html[data-darkmode=true] .toast-close:hover{background:var(--color-grey-400)}@media only screen and (max-width: 768px){.toast-container{left:50% !important;right:auto !important;top:80px !important;transform:translateX(-50%) !important;align-items:center}.toast-container.toast-bottom-right,.toast-container.toast-bottom-center,.toast-container.toast-bottom-left{top:auto !important;bottom:80px !important}.toast{min-width:auto;max-width:none;width:80vw;transform:translateY(-100px)}.toast.toast-show{transform:translateY(0)}.toast.toast-hide{transform:translateY(-100px) scale(0.95)}}@media(prefers-reduced-motion: reduce){.toast{transition:opacity .2s ease;transform:none !important}.toast.toast-show{opacity:1}.toast.toast-hide{opacity:0}}.login-form{min-height:52vh;display:flex;align-items:center;justify-content:center;padding:2rem 1rem}.login-form .inner{background:var(--color-background);border-radius:16px;box-shadow:0 10px 40px rgba(0,0,0,.08),0 2px 8px rgba(0,0,0,.04);padding:3rem 2.5rem;width:100%;max-width:420px;position:relative;overflow:hidden;transition:transform .3s ease,box-shadow .3s ease}.login-form .inner:hover{box-shadow:0 15px 50px rgba(0,0,0,.12),0 5px 15px rgba(0,0,0,.06)}.login-form form{margin:0}.login-form fieldset{border:none;padding:0;margin:0}.login-form .pure-control-group{margin-bottom:1.75rem}.login-form .pure-control-group:last-of-type{margin-bottom:0;margin-top:2rem}.login-form label{display:block;margin-bottom:.5rem;font-weight:600;font-size:.9rem;color:var(--color-text);letter-spacing:.01em}.login-form input[type=password]{width:100%;padding:.875rem 1rem;border:2px solid var(--color-grey-800);border-radius:8px;font-size:1rem;background:var(--color-background-input);color:var(--color-text-input);transition:all .2s ease;box-sizing:border-box}.login-form input[type=password]:focus{outline:none;border-color:var(--color-link);box-shadow:0 0 0 3px rgba(27,152,248,.1);transform:translateY(-1px)}.login-form input[type=password]::placeholder{color:var(--color-text-input-placeholder)}.login-form button[type=submit]{width:100%;padding:.875rem 1.5rem;font-size:1rem;font-weight:600;border-radius:8px;border:none;background:var(--color-background-button-primary);color:var(--color-text-button);cursor:pointer;transition:all .2s ease;box-shadow:0 2px 8px rgba(27,152,248,.2)}.login-form button[type=submit]:hover{box-shadow:0 4px 12px rgba(27,152,248,.3);background:#06c}.login-form button[type=submit]:active{transform:translateY(0);box-shadow:0 2px 4px rgba(27,152,248,.2)}.content-main>ul.messages{position:fixed;top:120px;left:50%;transform:translateX(-50%);list-style:none;padding:0;margin:0;z-index:1000;min-width:300px;max-width:500px}.content-main>ul.messages li{padding:1rem 1.25rem;border-radius:8px;font-size:.95rem;line-height:1.5;font-weight:500;box-shadow:0 4px 12px rgba(0,0,0,.15);animation:slideDown .3s ease-out;border:2px solid rgba(0,0,0,0)}.content-main>ul.messages li.error{background:#fee;border:2px solid #ef4444;color:#991b1b;font-weight:600}.content-main>ul.messages li.success{background:#f0fdf4;border:2px solid #10b981;color:#166534}.content-main>ul.messages li.info,.content-main>ul.messages li.message{background:#eff6ff;border:2px solid #3b82f6;color:#1e40af}@keyframes slideDown{from{opacity:0;transform:translateY(-20px)}to{opacity:1;transform:translateY(0)}}html[data-darkmode=true] .login-form .inner{box-shadow:0 10px 40px rgba(0,0,0,.4),0 2px 8px rgba(0,0,0,.2)}html[data-darkmode=true] .login-form .inner:hover{box-shadow:0 15px 50px rgba(0,0,0,.5),0 5px 15px rgba(0,0,0,.3)}html[data-darkmode=true] .login-form input[type=password]{border-color:var(--color-grey-400)}html[data-darkmode=true] .login-form input[type=password]:focus{border-color:var(--color-link)}html[data-darkmode=true] .content-main>ul.messages li{box-shadow:0 4px 12px rgba(0,0,0,.4)}html[data-darkmode=true] .content-main>ul.messages li.error{background:#4a1d1d;border-color:#ef4444;color:#fca5a5}html[data-darkmode=true] .content-main>ul.messages li.success{background:#1a3a2a;border-color:#10b981;color:#86efac}html[data-darkmode=true] .content-main>ul.messages li.info,html[data-darkmode=true] .content-main>ul.messages li.message{background:#1e3a5f;border-color:#3b82f6;color:#93c5fd}@media only screen and (max-width: 768px){.login-form{min-height:auto;padding:1rem .5rem;padding-top:5rem}.login-form .inner{padding:2rem 1.5rem;border-radius:12px}.content-main>ul.messages{top:70px;left:10px;right:10px;transform:none;min-width:auto}}body.wrapped-tabs .tabs ul{grid-template-columns:repeat(auto-fill, minmax(var(--tab-width, 180px), 1fr));grid-auto-flow:row;grid-auto-columns:unset;gap:0;column-gap:5px}body.wrapped-tabs .tabs ul li{border-radius:0}.tabs ul{margin:0px;padding:0px;display:grid;grid-auto-flow:column;grid-auto-columns:max-content;gap:5px;list-style:none}.tabs ul li{white-space:nowrap;color:var(--color-text-tab);border-top-left-radius:5px;border-top-right-radius:5px;background-color:var(--color-background-tab)}.tabs ul li:not(.active):hover{background-color:var(--color-background-tab-hover)}.tabs ul li.active,.tabs ul li :target{background-color:var(--color-background)}.tabs ul li.active a,.tabs ul li :target a{color:var(--color-text-tab-active);font-weight:bold}.tabs ul li a{display:block;padding:.7em;color:var(--color-text-tab)}.stab-shell{display:flex;align-items:stretch;background:var(--color-background);border:1px solid rgba(0,0,0,.08);border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,.05);margin-bottom:1.5rem}.stab-nav{display:flex;flex-direction:column;width:11rem;flex-shrink:0;padding:.75rem 0;gap:1px;background:linear-gradient(180deg, rgba(0, 0, 0, 0.03) 0%, rgba(0, 0, 0, 0.05) 100%);border-right:1px solid rgba(0,0,0,.07)}.stab-btn{position:relative;display:flex;align-items:center;gap:.5rem;padding:.65rem .9rem .65rem 1rem;width:100%;background:none;border:none;border-left:3px solid rgba(0,0,0,0);border-radius:0;cursor:pointer;font:inherit;color:var(--color-text);text-align:left;opacity:.65;transition:background .12s ease,opacity .12s ease,border-color .12s ease,color .12s ease}.stab-btn:hover{background:rgba(0,0,0,.04);opacity:.85}.stab-btn.active{border-left-color:var(--color-menu-accent);background:rgba(237,89,0,.07);color:var(--color-menu-accent);font-weight:700;opacity:1}.stab-btn .stab-icon{display:inline-flex;align-items:center;justify-content:center;width:1.1rem;flex-shrink:0;opacity:.8}.stab-btn .stab-icon svg{width:.95rem;height:.95rem;stroke:currentColor;fill:none}.stab-body{flex:1;min-width:0;padding:1.4rem 1.6rem;overflow-x:hidden}.stab-pane{visibility:hidden;height:0;overflow:hidden}.stab-pane.active{visibility:visible;height:auto;overflow:visible;animation:stab-enter .16s ease both}@keyframes stab-enter{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:translateY(0)}}.stab-overview-hero{margin-bottom:1.5rem}.stab-overview-hero h3{margin:0 0 .3rem;font-size:1.05rem}.stab-overview-hero .stab-overview-glyph{color:var(--color-menu-accent);margin-right:.2rem}.stab-overview-hero .stab-overview-glyph svg{width:1.1rem;height:1.1rem;stroke:currentColor;fill:none;vertical-align:-0.15em}.stab-overview-hero p{margin:0;font-size:.88rem;color:var(--color-text-input-description);max-width:44rem;line-height:1.55}.stab-overview-features{display:flex;flex-direction:column;gap:.7rem;margin-bottom:1.6rem}.stab-overview-feature{display:flex;gap:.85rem;align-items:flex-start;padding:.75rem 1rem;border-radius:6px;background:rgba(0,0,0,.022);border:1px solid rgba(0,0,0,.05);transition:background .12s ease}.stab-overview-feature:hover{background:rgba(0,0,0,.035)}.stab-overview-feature .stab-overview-icon{width:1.6rem;flex-shrink:0;padding-top:.05rem;opacity:.7;display:flex;justify-content:center}.stab-overview-feature .stab-overview-icon svg{width:1.3rem;height:1.3rem;stroke:currentColor;fill:none}.stab-overview-feature .stab-overview-text>strong{display:block;margin-bottom:.2rem}.stab-overview-feature .stab-overview-text p{color:var(--color-text-input-description)}.stab-overview-disclaimer{display:flex;gap:.75rem;align-items:flex-start;margin:0 0 1.1rem;padding:.85rem 1rem;border-radius:6px;border:1px solid rgba(211,136,0,.35);background:rgba(255,180,0,.07)}.stab-overview-disclaimer .stab-disclaimer-icon{flex-shrink:0;padding-top:.05rem;color:#c07800}.stab-overview-disclaimer .stab-disclaimer-icon svg{width:1.2rem;height:1.2rem;stroke:currentColor;fill:none}.stab-overview-disclaimer .stab-disclaimer-body{font-size:.85rem;line-height:1.55}.stab-overview-disclaimer .stab-disclaimer-body>strong{display:block;margin-bottom:.35rem;color:#8a5500;font-size:.87rem}.stab-overview-disclaimer .stab-disclaimer-body p{margin:0 0 .45rem;color:var(--color-text-input-description)}.stab-overview-disclaimer .stab-disclaimer-body ul{margin:0 0 .6rem;padding-left:1.25rem;color:var(--color-text-input-description)}.stab-overview-disclaimer .stab-disclaimer-body ul li{margin-bottom:.2rem}.stab-overview-disclaimer .stab-disclaimer-check{display:flex;gap:.5rem;align-items:flex-start;cursor:pointer;font-size:.82rem;color:var(--color-text-input-description);font-weight:600}.stab-overview-disclaimer .stab-disclaimer-check input[type=checkbox]{flex-shrink:0;margin-top:.18rem;cursor:pointer}.stab-overview-cta{margin-top:.4rem;display:flex;align-items:center;gap:.8rem;flex-wrap:wrap}.stab-configured-badge{display:inline-flex;align-items:center;gap:.4rem;padding:.35rem .75rem;background:rgba(39,174,96,.09);border:1px solid rgba(39,174,96,.28);border-radius:4px;color:#2a7a4e;font-size:.82rem;font-weight:600}.stab-section-title{font-size:.72rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;opacity:.45;margin:1.4rem 0 .6rem}.stab-section-title:first-child{margin-top:0}@media(max-width: 600px){.stab-shell{flex-direction:column;min-height:unset}.stab-nav{width:100%;border-right:none;border-bottom:1px solid rgba(0,0,0,.07);padding:.4rem 0}.stab-body{padding-left:1rem}}.llm-usage-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(12rem, 1fr));gap:.9rem;margin-bottom:1.4rem}.llm-stat-card{padding:1rem 1.1rem .85rem;border-radius:7px;background:rgba(0,0,0,.025);border:1px solid rgba(0,0,0,.07)}.llm-stat-card .llm-stat-label{font-size:.7rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;opacity:.4;margin-bottom:.4rem}.llm-stat-card .llm-stat-value{font-size:1.65rem;font-weight:700;letter-spacing:-0.02em;line-height:1;margin-bottom:.25rem}.llm-stat-card .llm-stat-sub{font-size:.79rem;opacity:.5}.llm-stat-card .llm-stat-budget-text{font-size:.77rem;opacity:.55;margin-top:.3rem}.llm-stat-bar-wrap{height:4px;border-radius:2px;background:rgba(0,0,0,.1);overflow:hidden;margin-top:.65rem}.llm-stat-bar-fill{height:100%;border-radius:2px;transition:width .5s ease}.llm-stat-bar-fill.bar-ok{background:#27ae60}.llm-stat-bar-fill.bar-warn{background:#e67e22}.llm-stat-bar-fill.bar-over{background:#c0392b}.llm-usage-settings{border-top:1px solid rgba(0,0,0,.07);padding-top:.9rem;display:flex;flex-direction:column;gap:.65rem}.llm-usage-row{display:flex;align-items:baseline;gap:.9rem;flex-wrap:wrap}.llm-usage-row .llm-usage-row-label{font-size:.82rem;font-weight:600;opacity:.6;min-width:12rem;flex-shrink:0}.llm-usage-row .llm-usage-row-value{display:flex;align-items:baseline;gap:.5rem;flex-wrap:wrap;font-size:.88rem}.llm-field-hint{font-size:.8rem;opacity:.55}.llm-env-badge{font-size:.79rem;opacity:.6}.llm-budget-alert{color:#c0392b;font-weight:600;font-size:.88rem;margin:0 0 1rem}.llm-no-usage{opacity:.5;font-style:italic;font-size:.88rem;margin-bottom:1rem}html[data-darkmode=true] .stab-shell{border-color:hsla(0,0%,100%,.07);box-shadow:0 2px 8px rgba(0,0,0,.25)}html[data-darkmode=true] .stab-nav{background:linear-gradient(180deg, rgba(255, 255, 255, 0.025) 0%, rgba(255, 255, 255, 0.04) 100%);border-right-color:hsla(0,0%,100%,.07)}html[data-darkmode=true] .stab-btn:hover{background:hsla(0,0%,100%,.05)}html[data-darkmode=true] .stab-btn.active{background:rgba(237,89,0,.12)}html[data-darkmode=true] .stab-overview-feature{background:hsla(0,0%,100%,.025);border-color:hsla(0,0%,100%,.05)}html[data-darkmode=true] .stab-overview-feature:hover{background:hsla(0,0%,100%,.04)}html[data-darkmode=true] .stab-configured-badge{background:rgba(39,174,96,.1);border-color:rgba(39,174,96,.22);color:#5db880}html[data-darkmode=true] .stab-overview-disclaimer{border-color:rgba(255,190,50,.22);background:rgba(255,180,0,.05)}html[data-darkmode=true] .stab-overview-disclaimer .stab-disclaimer-icon{color:#c9963a}html[data-darkmode=true] .stab-overview-disclaimer .stab-disclaimer-body>strong{color:#c9a050}html[data-darkmode=true] .llm-stat-card{background:hsla(0,0%,100%,.03);border-color:hsla(0,0%,100%,.07)}html[data-darkmode=true] .llm-stat-bar-wrap{background:hsla(0,0%,100%,.1)}html[data-darkmode=true] .llm-usage-settings{border-top-color:hsla(0,0%,100%,.07)}body,.pure-table,.pure-table thead,.pure-table td,.pure-table th,.pure-form input,.pure-form textarea,.pure-form select,.edit-form .inner,.pure-menu-horizontal,footer,.sticky-tab,#diff-jump,.button-tag,#new-watch-form,#new-watch-form input:not(.pure-button),code,.messages li,#checkbox-operations,.inline-warning,a,.watch-controls img{transition:color .4s ease,background-color .4s ease,background .4s ease,border-color .4s ease,box-shadow .4s ease}body{color:var(--color-text);background:var(--color-background-page);font-family:Helvetica Neue,Helvetica,Lucida Grande,Arial,Ubuntu,Cantarell,Fira Sans,sans-serif}.app{display:flex;align-items:stretch;min-height:100vh}.app-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:.55rem}.content-wrapper{display:flex;width:100%;max-width:100%;position:relative;align-items:flex-start}@media only screen and (max-width: 980px){.content-wrapper{flex-direction:column}}.content-main{flex:1 1 auto;width:100%;min-width:0;display:flex;flex-direction:column;align-items:center}@media only screen and (min-width: 980px){.content-main{flex-direction:column}}.visually-hidden{clip:rect(0 0 0 0);clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}.status-icon{display:inline-block;height:1rem;vertical-align:middle}a{text-decoration:none;color:var(--color-link)}#search-result-info{color:#fff}button.toggle-button{vertical-align:middle;background:rgba(0,0,0,0);border:none;cursor:pointer;color:var(--color-text-menu-heading)}button.toggle-button svg{fill:currentColor}button.toggle-button svg.feather{fill:none;stroke:currentColor}button.toggle-button .icon-light{display:block}body.spinner-active #pure-menu-horizontal-spinner{animation:gradient 1s ease infinite}@keyframes gradient{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}#cdio-logo{color:var(--color-white);text-transform:uppercase}.pure-menu-link{color:var(--color-text-menu-link)}.pure-menu-link:hover{background-color:var(--color-background-menu-link-hover);color:var(--color-text-menu-link-hover)}.tab-pane-inner{scroll-margin-top:200px}section.content{padding-bottom:1em;padding-left:.55rem;padding-right:.55rem;flex-direction:column;display:flex;align-items:center;justify-content:flex-start}details summary{cursor:pointer;font-weight:600;color:var(--color-link);width:fit-content}details summary:hover{text-decoration:underline}code{background:var(--color-background-code);color:var(--color-text)}.inline-tag,.restock-label,.tracking-ldjson-price-data,.watch-tag-list,.processor-badge{white-space:nowrap;border-radius:5px;padding:2px 5px;margin-right:4px}.processor-badge{font-weight:900;text-decoration:none}.processor-badge:hover{text-decoration:none;opacity:.8;cursor:pointer}.processor-badge.active{outline:2px solid var(--color-link);outline-offset:1px}.watch-tag-list{color:var(--color-white);background:var(--color-text-watch-tag-list);text-decoration:none}.watch-tag-list:hover{text-decoration:none;opacity:.8;cursor:pointer}.watch-tag-list:visited{color:var(--color-white)}body:after{content:"";background:linear-gradient(130deg, var(--color-background-gradient-first), var(--color-background-gradient-second) 41.07%, var(--color-background-gradient-third) 84.05%)}body:after,body:before{display:block;position:fixed;top:0;left:0;width:100%;height:100vh;z-index:-1}body::after{opacity:.91}body::before{content:""}.button-small{font-size:85%}.button-xsmall{font-size:70%}.fetch-error{padding-top:1em;font-size:80%;max-width:400px;display:block}.pure-button-primary,a.pure-button-primary,.pure-button-selected,a.pure-button-selected{background-color:var(--color-background-button-primary)}.button-secondary{color:var(--color-text-button);border-radius:4px;text-shadow:0 1px 1px rgba(0,0,0,.2)}.button-success{background:var(--color-background-button-success)}.button-tag{background:var(--color-background-button-tag);color:var(--color-text-button);font-size:75%;border-radius:6px;margin-right:4px;margin-bottom:1px}.button-tag.active{background:var(--color-background-button-tag-active);font-weight:bold}.button-error{background:var(--color-background-button-error);color:var(--color-text-button-error)}.button-warning{background:var(--color-background-button-warning);color:var(--color-text-button-warning)}.button-secondary{background:var(--color-background-button-secondary)}.button-cancel{background:var(--color-background-button-cancel)}.messages li{list-style:none;padding:1em;border-radius:10px;color:var(--color-text-messages);font-weight:bold}.messages li.message{background:var(--color-background-messages-message)}.messages li.error{background:var(--color-background-messages-error)}.messages li.notice{background:var(--color-background-messages-notice)}.messages.with-share-link>*:hover{cursor:pointer}label:hover{cursor:pointer}.grey-form-border{border:1px solid var(--color-border-notification);padding:.5rem;border-radius:5px}#token-table.pure-table td,#token-table.pure-table th{font-size:80%}.pure-form input[type=text].transparent-field{background-color:var(--color-background-new-watch-input-transparent) !important;color:var(--color-white) !important;border:1px solid hsla(0,0%,100%,.2) !important;box-shadow:none !important;-webkit-box-shadow:none !important}.pure-form input[type=text].transparent-field::placeholder{opacity:.5;color:hsla(0,0%,100%,.7);font-weight:lighter}#new-watch-form{background:var(--color-background-new-watch-form);padding:1em;border-radius:10px;max-width:100%}#new-watch-form #url::placeholder{font-weight:bold}#new-watch-form input{display:inline-block}#new-watch-form input:not(.pure-button){background-color:var(--color-background-new-watch-input);color:var(--color-text-new-watch-input)}#new-watch-form .label{display:none}#new-watch-form legend{color:var(--color-text-legend);font-weight:bold}@media only screen and (min-width: 760px){#new-watch-form #watch-add-wrapper-zone{display:flex;gap:.3rem;flex-direction:row;min-width:70vw}}#new-watch-form #watch-add-wrapper-zone>span{flex-grow:0}#new-watch-form #watch-add-wrapper-zone>span input{width:100%;padding-right:1em}#new-watch-form #watch-add-wrapper-zone>span:first-child{flex-grow:1}@media only screen and (max-width: 760px){#new-watch-form #watch-add-wrapper-zone #url{width:100%}}#new-watch-form #watch-group-tag{font-size:.9rem;padding:.3rem;display:flex;align-items:center;gap:.5rem;color:var(--color-white)}#new-watch-form #watch-group-tag label,#new-watch-form #watch-group-tag input{margin:0}#new-watch-form #watch-group-tag input{flex:1}#diff-col{padding-left:40px}#diff-jump{position:fixed;left:0px;top:120px;background:var(--color-background);padding:10px;border-top-right-radius:5px;border-bottom-right-radius:5px;box-shadow:1px 1px 4px var(--color-shadow-jump)}#diff-jump a{color:var(--color-link);cursor:pointer;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;-o-user-select:none}footer{padding:10px;background:var(--color-background);color:var(--color-text-footer);text-align:center}#feed-icon{vertical-align:middle}#new-version-text a{color:var(--color-link-new-version)}.watch-controls{color:#f8321b}.watch-controls .state-on img{opacity:.8}.watch-controls img{opacity:.2}.watch-controls img:hover{transition:opacity .3s;opacity:.8}.monospaced-textarea textarea{width:100%;font-family:monospace;white-space:pre;overflow-wrap:normal;overflow-x:auto}.pure-form fieldset{padding-top:0px}.pure-form fieldset ul{padding-bottom:0px;margin-bottom:0px}.pure-form .pure-control-group,.pure-form .pure-group,.pure-form .pure-controls{padding-bottom:1em}.pure-form .pure-control-group div,.pure-form .pure-group div,.pure-form .pure-controls div{margin:0px}.pure-form .pure-control-group .checkbox>*,.pure-form .pure-group .checkbox>*,.pure-form .pure-controls .checkbox>*{display:inline;vertical-align:middle}.pure-form .pure-control-group .checkbox>label,.pure-form .pure-group .checkbox>label,.pure-form .pure-controls .checkbox>label{padding-left:5px}.pure-form .pure-control-group legend,.pure-form .pure-group legend,.pure-form .pure-controls legend{color:var(--color-text-legend)}.pure-form .error input{background-color:var(--color-error-input)}.pure-form ul.errors{padding:.5em .6em;border:1px solid var(--color-error-list);border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form ul.errors li{margin-left:1em;color:var(--color-error-list)}.pure-form label{font-weight:bold}.pure-form textarea{width:100%}.pure-form .inline-radio ul{margin:0px;list-style:none}.pure-form .inline-radio ul li{display:flex;align-items:center;gap:1em}@media only screen and (max-width: 760px),(min-device-width: 768px)and (max-device-width: 980px){.edit-form{padding:.5em;margin:0}#nav-menu{overflow-x:scroll}}@media only screen and (max-width: 760px),(min-device-width: 768px)and (max-device-width: 980px){input[type=text]{width:100%}}.pure-table{border-color:var(--color-border-table-cell)}.pure-table thead{background-color:var(--color-background-table-thead);color:var(--color-text);border-bottom:1px solid var(--color-background-table-thead)}.pure-table td,.pure-table th{border-left-color:var(--color-border-table-cell)}.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form select,.pure-form textarea{border:var(--color-border-input);box-shadow:inset 0 1px 3px var(--color-shadow-input);background-color:var(--color-background-input);color:var(--color-text-input)}.pure-form input[type=color]:active,.pure-form input[type=date]:active,.pure-form input[type=datetime-local]:active,.pure-form input[type=datetime]:active,.pure-form input[type=email]:active,.pure-form input[type=month]:active,.pure-form input[type=number]:active,.pure-form input[type=password]:active,.pure-form input[type=search]:active,.pure-form input[type=tel]:active,.pure-form input[type=text]:active,.pure-form input[type=time]:active,.pure-form input[type=url]:active,.pure-form input[type=week]:active,.pure-form select:active,.pure-form textarea:active{background-color:var(--color-background-input)}input::placeholder,textarea::placeholder{color:var(--color-text-input-placeholder)}.m-d{min-width:100%}@media only screen and (min-width: 761px){.m-d{min-width:80%}}.pure-form-stacked>div:first-child{display:block}.tab-pane-inner{padding:0px}.tab-pane-inner:not(:target){display:none}.tab-pane-inner:target{display:block}.beta-logo{height:50px;right:-3px;top:-3px;position:absolute}#selector-header{padding-bottom:1em}.edit-form{max-width:95%}.edit-form .box-wrap{position:relative}.edit-form .inner{background:var(--color-background);padding:1.1rem}.edit-form #actions{display:block;background:var(--color-background)}.edit-form #actions .pure-control-group{display:flex;gap:.625em;flex-wrap:wrap}.edit-form .pure-form-message-inline{padding-left:0;color:var(--color-text-input-description)}.edit-form .pure-form-message-inline code{font-size:.875em}.border-fieldset{border:1px solid #ccc;padding:1rem;border-radius:5px;margin-bottom:1rem}.border-fieldset h3{margin-top:0}.border-fieldset fieldset:last-of-type{padding-bottom:0}.border-fieldset fieldset:last-of-type .pure-control-group{padding-bottom:0}ul{padding-left:1em;padding-top:0px;margin-top:4px}.time-check-widget tr{display:inline}.time-check-widget tr input[type=number]{width:5em}@media only screen and (max-width: 760px){.time-check-widget tbody{display:grid;grid-template-columns:auto 1fr auto 1fr;gap:.625em .3125em;align-items:center}.time-check-widget tr{display:contents}.time-check-widget tr th{text-align:right;padding-right:5px}.time-check-widget tr input[type=number]{width:100%;max-width:5em}}#webdriver_delay{width:5em}#api-key:hover{cursor:pointer}#api-key-copy{color:var(--color-api-key)}.button-green{background-color:var(--color-background-button-green)}.button-red{background-color:var(--color-background-button-red)}.noselect{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}[type=checkbox],[type=radio]{width:17px;height:17px;border-radius:5px;cursor:pointer}.inline-warning{border:1px solid var(--color-border-warning);padding:.5rem;border-radius:5px;color:var(--color-warning)}.inline-warning>span{display:inline-block;vertical-align:middle}.inline-warning img.inline-warning-icon{display:inline;height:26px;vertical-align:middle}.tracking-ldjson-price-data{background-color:var(--color-background-button-green);color:#000;opacity:.6}.ldjson-price-track-offer{font-weight:bold;font-style:italic}.ldjson-price-track-offer a.pure-button{border-radius:3px;padding:3px;background-color:var(--color-background-button-green)}.price-follow-tag-icon{display:inline-block;height:.8rem;vertical-align:middle}#quick-watch-processor-type ul#processor{color:#fff;padding-left:0px}#quick-watch-processor-type ul#processor li{list-style:none;font-size:.9rem;display:grid;grid-template-columns:auto 1fr;align-items:center;gap:.5rem;margin-bottom:.5rem}#quick-watch-processor-type label,#quick-watch-processor-type input{padding:0;margin:0}.restock-label.in-stock{background-color:#7a0cc5;color:#fff}.restock-label.not-in-stock{background-color:var(--color-background-button-cancel);color:#777}.restock-label.error{background-color:var(--color-background-button-error);color:#fff;opacity:.7}.restock-label.price{border:1px solid var(--color-background-button-cancel)}.restock-label svg{vertical-align:middle}.price-change{white-space:nowrap;font-weight:700;font-size:90%;margin-left:4px;vertical-align:middle}.price-change.down{color:var(--color-background-button-green)}.price-change.up{color:var(--color-background-button-error)}#chrome-extension-link{padding:9px;border:1px solid var(--color-grey-800);border-radius:10px;vertical-align:middle}#chrome-extension-link img{height:21px;padding:2px;vertical-align:middle}#realtime-conn-error{position:fixed;bottom:0;left:0;background:var(--color-warning);padding:10px;font-size:.8rem;color:#fff;opacity:.8;z-index:100}#bottom-horizontal-offscreen{position:fixed;bottom:0;left:0;right:0;width:100%;min-height:50px;max-height:50vh;background:hsla(0,0%,100%,.7215686275);border-top:1px solid var(--color-border-table-cell);padding:10px;box-shadow:0 -2px 10px rgba(0,0,0,.2);z-index:100;overflow-y:auto;transition:opacity .3s ease-in-out;scroll-margin-bottom:10px;display:flex;justify-content:center;align-items:center}ul#highlightSnippetActions{list-style:none}ul#highlightSnippetActions li{display:inline-block}@media only screen and (max-width: 768px){.box{padding:.25rem !important}}.box{color:var(--color-white);border-width:1px;border-style:dashed;border-color:hsla(0,0%,100%,.25);border-image:initial;background:hsla(0,0%,100%,.04);border-radius:var(--common-round-border);padding:1.1rem}header{color:var(--color-white)}#heart-us svg{display:inline-block;vertical-align:middle;cursor:pointer;width:1.4rem}
+.header{color:var(--color-text-menu-heading)}.header a{color:var(--color-text-menu-heading)}.header::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;pointer-events:none;background:linear-gradient(to right, rgba(200, 200, 200, 0.02), rgba(200, 200, 200, 0.6))}ul#top-right-menu{list-style:none;margin-left:auto;padding:0;margin-top:0;margin-right:0;margin-bottom:0;display:grid;gap:1.1rem;grid-auto-flow:column;grid-auto-columns:max-content;align-items:center}ul#top-right-menu .toggle-button{padding:0}.current-diff-url{flex:1 1 auto;min-width:0;overflow:hidden;white-space:nowrap;text-align:left;margin:0 .55rem;-webkit-mask-image:linear-gradient(to right, #000 calc(100% - 2.5em), transparent);mask-image:linear-gradient(to right, #000 calc(100% - 2.5em), transparent)}.current-diff-url span{overflow:visible;white-space:nowrap}.pure-menu-horizontal{padding:.55rem;display:flex;justify-content:space-between;align-items:center}.pure-menu-horizontal svg{height:1.3rem}.fi{height:1.3rem;cursor:pointer}#pure-menu-horizontal-spinner{height:2px;background:linear-gradient(-75deg, #ff6000, #ff8f00, #ffdd00, #ed0000);background-size:400% 400%;animation:gradient 200s ease infinite;opacity:.8;position:fixed;bottom:0;left:0;width:100%;z-index:100;pointer-events:none}.status-pill{display:inline-flex;align-items:center;gap:8px;height:30px;padding:0 12px;border-radius:var(--common-round-border);border:1px solid hsla(0,0%,100%,.25);background:hsla(0,0%,100%,.1);color:var(--color-text-menu-heading);font-size:.78rem;font-weight:600;white-space:nowrap;text-decoration:none}.status-pill:hover{background:hsla(0,0%,100%,.18)}.status-pill .live-dot{width:8px;height:8px;border-radius:50%;background:#42dd53;box-shadow:0 0 0 3px rgba(66,221,83,.3);animation:status-pill-pulse 2s infinite}.status-pill.paused .live-dot{background:#e8a33d;box-shadow:0 0 0 3px rgba(232,163,61,.3);animation:none}.status-pill .action-icon{width:16px;height:16px}.status-pill.muted{opacity:.8}.status-pill.muted .action-icon{color:#e8a33d}@keyframes status-pill-pulse{0%,100%{opacity:1}50%{opacity:.4}}.menu-pop-wrap{position:relative}.icon-btn{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border:0;border-radius:var(--common-round-border);background:rgba(0,0,0,0);color:var(--color-text-menu-heading);cursor:pointer}.icon-btn svg{width:18px;height:18px;fill:none;stroke:currentColor}.icon-btn:hover{background:hsla(0,0%,100%,.14)}.menu-pop{display:none;position:absolute;top:calc(100% + 8px);right:0;min-width:220px;padding:6px;border:1px solid var(--color-border-table-cell);border-radius:12px;background:var(--color-background);box-shadow:0 18px 50px rgba(0,0,0,.18);z-index:80}.menu-pop.open{display:block}.menu-pop .mi{display:flex;align-items:center;gap:10px;width:100%;padding:9px 10px;border:0;border-radius:8px;background:rgba(0,0,0,0);color:var(--color-text);font-size:.85rem;text-align:left;text-decoration:none;white-space:nowrap;cursor:pointer}.menu-pop .mi:hover{background:var(--color-background-menu-link-hover)}.menu-pop .mi .ico{display:inline-flex;color:var(--color-text-input-description)}.menu-pop .mi .ico svg{width:16px;height:16px;fill:none;stroke:currentColor}.menu-pop .mi .right{margin-left:auto;font-size:.75rem;color:var(--color-text-input-description)}.top-menu-list .action-label{color:var(--color-text-menu-heading)}@media only screen and (max-width: 768px){.top-menu-list .action-label{display:none}}#inline-menu-extras-group{list-style:none;margin:0;padding:0;display:grid;gap:.55rem;grid-auto-flow:column;grid-auto-columns:max-content;align-items:center}@media only screen and (max-width: 980px){#nav-menu #menu-settings span{display:none}}:root{--body-main-text-size: 0.9rem;--color-white: #fff;--color-grey-50: #111;--color-grey-100: #262626;--color-grey-200: #333;--color-grey-300: #444;--color-grey-325: #555;--color-grey-350: #565d64;--color-grey-400: #666;--color-grey-500: #777;--color-grey-600: #999;--color-grey-700: #cbcbcb;--color-grey-750: #ddd;--color-grey-800: #e0e0e0;--color-grey-850: #eee;--color-grey-900: #f2f2f2;--color-black: #000;--color-dark-red: #a00;--color-light-red: #dd0000;--color-background-page: var(--color-grey-100);--color-background-gradient-first: #5ad8f7;--color-background-gradient-second: #2f50af;--color-background-gradient-third: #9150bf;--color-background: var(--color-white);--color-text: var(--color-grey-200);--color-link: #1b98f8;--color-menu-accent: #ed5900;--color-background-code: var(--color-grey-850);--color-error: var(--color-dark-red);--color-error-input: #ffebeb;--color-error-list: var(--color-light-red);--color-table-background: var(--color-background);--color-table-stripe: var(--color-grey-900);--watchlist-row-selected: #e3f0ff;--watchlist-row-hover: #f2f2f2;--color-text-tab: var(--color-white);--color-background-tab: rgba(255, 255, 255, 0.2);--color-background-tab-hover: rgba(255, 255, 255, 0.5);--color-text-tab-active: #222;--color-api-key: #0078e7;--color-background-button-primary: #0078e7;--color-background-button-green: #42dd53;--color-background-button-red: #dd4242;--color-background-button-success: rgb(28, 184, 65);--color-background-button-error: rgb(202, 60, 60);--color-text-button-error: var(--color-white);--color-background-button-warning: rgb(202, 60, 60);--color-text-button-warning: var(--color-white);--color-background-button-secondary: rgb(66, 184, 221);--color-background-button-cancel: rgb(200, 200, 200);--color-text-button: var(--color-white);--color-background-button-tag: rgb(99, 99, 99);--color-background-snapshot-age: #dfdfdf;--color-error-text-snapshot-age: var(--color-white);--color-error-background-snapshot-age: #ff0000;--color-background-button-tag-active: #9c9c9c;--color-text-messages: var(--color-white);--color-background-messages-message: rgba(255, 255, 255, .2);--color-background-messages-error: rgba(255, 1, 1, .5);--color-background-messages-notice: rgba(255, 255, 255, .5);--color-border-notification: #ccc;--color-background-checkbox-operations: rgba(0, 0, 0, 0.05);--color-warning: #ff3300;--color-border-warning: var(--color-warning);--color-text-legend: var(--color-white);--color-link-new-version: #e07171;--color-last-checked: #bbb;--color-text-footer: #444;--color-border-watch-table-cell: #eee;--color-text-watch-tag-list: rgba(231, 0, 105, 0.4);--color-background-new-watch-form: rgba(0, 0, 0, 0.05);--color-background-new-watch-input: var(--color-white);--color-background-new-watch-input-transparent: rgba(255, 255, 255, 0.1);--color-text-new-watch-input: var(--color-text);--color-border-input: var(--color-grey-500);--color-shadow-input: var(--color-grey-400);--color-background-input: var(--color-white);--color-text-input: var(--color-text);--color-text-input-description: var(--color-grey-500);--color-text-input-placeholder: var(--color-grey-600);--color-background-table-thead: var(--color-grey-800);--color-border-table-cell: var(--color-grey-700);--color-text-menu-heading: var(--color-white);--color-text-menu-link: var(--color-grey-500);--color-background-menu-link-hover: var(--color-grey-850);--color-text-menu-link-hover: var(--color-grey-300);--color-shadow-jump: var(--color-grey-500);--color-icon-github: var(--color-black);--color-watch-table-error: var(--color-dark-red);--color-watch-table-row-text: var(--color-grey-100);--color-table-line: #e7e9ee;--highlight-trigger-text-bg-color: #1b98f8;--highlight-ignored-text-bg-color: var(--color-grey-700);--highlight-blocked-text-bg-color: rgb(202, 60, 60);--color-sidebar-bg: rgba(255, 255, 255, 0.97);--color-sidebar-text: var(--color-text);--color-sidebar-shadow: 6px 0 28px rgba(0, 0, 0, 0.18);--color-sidebar-item-hover-bg: rgba(0, 0, 0, 0.06);--color-sidebar-item-active-bg: rgba(0, 0, 0, 0.10);--common-round-border: 8px}html[data-darkmode=true]{--color-link: #59bdfb;--color-text: var(--color-white);--color-table-line: #262c37;--color-background-gradient-first: #3f90a5;--color-background-gradient-second: #1e316c;--color-background-gradient-third: #4d2c64;--color-background-new-watch-input: var(--color-grey-100);--color-background-new-watch-input-transparent: var(--color-grey-100);--color-text-new-watch-input: var(--color-text);--color-background-table-thead: var(--color-grey-200);--color-table-background: var(--color-grey-300);--color-table-stripe: var(--color-grey-325);--watchlist-row-selected: #1e3a5f;--watchlist-row-hover: #2a2a2a;--color-background: var(--color-grey-300);--color-text-menu-heading: var(--color-grey-850);--color-text-menu-link: var(--color-grey-800);--color-border-table-cell: var(--color-grey-400);--color-text-tab-active: var(--color-text);--color-border-input: var(--color-grey-400);--color-shadow-input: var(--color-grey-50);--color-background-input: var(--color-grey-350);--color-text-input-description: var(--color-grey-600);--color-text-input-placeholder: var(--color-grey-600);--color-text-watch-tag-list: rgba(250, 62, 146, 0.4);--color-background-code: var(--color-grey-200);--color-background-tab: rgba(0, 0, 0, 0.2);--color-background-tab-hover: rgba(0, 0, 0, 0.5);--color-background-snapshot-age: var(--color-grey-200);--color-shadow-jump: var(--color-grey-200);--color-icon-github: var(--color-white);--color-watch-table-error: var(--color-light-red);--color-watch-table-row-text: var(--color-grey-800);--color-sidebar-bg: rgba(8, 10, 14, 0.97);--color-sidebar-text: var(--color-white);--color-sidebar-shadow: 6px 0 28px rgba(0, 0, 0, 0.45);--color-sidebar-item-hover-bg: rgba(255, 255, 255, 0.06);--color-sidebar-item-active-bg: rgba(255, 255, 255, 0.10)}html[data-darkmode=true] .icon-spread{filter:hue-rotate(-10deg) brightness(1.5)}html[data-darkmode=true] .watch-table .title-col a[target=_blank]::after,html[data-darkmode=true] .watch-table .current-diff-url::after{filter:invert(0.5) hue-rotate(10deg) brightness(2)}html[data-darkmode=true] .watch-table .status-browsersteps{filter:invert(0.5) hue-rotate(10deg) brightness(1.5)}html[data-darkmode=true] .watch-table .watch-controls .state-off svg{opacity:.3}html[data-darkmode=true] .watch-table .watch-controls .state-on svg{opacity:1}html[data-darkmode=true] .watch-table .unviewed{color:#fff}html[data-darkmode=true] .watch-table .unviewed.error{color:var(--color-watch-table-error)}.arrow{border:solid var(--color-border-input);border-width:0 2px 2px 0;display:inline-block;padding:3px}.arrow.right{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}.arrow.left{transform:rotate(135deg);-webkit-transform:rotate(135deg)}.arrow.up,.arrow.asc{transform:rotate(-135deg);-webkit-transform:rotate(-135deg)}.arrow.down,.arrow.desc{transform:rotate(45deg);-webkit-transform:rotate(45deg)}#browser_steps th{display:none}#browser_steps li{list-style:decimal;padding:5px}#browser_steps li.browser-step-with-error{background-color:#ffd6d6;border-radius:4px}#browser_steps li:not(:first-child):hover{opacity:1}#browser_steps li .control{padding-left:5px;padding-right:5px}#browser_steps li .control a{font-size:70%}#browser_steps li.empty{padding:0px;opacity:.35}#browser_steps li.empty .control{display:none}#browser_steps li:hover{background:#eee}#browser_steps li>label{display:none}@media only screen and (min-width: 760px){#browser-steps .flex-wrapper{display:flex;flex-flow:row;height:70vh;font-size:80%}#browser-steps .flex-wrapper #browser-steps-ui{flex-grow:1;flex-shrink:1;flex-basis:0;background-color:#eee;border-radius:5px}#browser-steps-fieldlist{flex-grow:0;flex-shrink:0;flex-basis:auto;max-width:400px;padding-left:1rem;overflow-y:scroll}#browsersteps-selector-wrapper{height:100% !important}}#browsersteps-selector-wrapper{width:100%;overflow-y:scroll;position:relative;height:80vh}#browsersteps-selector-wrapper>img{position:absolute;max-width:100%}#browsersteps-selector-wrapper>canvas{position:relative;max-width:100%}#browsersteps-selector-wrapper>canvas:hover{cursor:pointer}#browsersteps-selector-wrapper .loader{position:absolute;left:50%;top:50%;transform:translate(-50%, -50%);z-index:100;max-width:350px;text-align:center}#browsersteps-selector-wrapper .spinner,#browsersteps-selector-wrapper .spinner:after{width:80px;height:80px;font-size:3px}#browsersteps-selector-wrapper #browsersteps-click-start{color:var(--color-grey-400)}#browsersteps-selector-wrapper #browsersteps-click-start:hover{cursor:pointer}ul#requests-extra_proxies{list-style:none}ul#requests-extra_proxies li>label{display:none}ul#requests-extra_proxies table tr{display:table-row}ul#requests-extra_proxies table tr input[type=text]{width:100%}@media only screen and (min-width: 1024px){ul#requests-extra_proxies table tr{display:inline}}#request label[for=proxy]{display:inline-block}body.proxy-check-active #request .proxy-check-details{font-size:80%;color:#555;display:block;padding-left:2em;max-width:500px}body.proxy-check-active #request .proxy-timing{font-size:80%;padding-left:1rem;color:var(--color-link)}#recommended-proxy{display:grid;gap:2rem;padding-bottom:1em}@media(min-width: 991px){#recommended-proxy{grid-template-columns:repeat(2, 1fr)}}#recommended-proxy>div{border:1px #aaa solid;border-radius:4px;padding:1em}#extra-proxies-setting{border:1px solid var(--color-grey-800);border-radius:4px;margin:1em;padding:1em}ul#requests-extra_browsers{list-style:none}ul#requests-extra_browsers li>label{display:none}ul#requests-extra_browsers table tr{display:table-row}ul#requests-extra_browsers table tr input[type=text]{width:100%}@media only screen and (min-width: 1280px){ul#requests-extra_browsers table tr{display:inline}ul#requests-extra_browsers table tr input[type=text]{width:100%}}#extra-browsers-setting{border:1px solid var(--color-grey-800);border-radius:4px;margin:1em;padding:1em}.pagination-page-info{text-transform:capitalize}.pagination.menu>*{display:inline-block}.pagination.menu li{display:inline-block}.pagination.menu a{padding:.65rem;margin:3px;border:none;background:#444;border-radius:2px;color:var(--color-text-button)}.pagination.menu a.disabled{display:none}.pagination.menu a.active{font-weight:bold;background:#888}.pagination.menu a:hover{background:#999}.spinner,.spinner:after{border-radius:50%;width:10px;height:10px}.spinner{margin:0px auto;font-size:3px;vertical-align:middle;display:inline-block;text-indent:-9999em;border-top:1.1em solid rgba(38,104,237,.2);border-right:1.1em solid rgba(38,104,237,.2);border-bottom:1.1em solid rgba(38,104,237,.2);border-left:1.1em solid #2668ed;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);-webkit-animation:load8 1.1s infinite linear;animation:load8 1.1s infinite linear}@-webkit-keyframes load8{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes load8{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.toggle-light-mode .icon-dark{display:none}html[data-darkmode=true] .toggle-light-mode .icon-light{display:none}html[data-darkmode=true] .toggle-light-mode .icon-dark{display:block}.pure-menu-link{padding:.5rem 1em;line-height:1.2rem}#menu-mute img,#menu-pause img{height:1.2rem}.pure-menu-item{height:initial}.pure-menu-item svg{height:1.2rem}.pure-menu-item *{vertical-align:middle}.pure-menu-item .bi-heart:hover{cursor:pointer}.pure-menu-item.active .pure-menu-link{background-color:var(--color-background-menu-link-hover);color:var(--color-text-menu-heading)}.pure-menu-item .action-icon{stroke:var(--color-text-menu-heading)}#overlay{opacity:.95;position:fixed;width:350px;max-width:100%;height:100%;top:50px;right:-350px;background-color:var(--color-table-stripe);border:1px solid #aaa;z-index:2;transform:translateX(0);transition:transform .5s ease}#overlay.visible{transform:translateX(-100%)}#overlay .content{font-size:.875rem;padding:1.1rem;max-width:400px;color:var(--color-watch-table-row-text)}#heartpath{height:1.6rem;width:1.6rem;z-index:100;transition:all ease .3s !important}#heartpath:hover{fill:red !important;transition:all ease .3s !important}.minitabs-wrapper{width:100%}.minitabs-wrapper>div[id]{padding:20px;border:1px solid #ccc;border-top:none}.minitabs-wrapper .minitabs-content{width:100%;display:flex}.minitabs-wrapper .minitabs-content>div{flex:1 1 auto;min-width:0;overflow:scroll}.minitabs-wrapper .minitabs{display:flex;border-bottom:1px solid #ccc}.minitabs-wrapper .minitab{flex:1;text-align:center;padding:12px 0;text-decoration:none;color:#333;background-color:#f1f1f1;border:1px solid #ccc;border-bottom:none;cursor:pointer;transition:background-color .3s}.minitabs-wrapper .minitab:hover{background-color:#ddd}.minitabs-wrapper .minitab.active{background-color:#fff;font-weight:bold}@media(min-width: 800px){body.preview-text-enabled #filters-and-triggers>div{display:flex;gap:20px;position:relative}}body.preview-text-enabled #edit-text-filter,body.preview-text-enabled #text-preview{flex:1;align-self:flex-start}body.preview-text-enabled #edit-text-filter #pro-tips{display:none}body.preview-text-enabled #text-preview{position:sticky;top:20px;padding-top:1rem;padding-bottom:1rem;display:block !important}body.preview-text-enabled #activate-text-preview{background-color:var(--color-grey-500)}body.preview-text-enabled .monospace-preview{background:var(--color-background-input);border:1px solid var(--color-grey-600);padding:1rem;color:var(--color-text-input);font-family:"Courier New",Courier,monospace;font-size:70%;word-break:break-word;white-space:pre-wrap}#activate-text-preview{right:0;position:absolute;z-index:3;box-shadow:1px 1px 4px var(--color-shadow-jump)}.cdio-table{width:100%;font-size:var(--body-main-text-size)}.cdio-table thead{text-transform:uppercase}.cdio-table thead a{color:var(--color-text)}.cdio-table td,.cdio-table th{vertical-align:middle;border:none}.cdio-table tbody tr{color:var(--color-watch-table-row-text);border-bottom:1px solid var(--color-table-line);background-color:var(--color-table-background)}.cdio-table tbody tr td{background-color:var(--color-table-background)}.cdio-table tbody tr:hover>td{background-color:var(--watchlist-row-hover)}.cdio-table-clip{border-radius:var(--common-round-border);overflow:hidden;margin-bottom:1.1rem}.seg{display:inline-flex;align-items:center;gap:2px;padding:2px;border:1px solid var(--color-border-table-cell);border-radius:var(--common-round-border);background:var(--color-background-table-thead)}.seg a,.seg button{display:inline-flex;align-items:center;gap:6px;border:0;background:rgba(0,0,0,0);color:var(--color-text-input-description);font-size:.8rem;font-weight:600;line-height:1.4;padding:5px 11px;border-radius:calc(var(--common-round-border) - 1px);text-decoration:none;cursor:pointer;white-space:nowrap}.seg a:hover,.seg button:hover{color:var(--color-text)}.seg a.active,.seg a:hover,.seg button.active,.seg button:hover{background:var(--color-background);color:var(--color-text);box-shadow:0 1px 2px rgba(0,0,0,.15)}html[data-darkmode=true] .seg a.active,html[data-darkmode=true] .seg button.active{background:var(--color-grey-400);box-shadow:0 1px 2px rgba(0,0,0,.5)}.seg-count{font-size:.62rem;line-height:1;font-weight:700;padding:2px 6px;border-radius:999px;background:var(--color-background-button-tag);color:var(--color-white)}.seg-count--unread{background:#3e95bb}.seg-count--error{background:var(--color-background-button-error)}.seg-count--deal{background:var(--color-background-button-success)}.cdio-btn{display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 12px;border-radius:var(--common-round-border);border:1px solid var(--color-border-table-cell);background:var(--color-background);color:var(--color-text-input-description);font-family:inherit;font-size:.8rem;font-weight:600;line-height:1;white-space:nowrap;text-decoration:none;cursor:pointer;transition:border-color .12s ease,color .12s ease,background-color .12s ease}.cdio-btn:hover{border-color:var(--color-border-input);color:var(--color-text)}.cdio-btn:focus-visible{outline:2px solid var(--color-link);outline-offset:2px}.cdio-btn svg{width:15px;height:15px;stroke:currentColor}.cdio-btn img{height:15px;display:block}.cdio-btn--icon{width:32px;padding:0;justify-content:center}.cdio-btn--sm{height:26px;padding:0 9px;font-size:.72rem;gap:5px}.cdio-btn--sm svg{width:13px;height:13px}.cdio-btn--primary{background:var(--color-background-button-primary);border-color:var(--color-background-button-primary);color:var(--color-text-button)}.cdio-btn--primary:hover{background:var(--color-link);border-color:var(--color-link);color:var(--color-text-button)}.cdio-btn--danger{color:var(--color-background-button-error)}.cdio-btn--danger:hover{color:var(--color-background-button-error);border-color:var(--color-background-button-error)}.cdio-btn--warning{color:#d68a00}.cdio-btn--warning:hover{color:#d68a00;border-color:#d68a00}.watch-table tr:has(input[name=uuids]:checked)>td{background-color:var(--watchlist-row-selected)}.watch-table tbody tr:hover td.buttons *,.watch-table tbody tr:focus-within td.buttons *,.watch-table tbody tr:has(input[name=uuids]:checked) td.buttons *{opacity:1}.select-all-banner{margin:.4rem 0;padding:.5rem .75rem;border-radius:var(--common-round-border);background:var(--watchlist-row-selected);color:var(--color-watch-table-row-text);font-size:var(--body-main-text-size)}.select-all-banner button{margin-left:.5rem;vertical-align:baseline}.watch-controls svg{width:18px;height:18px;stroke:currentColor;fill:none;vertical-align:middle}#stats_row{display:flex;align-items:center;width:100%;color:#fff;font-size:.85rem}#stats_row>*{padding-bottom:.5rem}#stats_row .left{text-align:left}#stats_row .left .records-selected{margin-top:.25rem;opacity:.9}#stats_row .right{opacity:.5;transition:opacity .6s ease;margin-left:auto;text-align:right}body.has-queue #stats_row .right{opacity:1}#checkbox-operations{margin-bottom:.55rem;background:var(--color-background-new-watch-form);padding:1em;border-radius:10px;max-width:100%;position:sticky;top:20px;display:none}#checkbox-operations button{margin-bottom:3px;margin-top:3px;display:inline-flex;align-items:center}#checkbox-operations i,#checkbox-operations svg{width:14px;height:14px;stroke:#fff}body.watch-selection-active #checkbox-operations{display:block}.watch-table .checkbox-uuid{text-align:center}.watch-table .checkbox-uuid>*{vertical-align:middle}@media only screen and (max-width: 1200px){.watch-table .last-checked,.watch-table .last-changed{text-align:center}}.watch-table #th-webpage{text-align:center}.watch-table tbody tr.unviewed{font-weight:bold}.watch-table tbody tr td.inline.title-col{width:100%}.watch-table tbody tr td.inline.title-col .grid-wrapper{display:grid;grid-template-columns:auto minmax(0, 1fr) auto;grid-auto-columns:auto;align-items:center;gap:.55rem}.watch-table tbody tr td.inline.title-col .grid-wrapper>.favicon{grid-column:1}.watch-table tbody tr td.inline.title-col .grid-wrapper>.watch-text-info{grid-column:2}.watch-table tbody tr td.inline.title-col .grid-wrapper>.status-icons{grid-column:3}.watch-table tbody tr td.inline.title-col .grid-wrapper>.restock-info-wrap{grid-column:4}@media only screen and (max-width: 1200px){.watch-table tbody tr td.inline.title-col .grid-wrapper>.restock-info-wrap{grid-column:1/-1;justify-self:center}}.watch-table tbody tr .watch-text-info{line-height:1.5}.watch-table tbody tr.checking-now td:first-child{position:relative}.watch-table tbody tr.checking-now td:first-child::before{content:"";position:absolute;top:0;bottom:0;left:0;width:3px;background-color:#293eff}.watch-table tbody tr.checking-now td.last-checked .spinner-wrapper{display:inline-block !important;white-space:nowrap !important}.watch-table tbody tr.checking-now td.last-checked .spinner{margin-right:.275rem}.watch-table tbody tr.checking-now td.last-checked .innertext{display:none !important}.watch-table tbody tr.queued a.recheck{display:none !important}.watch-table tbody tr.queued a.already-in-queue-button{display:inline-flex !important;opacity:.8}.watch-table tbody tr.paused a.pause-toggle.state-on{display:inline !important}.watch-table tbody tr.paused a.pause-toggle.state-off{display:none !important}.watch-table tbody tr.notification_muted a.mute-toggle.state-on{display:inline !important}.watch-table tbody tr.notification_muted a.mute-toggle.state-off{display:none !important}.watch-table tbody tr.has-error .error-text{display:block !important;color:var(--color-watch-table-error)}.watch-table tbody tr.single-history a.preview-link{display:inherit !important}.watch-table tbody tr.multiple-history a.history-link{display:inherit !important}.watch-table tbody tr.has-favicon.unviewed img.favicon{opacity:1 !important;border-radius:4px}.watch-table td.buttons{font-size:12px;white-space:nowrap}.watch-table td.buttons>div{display:inline-flex;align-items:center;gap:6px}.watch-table td.buttons>div>*{opacity:0;transition:opacity .12s ease}.watch-table td.title-col{word-break:break-all;white-space:normal}.watch-table td a.external::after{content:"";display:inline-block;width:var(--body-main-text-size);height:var(--body-main-text-size);vertical-align:-0.1em;background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23777' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E") no-repeat center/contain;margin:0 3px 0 5px}.watch-table td.watch-controls>div{display:flex;justify-content:space-between;align-items:center}@media(min-width: 1020px){.watch-table th{white-space:nowrap}}.watch-table th#h-lastchanged,.watch-table th#h-lastchecked{text-align:center}.watch-table th a{font-weight:normal}.watch-table th a.active{font-weight:bolder}.watch-table th a.inactive .arrow{display:none}.watch-table th#mute-pause{white-space:nowrap}.watch-table th#mute-pause>div{display:flex;justify-content:space-between;align-items:center}.watch-table.favicon-not-enabled tr .favicon{display:none}.watch-table .status-icons{white-space:nowrap;display:flex;align-items:center;gap:4px}.watch-table .status-icons>*{vertical-align:middle}.watch-table .title-wrapper{display:flex;align-items:center;gap:10px}.watch-table .title-col-inner{display:inline-block;vertical-align:middle}.watch-table img.favicon{vertical-align:middle;max-width:36px;max-height:36px;height:36px;border-radius:var(--common-round-border)}.watch-table img.favicon:hover{outline:2px solid color-mix(in srgb, var(--color-watch-table-row-text) 50%, transparent);outline-offset:1px}body.watch-selection-active #buttons-for-all-watches{display:none !important}#buttons-for-all-watches{display:flex;align-items:center;justify-content:flex-start;flex-wrap:wrap;gap:.55rem;margin:0}#buttons-for-all-watches #post-list-mark-views{display:none}body.has-any-unviewed #post-list-mark-views{display:inline-flex !important}#watch-table-wrapper{display:inline-block;width:100%}#watch-table-wrapper #list-related-buttons{display:flex;align-items:center;justify-content:flex-start;flex-wrap:wrap;gap:.55rem;margin:0;padding:1.1rem 0 .55rem 0}#watch-table-wrapper.has-error #list-related-buttons #post-list-with-errors{display:inline-flex !important}#watch-table-wrapper.has-unread-changes #list-related-buttons #post-list-unread{display:inline-flex !important}#watch-table-wrapper #tag-lister #tag-all{opacity:1}#watch-table-wrapper #tag-lister.active-tag .button-tag{opacity:.35}#watch-table-wrapper #tag-lister.active-tag .button-tag.active,#watch-table-wrapper #tag-lister.active-tag .button-tag:hover{opacity:1}.content .group-overview-table{width:100%}.content .group-overview-table .pure-button{margin-top:.3rem;margin-bottom:.3rem}.content .group-overview-table .watch-controls,.content .group-overview-table .watch-count{text-align:center}.content .group-overview-table td{padding:5px !important;color:var(--color-watch-table-row-text)}.pure-button{border-radius:var(--common-round-border)}body.blueprint-watchlist #add-watch-ui{margin-bottom:1.1rem;padding:0}body.blueprint-watchlist #add-watch-url-row{margin-bottom:0 !important}body.blueprint-watchlist #quick-watch-llm-intent{margin-top:.55rem}body.blueprint-watchlist #url{width:auto}@media only screen and (min-width: 980px){body.blueprint-watchlist #url{min-width:32rem;max-width:min(80%,80vw);box-sizing:border-box}}body.blueprint-watchlist #quick-watch-llm-intent,body.blueprint-watchlist #quick-watch-processor-type{display:none}body.blueprint-watchlist #new-watch-form:has(#url:not(:placeholder-shown)) #quick-watch-llm-intent,body.blueprint-watchlist #new-watch-form:has(#url:not(:placeholder-shown)) #quick-watch-processor-type{display:block}@media(max-width: 767px){.watch-table thead{display:block}.watch-table thead tr th{display:inline-block}.watch-table thead tr th .hide-on-mobile{display:none}.watch-table thead .empty-cell{display:none}.watch-table .last-checked::before{color:var(--color-text);content:attr(data-label) " "}.watch-table .last-changed::before{color:var(--color-text);content:attr(data-label) " "}.watch-table td.inline{display:inline-block}.watch-table .pure-table td,.watch-table .pure-table th{border:none}.watch-table td{border:none;border-bottom:1px solid var(--color-border-watch-table-cell);vertical-align:middle}.watch-table td:before{top:6px;left:6px;width:45%;padding-right:10px;white-space:nowrap}.watch-table.pure-table-striped tr{background-color:var(--color-table-background)}.watch-table.pure-table-striped tr:nth-child(2n-1){background-color:var(--color-table-stripe)}.watch-table.pure-table-striped tr:nth-child(2n-1) td{background-color:inherit}}@media(max-width: 767px){.watch-table tbody tr{padding-bottom:10px;padding-top:10px;display:grid;grid-template-columns:40px 1fr 100px;grid-template-rows:auto auto auto auto;gap:.5rem}.watch-table tbody tr .counter-i{display:none}.watch-table tbody tr>td{border-bottom:none}.watch-table tbody tr>td[colspan]{grid-column:1/-1}.watch-table tbody tr>td.title-col{grid-column:1/-1;grid-row:1}.watch-table tbody tr>td.title-col .watch-title{font-size:.92rem}.watch-table tbody tr>td.title-col .link-spread{display:none}.watch-table tbody tr>td.last-checked{grid-column:1/-1;grid-row:2}.watch-table tbody tr>td.last-changed{grid-column:1/-1;grid-row:3}.watch-table tbody tr>td.checkbox-uuid{grid-column:1;grid-row:4}.watch-table tbody tr>td.buttons{grid-column:2;grid-row:4;display:flex;align-items:center;justify-content:flex-start}.watch-table tbody tr>td.watch-controls{grid-column:3;grid-row:4;display:grid;place-items:center}.watch-table tbody tr>td.watch-controls a img{padding:10px}.pure-table td{padding:0 !important}}@media(min-width: 768px){.watch-table thead tr th .hide-on-desktop{display:none}.watch-table td.last-checked .innertext,.watch-table td.last-changed .innertext{white-space:nowrap}}#llm-intent-section textarea{white-space:normal;overflow-wrap:break-word;overflow-x:hidden;overflow-y:auto;resize:vertical;font-family:inherit}ul#conditions_match_logic{list-style:none}ul#conditions_match_logic input,ul#conditions_match_logic label,ul#conditions_match_logic li{display:inline-block}ul#conditions_match_logic li{padding-right:1em}.fieldlist_formfields{width:100%;background-color:var(--color-background, #fff);border-radius:4px;border:1px solid var(--color-border-table-cell, #cbcbcb)}.fieldlist_formfields .fieldlist-header{display:flex;background-color:var(--color-background-table-thead, #e0e0e0);font-weight:bold;border-bottom:1px solid var(--color-border-table-cell, #cbcbcb)}.fieldlist_formfields .fieldlist-header-cell{flex:1;padding:.5em 1em;text-align:left}.fieldlist_formfields .fieldlist-header-cell:last-child{flex:0 0 120px}.fieldlist_formfields .fieldlist-body{display:flex;flex-direction:column}.fieldlist_formfields .fieldlist-row{display:flex;border-bottom:1px solid var(--color-border-table-cell, #cbcbcb)}.fieldlist_formfields .fieldlist-row:last-child{border-bottom:none}.fieldlist_formfields .fieldlist-row:nth-child(2n-1){background-color:var(--color-table-stripe, #f2f2f2)}.fieldlist_formfields .fieldlist-row.error-row{background-color:var(--color-error-input, #ffdddd)}.fieldlist_formfields .fieldlist-cell{flex:1;padding:.5em 1em;display:flex;flex-direction:column;justify-content:center}.fieldlist_formfields .fieldlist-cell input,.fieldlist_formfields .fieldlist-cell select{width:100%}.fieldlist_formfields .fieldlist-cell.fieldlist-actions{flex:0 0 120px;display:flex;flex-direction:row;align-items:center;gap:4px}.fieldlist_formfields ul.errors{margin-top:.5em;margin-bottom:0;padding:.5em;background-color:var(--color-error-background-snapshot-age, #ffdddd);border-radius:4px;list-style-position:inside}@media only screen and (max-width: 760px){.fieldlist_formfields .fieldlist-header,.fieldlist_formfields .fieldlist-row{flex-direction:column}.fieldlist_formfields .fieldlist-header-cell{display:none}.fieldlist_formfields .fieldlist-row{padding:.5em 0;border-bottom:2px solid var(--color-border-table-cell, #cbcbcb)}.fieldlist_formfields .fieldlist-cell{padding:.25em .5em}.fieldlist_formfields .fieldlist-cell.fieldlist-actions{flex:1;justify-content:flex-start;padding-top:.5em}.fieldlist_formfields .fieldlist-cell:not(:last-child){margin-bottom:.5em}.fieldlist_formfields .fieldlist-cell::before{content:attr(data-label);font-weight:bold;margin-bottom:.25em}}.fieldlist_formfields .addRuleRow,.fieldlist_formfields .removeRuleRow,.fieldlist_formfields .verifyRuleRow{cursor:pointer;border:none;padding:4px 8px;border-radius:3px;font-weight:bold;background-color:#aaa;color:var(--color-foreground-text, #fff)}.fieldlist_formfields .addRuleRow:hover,.fieldlist_formfields .removeRuleRow:hover,.fieldlist_formfields .verifyRuleRow:hover{background-color:#999}body.checking-now #checking-now-fixed-tab{display:block !important}#checking-now-fixed-tab{background:#ccc;border-radius:5px;bottom:0;color:var(--color-text);display:none;font-size:.8rem;left:0;padding:5px;position:fixed}#selector-wrapper{height:100%;text-align:center;max-height:70vh;overflow-y:scroll;position:relative}#selector-wrapper>img{position:absolute;z-index:4;max-width:100%}#selector-wrapper>canvas{position:relative;z-index:5;max-width:100%}#selector-wrapper>canvas:hover{cursor:pointer}#selector-current-xpath{font-size:80%}@media(min-width: 901px){body.blueprint-add_watch_ui #add-watch-ui{width:80%}}#add-watch-ui{padding:0}#add-watch-ui #add-watch-url-row{display:flex;gap:.5rem;align-items:stretch;margin-bottom:1rem}#add-watch-ui #add-watch-url-row>span{flex:1 1 auto;min-width:0}#add-watch-ui #add-watch-url-row>span input{width:100%}#add-watch-ui #add-watch-url-row #add-watch-go{flex:0 0 auto;white-space:nowrap}#add-watch-ui #add-watch-panes{display:flex;gap:.55rem;align-items:stretch}@media(max-width: 900px){#add-watch-ui #add-watch-panes{flex-direction:column}}#add-watch-ui #add-watch-selector-pane{flex:1 1 62%;min-width:0;min-height:380px;position:relative;display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--color-background-tab);border-radius:6px;background:rgba(0,0,0,.15);padding:.75rem}#add-watch-ui #add-watch-selector-pane #add-watch-empty-state,#add-watch-ui #add-watch-selector-pane #add-watch-spinner,#add-watch-ui #add-watch-selector-pane #add-watch-error{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.6rem;text-align:center;padding:2rem 1rem}#add-watch-ui #add-watch-selector-pane #add-watch-empty-state{opacity:.8}#add-watch-ui #add-watch-selector-pane #add-watch-empty-state svg{opacity:.55}#add-watch-ui #add-watch-selector-pane #add-watch-empty-state strong{font-size:1.05rem}#add-watch-ui #add-watch-selector-pane #add-watch-empty-state span{font-size:.85rem;opacity:.8;max-width:32ch}#add-watch-ui #add-watch-selector-pane #add-watch-spinner{gap:1.2rem}#add-watch-ui #add-watch-selector-pane #add-watch-spinner .spinner{font-size:5px}#add-watch-ui #add-watch-selector-pane #add-watch-spinner .fetching-update-notice{font-size:.85rem;opacity:.85}#add-watch-ui #add-watch-selector-pane #add-watch-error{color:#ffb4b4;font-size:.9rem;word-break:break-word}#add-watch-ui #add-watch-selector-pane #selector-wrapper{position:relative;display:block;width:100%;flex:1 1 auto;min-height:0;max-height:none;overflow-y:auto;overflow-x:hidden;text-align:left}#add-watch-ui #add-watch-selector-pane #selector-wrapper>img{position:relative;display:block;max-width:100%;height:auto;z-index:4}#add-watch-ui #add-watch-selector-pane #selector-wrapper>canvas{position:absolute;top:0;left:0;max-width:none;z-index:5}#add-watch-ui #add-watch-options-pane{flex:0 0 34%;min-width:0;display:flex;flex-direction:column;gap:1.1rem}@media(max-width: 900px){#add-watch-ui #add-watch-options-pane{flex:1 1 auto}}#add-watch-ui #add-watch-options-pane .add-watch-option-group label{display:inline-block}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend ul{margin:.35rem 0 0 0;padding:0;list-style:none}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend li{display:flex;align-items:flex-start;gap:.5em;padding:.15rem 0}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend li input[type=radio]{flex:0 0 auto;margin-top:.2em}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend li label{display:block;min-width:0;overflow-wrap:anywhere;font-size:.85rem;line-height:1.35}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend li.unusable{opacity:.55;cursor:not-allowed}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend li.unusable label{cursor:not-allowed}#add-watch-ui #add-watch-options-pane #quick-watch-fetch-backend .pure-form-message-inline{display:block;margin-top:.35rem;font-size:.8rem;opacity:.8}#add-watch-ui #add-watch-options-pane #by-element-toggle-group .pure-form-message-inline{display:block;margin-top:.25rem;font-size:.8rem;opacity:.8}#add-watch-ui #add-watch-options-pane #by-element-toggle-group #clear-selector{margin-top:.5rem}#add-watch-ui #add-watch-options-pane #quick-watch-llm-intent label{display:block;margin-bottom:.35rem}#add-watch-ui #add-watch-options-pane #add-watch-submit-row{display:flex;flex-wrap:wrap;gap:.5rem}body.blueprint-add_watch_ui #add-watch-ui{height:90vh;display:flex;flex-direction:column}body.blueprint-add_watch_ui #add-watch-fieldset{flex:1 1 auto;min-height:0;min-width:0;display:flex;flex-direction:column;border:0;margin:0;padding:0}body.blueprint-add_watch_ui #add-watch-legend{margin:0 0 .75rem;font-size:1.1rem;font-weight:600}body.blueprint-add_watch_ui #new-watch-form{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}@media(min-width: 901px){body.blueprint-add_watch_ui #add-watch-panes{flex:1 1 auto;min-height:0}body.blueprint-add_watch_ui #add-watch-selector-pane{min-height:0}}@media(max-width: 900px){body.blueprint-add_watch_ui #add-watch-ui{height:auto}body.blueprint-add_watch_ui #add-watch-panes{flex:0 0 auto}}.ternary-radio-group{display:flex;gap:0;border:1px solid var(--color-grey-750);border-radius:4px;overflow:hidden;width:fit-content;background:var(--color-background)}.ternary-radio-group .ternary-radio-option{position:relative;cursor:pointer;margin:0;display:flex;align-items:center}.ternary-radio-group .ternary-radio-option input[type=radio]{position:absolute;opacity:0;width:0;height:0}.ternary-radio-group .ternary-radio-option .ternary-radio-label{padding:8px 16px;background:var(--color-grey-900);border:none;border-right:1px solid var(--color-grey-750);font-size:13px;font-weight:500;color:var(--color-text);transition:all .2s ease;cursor:pointer;display:block;text-align:center}.ternary-radio-group .ternary-radio-option:last-child .ternary-radio-label{border-right:none}.ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label{background:var(--color-link);color:var(--color-text-button);font-weight:600}.ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label.ternary-default{background:var(--color-grey-600);color:var(--color-text-button)}.ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label:hover{background:#1a7bc4}.ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label:hover.ternary-default{background:var(--color-grey-500)}.ternary-radio-group .ternary-radio-option:hover .ternary-radio-label{background:var(--color-grey-800)}@media(max-width: 480px){.ternary-radio-group{width:100%}.ternary-radio-group .ternary-radio-label{flex:1;min-width:auto}}input[type=radio].pure-radio:checked+label,input[type=radio].pure-radio:checked{background:var(--color-link);color:var(--color-text-button)}html[data-darkmode=true] .ternary-radio-group .ternary-radio-option .ternary-radio-label{background:var(--color-grey-350)}html[data-darkmode=true] .ternary-radio-group .ternary-radio-option:hover .ternary-radio-label{background:var(--color-grey-400)}html[data-darkmode=true] .ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label{background:var(--color-link);color:var(--color-text-button)}html[data-darkmode=true] .ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label.ternary-default{background:var(--color-grey-600)}html[data-darkmode=true] .ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label:hover{background:#1a7bc4}html[data-darkmode=true] .ternary-radio-group .ternary-radio-option input:checked+.ternary-radio-label:hover.ternary-default{background:var(--color-grey-500)}body.processor-image_ssim_diff #edit-text-filter .text-filtering{display:none}body.processor-image_ssim_diff #conditions-tab{display:none}.modal-dialog{border:none;border-radius:10px;padding:0;background:var(--color-background);color:var(--color-text);box-shadow:0 5px 20px rgba(0,0,0,.3);max-width:500px;width:90%}.modal-dialog::backdrop{background:rgba(0,0,0,.6);backdrop-filter:blur(3px);animation:fadeIn .2s ease-out}.modal-dialog[open]{animation:slideIn .25s ease-out}.modal-dialog .modal-header{padding:1.5rem;border-bottom:1px solid var(--color-border-table-cell);display:flex;align-items:center;gap:1rem}.modal-dialog .modal-header .modal-icon{font-size:2rem;line-height:1;flex-shrink:0}.modal-dialog .modal-header .modal-icon.warning{color:var(--color-warning)}.modal-dialog .modal-header .modal-icon.danger{color:var(--color-background-button-error)}.modal-dialog .modal-header .modal-icon.info{color:var(--color-background-button-primary)}.modal-dialog .modal-header .modal-title{font-size:1.3rem;font-weight:bold;margin:0;color:var(--color-text)}.modal-dialog .modal-body{padding:1.5rem;line-height:1.6}.modal-dialog .modal-body p{margin:0 0 1rem 0}.modal-dialog .modal-body p:last-child{margin-bottom:0}.modal-dialog .modal-body strong{color:var(--color-text);font-weight:600}.modal-dialog .modal-footer{padding:1rem 1.5rem;border-top:1px solid var(--color-border-table-cell);display:flex;gap:.75rem;justify-content:flex-end;background:var(--color-grey-900)}.modal-dialog .modal-footer button{padding:.6rem 1.5rem;border:none;border-radius:4px;cursor:pointer;font-weight:500;transition:all .2s ease;font-size:.95rem}.modal-dialog .modal-footer button:hover{transform:translateY(-1px);box-shadow:0 2px 8px rgba(0,0,0,.15)}.modal-dialog .modal-footer button:active{transform:translateY(0)}.modal-dialog .modal-footer button.modal-btn-cancel{background:var(--color-background-button-cancel);color:var(--color-grey-200)}.modal-dialog .modal-footer button.modal-btn-cancel:hover{background:var(--color-grey-700)}.modal-dialog .modal-footer button.modal-btn-confirm{background:var(--color-background-button-primary);color:var(--color-white)}.modal-dialog .modal-footer button.modal-btn-confirm:hover{opacity:.9}.modal-dialog .modal-footer button.modal-btn-danger{background:var(--color-background-button-error);color:var(--color-white)}.modal-dialog .modal-footer button.modal-btn-danger:hover{background:var(--color-dark-red)}.modal-dialog .modal-footer button.modal-btn-warning{background:var(--color-background-button-warning);color:var(--color-white)}.modal-dialog .modal-footer button.modal-btn-warning:hover{opacity:.9}html[data-darkmode=true] .modal-dialog{box-shadow:0 5px 30px rgba(0,0,0,.7)}html[data-darkmode=true] .modal-dialog .modal-footer{background:var(--color-grey-200)}@keyframes fadeIn{from{opacity:0}to{opacity:1}}@keyframes slideIn{from{opacity:0;transform:translateY(-20px) scale(0.95)}to{opacity:1;transform:translateY(0) scale(1)}}@media only screen and (max-width: 760px){.modal-dialog{width:95%;max-width:none}.modal-dialog .modal-header{padding:1rem}.modal-dialog .modal-header .modal-title{font-size:1.1rem}.modal-dialog .modal-body{padding:1rem;font-size:.95rem}.modal-dialog .modal-footer{padding:.75rem 1rem;flex-wrap:wrap}.modal-dialog .modal-footer button{flex:1;min-width:120px}}.bulk-choice-list{display:flex;flex-direction:column;gap:2px;max-height:50vh;overflow-y:auto;text-align:left}.bulk-choice-list .bulk-choice-row{display:block;padding:6px 8px;cursor:pointer;border-radius:4px}.bulk-choice-list .bulk-choice-row input[type=radio]{margin-right:8px}.bulk-choice-list .bulk-choice-row:hover{background:rgba(127,127,127,.15)}.bulk-choice-list .bulk-choice-row em{opacity:.7;font-size:.9em}#language-selector-flag{display:inline-block;width:1.2em;height:1.2em;vertical-align:middle;border-radius:50%;overflow:hidden;opacity:.6}#language-selector-flag:hover{opacity:1}.language-list{display:flex;flex-direction:column;gap:.5rem;padding:.5rem 0}.language-option{display:flex;align-items:center;gap:1rem;padding:.25rem;border-radius:4px;transition:background-color .2s ease;text-decoration:none;color:var(--color-text);border:1px solid rgba(0,0,0,0)}.language-option:hover{background-color:var(--color-background-menu-link-hover);border-color:var(--color-border-table-cell)}.language-option.active{background-color:var(--color-link);color:var(--color-text-button);font-weight:600}.language-option .flag{font-size:1.5rem;flex-shrink:0}.language-option .language-name{flex-grow:1;font-size:1rem}#language-modal .language-list .lang-option{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;margin-right:.5em;border-radius:50%;overflow:hidden}.action-sidebar{display:flex;flex-direction:column;align-items:center;align-self:flex-start;position:sticky;top:0;height:100vh;background:hsla(0,0%,100%,.05);z-index:60;pointer-events:none}@media only screen and (max-width: 980px){.action-sidebar{display:none}}.action-sidebar-inner{pointer-events:auto;width:64px;overflow:hidden;flex:1 1 auto;display:flex;flex-direction:column;transition:width .08s ease-out;padding-left:.55rem;padding-right:.55rem}body.actionsidebar-minimal .action-sidebar-inner:hover,body.actionsidebar-minimal .action-sidebar-inner:focus-within{width:200px;transition:width .22s cubic-bezier(0.2, 0.7, 0.2, 1)}body.actionside-bar-on .action-sidebar-inner{width:200px;transition:none}ul.action-sidebar-list{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:0}.action-sidebar-list.action-sidebar-list--bottom{margin-top:auto}.action-sidebar-li{list-style:none;margin:0;position:relative;color:var(--color-white)}.action-sidebar-li:nth-child(2){padding-top:2rem}.action-sidebar-li button{padding:0;margin:0}.action-sidebar-li a{color:var(--color-white)}.action-sidebar-li--spark .queue-spark{display:block;width:100%;height:15px;box-sizing:border-box;padding:0;margin:0;border-radius:3px;background:hsla(0,0%,100%,.05);box-shadow:inset 0 0 0 1px hsla(0,0%,100%,.06)}.action-sidebar-divider{height:1px;background:hsla(0,0%,100%,.18);margin:6px 16px;list-style:none}.action-sidebar .action-sidebar-item{position:relative;display:flex;align-items:center;padding:.55rem;font-size:var(--body-main-text-size);border-radius:var(--common-round-border);color:var(--color-white);text-decoration:none;white-space:nowrap;background:rgba(0,0,0,0);border:0;text-align:left;cursor:pointer;transition:background-color .12s ease,color .12s ease}.action-sidebar .action-sidebar-item:hover{background-color:var(--color-sidebar-item-hover-bg)}.action-sidebar .action-sidebar-item:hover .action-icon{stroke-width:2.3}.action-sidebar .action-sidebar-item:focus-visible{outline:2px solid var(--color-link);outline-offset:2px}.action-sidebar .action-sidebar-item.active .action-icon{stroke-width:2.4;filter:drop-shadow(0 0 0.4px currentColor)}.action-sidebar .action-sidebar-item.active .action-label{font-weight:700}.action-sidebar .action-sidebar-item.is-disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.action-sidebar .action-sidebar-item.action-sidebar-item--accent .action-icon{stroke:#fff;stroke-width:2.6}.action-sidebar .action-sidebar-item .action-label{flex:0 0 auto;margin-left:14px;font-family:inherit;font-weight:400;letter-spacing:0;text-transform:none;color:inherit;opacity:0;transform:translateX(-4px);transition:opacity .05s ease-out,transform .05s ease-out}.action-sidebar .action-sidebar-item .action-badge{margin-left:10px;font-size:.7rem;line-height:1;padding:3px 7px;border-radius:999px;background:hsla(0,0%,100%,.14);color:hsla(0,0%,100%,.85);text-transform:uppercase;letter-spacing:.06em;font-weight:700;pointer-events:none;opacity:0;transition:opacity .05s ease-out}.action-sidebar .action-sidebar-item .action-badge.action-badge--count{margin-left:auto;text-transform:none;letter-spacing:0;background:#3e95bb;color:var(--color-white);font-variant-numeric:tabular-nums}body.actionsidebar-minimal .action-sidebar-inner:hover .action-sidebar-item .action-label,body.actionsidebar-minimal .action-sidebar-inner:focus-within .action-sidebar-item .action-label{opacity:1;transform:translateX(0);transition:opacity .18s ease .05s,transform .18s cubic-bezier(0.2, 0.7, 0.2, 1) .05s}body.actionsidebar-minimal .action-sidebar-inner:hover .action-sidebar-item .action-badge,body.actionsidebar-minimal .action-sidebar-inner:focus-within .action-sidebar-item .action-badge{opacity:1;transition:opacity .18s ease .05s}body.actionside-bar-on .action-sidebar .action-sidebar-item .action-label{opacity:1;transform:translateX(0);transition:none}body.actionside-bar-on .action-sidebar .action-sidebar-item .action-badge{opacity:1;transition:none}.action-icon{flex:0 0 auto;width:24px;height:24px;stroke:currentColor;stroke-width:1.9;fill:none;stroke-linecap:round;stroke-linejoin:round}.action-badge{flex:0 0 auto;font-size:.62rem;text-transform:uppercase;letter-spacing:.08em;padding:2px 6px;border-radius:999px;background:hsla(0,0%,100%,.15);color:hsla(0,0%,100%,.85);font-weight:700}.mobile-menu-section{padding:.5rem .75rem;border-bottom:1px solid var(--color-border-table-cell)}.mobile-menu-section ul.action-sidebar-list{gap:1px}.mobile-menu-section ul.action-sidebar-list .action-sidebar-li a,.mobile-menu-section ul.action-sidebar-list .action-sidebar-li button{padding:.55rem}.mobile-menu-section .action-sidebar-item{position:relative;display:flex;align-items:center;justify-content:flex-start;gap:.55rem;padding:.55rem .55rem;border-radius:var(--common-round-border);color:var(--color-text)}.mobile-menu-section .action-sidebar-item:hover{background-color:var(--color-background-menu-link-hover);color:var(--color-text)}.mobile-menu-section .action-sidebar-item.active{background-color:var(--color-background-menu-link-hover);color:var(--color-text)}.mobile-menu-section .action-sidebar-item .action-label{position:static;transform:none;background:rgba(0,0,0,0);box-shadow:none;color:inherit;opacity:1;pointer-events:auto;padding:0;font-weight:500}.mobile-menu-section .action-sidebar-item .action-label::before{display:none}.mobile-menu-section .action-sidebar-item .action-badge{position:static;margin-left:auto;font-size:.62rem;background:rgba(0,0,0,.08);color:var(--color-text)}.mobile-menu-section .action-sidebar-item.action-sidebar-item--accent{background:var(--color-background-menu-link-hover);box-shadow:inset 0 0 0 1px var(--color-border-table-cell);color:var(--color-text)}.mobile-menu-section .action-sidebar-item.action-sidebar-item--accent .action-label{color:inherit}.mobile-menu-section .action-sidebar-item--button{background:rgba(0,0,0,0);border:none;cursor:pointer;text-align:left;font:inherit}#add-watch-live-info{width:100%;margin-top:1rem}#add-watch-live-info .add-watch-live-placeholder{border:1px dashed hsla(0,0%,100%,.25);background:hsla(0,0%,100%,.04);border-radius:10px;padding:1.25rem;color:var(--color-white)}#add-watch-live-info .add-watch-live-placeholder h3{margin:0 0 .4rem 0;font-size:1rem;letter-spacing:.02em}#add-watch-live-info .add-watch-live-placeholder .muted{opacity:.7;margin:0 0 .75rem 0;font-size:.85rem}#add-watch-live-info .add-watch-live-placeholder .add-watch-live-stream{font-size:.85rem;opacity:.6;padding:.6rem 0}.mobile-menu-drawer .action-sidebar-list{padding:0}.mobile-menu-drawer .mobile-menu-section .action-sidebar-item{padding-left:0}#action-sidebar-logo{padding-top:1.1rem;padding-left:.55rem}.actionsidebar-minimal #checking-now-stats-sidebar{display:none}.actionsidebar-minimal #cdio-logo #logo-expanded{display:none}.actionsidebar-minimal.action-side-bar-expanded #checking-now-stats-sidebar{display:block}.actionsidebar-minimal.action-side-bar-expanded #cdio-logo #logo-expanded{display:inline-block}.action-side-bar-expanded #cdio-logo #logo-short{display:none}#queue-page{width:100%;color:var(--color-white)}#queue-page h2,#queue-page h3{color:var(--color-white)}#queue-page .queue-panel{background:var(--color-background-new-watch-form);padding:1em;border-radius:10px;margin-bottom:1em;width:100%;box-sizing:border-box;color:var(--color-white)}#queue-page .queue-stats{display:grid;grid-template-columns:repeat(auto-fit, minmax(160px, 1fr));gap:.75rem}#queue-page .queue-stat .label{font-size:.7rem;text-transform:uppercase;letter-spacing:.06em;opacity:.7}#queue-page .queue-stat .value{font-size:1.6rem;font-weight:700;color:var(--color-white)}#queue-page .queue-stat.queue-stat--action{display:flex;align-items:center;justify-content:flex-start}#queue-page .queue-stat.queue-stat--action .pure-button{white-space:nowrap}#queue-page table.pure-table{width:100%;background:rgba(0,0,0,0);color:var(--color-white);font-size:80%}#queue-page table.pure-table thead th{background:rgba(0,0,0,0);color:var(--color-white);border-bottom:1px solid hsla(0,0%,100%,.18);font-weight:700;white-space:nowrap}#queue-page table.pure-table td{color:var(--color-white);border-color:hsla(0,0%,100%,.08);white-space:nowrap}#queue-page table.pure-table td.title-col,#queue-page table.pure-table td.watch-cell{white-space:normal;word-break:break-all}#queue-page table.pure-table td.time-cell{font-variant-numeric:tabular-nums;color:hsla(0,0%,100%,.75);font-size:.95em}#queue-page table.pure-table code,#queue-page table.pure-table small,#queue-page table.pure-table em,#queue-page table.pure-table strong{color:var(--color-white)}#queue-page table.pure-table code{background:rgba(0,0,0,.18)}#queue-page table.pure-table small{opacity:.7}#queue-page table.pure-table-striped tr:nth-child(2n-1) td{background:hsla(0,0%,100%,.04)}#queue-page tr.is-completed td{opacity:.45;transition:opacity .4s ease}#queue-page tbody[data-section=workers]{border-bottom:1px solid hsla(0,0%,100%,.18)}#queue-page tr.worker-slot td{border-color:hsla(0,0%,100%,.05)}#queue-page tr.worker-idle td{background:hsla(0,0%,100%,.02)}#queue-page .inline-tag,#queue-page .processor-badge,#queue-page .watch-tag-list,#queue-page .tracking-ldjson-price-data,#queue-page .restock-label{background:hsla(0,0%,100%,.14);color:var(--color-white)}#queue-page .inline-tag--running{background:rgba(28,184,65,.45)}#queue-page .inline-tag--idle{background:hsla(0,0%,100%,.08);color:hsla(0,0%,100%,.6)}#queue-page .inline-tag--done{background:hsla(0,0%,100%,.1);color:hsla(0,0%,100%,.7)}#queue-page a.queue-cancel{display:inline-block;margin-left:8px;font-size:.75rem;color:hsla(0,0%,100%,.65);text-decoration:underline;text-decoration-style:dotted;text-underline-offset:2px}#queue-page a.queue-cancel:hover{color:var(--color-white);text-decoration-style:solid}#queue-page a.queue-cancel.is-busy{pointer-events:none;opacity:.5}#queue-page tr.is-new td{animation:queue-row-in .45s ease}@keyframes queue-row-in{from{background-color:rgba(28,184,65,.18)}to{background-color:rgba(0,0,0,0)}}#queue-page .queue-waiting{display:none;align-items:center;gap:.5rem;margin-top:1rem;padding:.5rem 0;color:hsla(0,0%,100%,.7);font-size:.85rem}#queue-page .queue-waiting[data-show=true]{display:flex}#queue-page .queue-waiting .spinner{margin:0;flex:0 0 auto;border-top-color:hsla(0,0%,100%,.18);border-right-color:hsla(0,0%,100%,.18);border-bottom-color:hsla(0,0%,100%,.18);border-left-color:var(--color-white)}.hamburger-menu{display:none;background:rgba(0,0,0,0);border:none;cursor:pointer;padding:.55rem;z-index:10001;position:relative}@media only screen and (max-width: 980px){.hamburger-menu{display:flex;flex-direction:column;justify-content:center;align-items:center}}.hamburger-icon{width:24px;height:20px;position:relative;display:flex;flex-direction:column;justify-content:space-between}.hamburger-icon span{display:block;height:3px;width:100%;background:var(--color-white);border-radius:2px;transition:all .3s cubic-bezier(0.68, -0.55, 0.265, 1.55);transform-origin:center}.hamburger-menu.active .hamburger-icon span{background-color:var(--color-text)}.hamburger-menu.active .hamburger-icon span:nth-child(1){transform:translateY(8.5px) rotate(45deg)}.hamburger-menu.active .hamburger-icon span:nth-child(2){opacity:0;transform:translateX(-10px)}.hamburger-menu.active .hamburger-icon span:nth-child(3){transform:translateY(-8.5px) rotate(-45deg)}.mobile-menu-overlay{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.5);z-index:9999;opacity:0;transition:opacity .3s ease}.mobile-menu-overlay.active{display:block;opacity:1}.mobile-menu-drawer{position:fixed;top:0;right:-280px;width:280px;height:100%;background:var(--color-background);opacity:1;box-shadow:-2px 0 8px rgba(0,0,0,.15);z-index:10000;transition:right .3s cubic-bezier(0.68, -0.55, 0.265, 1.55);overflow-y:auto;padding-top:60px}.mobile-menu-drawer #cdio-logo{color:var(--color-text)}.mobile-menu-drawer #cdio-logo #logo-short{display:none}.mobile-menu-drawer #cdio-logo #logo-expanded{display:inline-block}.mobile-menu-drawer .action-icon{stroke:var(--color-text)}.mobile-menu-drawer.active{right:0}.mobile-menu-drawer .mobile-menu-items{list-style:none;padding:1rem 0;margin:0}.mobile-menu-drawer .mobile-menu-items li{border-bottom:1px solid var(--color-border-table-cell)}.mobile-menu-drawer .mobile-menu-items li>*{display:block;padding:1rem 1.5rem;color:var(--color-text);text-decoration:none;font-weight:500;transition:background .2s ease}.mobile-menu-drawer .mobile-menu-items li>*:hover{background:var(--color-background-menu-link-hover)}.mobile-menu-drawer .mobile-menu-items li#menu-pause,.mobile-menu-drawer .mobile-menu-items li#menu-mute{display:none}.logo-cdio{font-weight:bold;font-size:1.1rem}.logo-cdio .logo-cd{color:var(--color-grey-500)}.logo-cdio .logo-io{color:var(--color-text)}.menu-always-visible{display:flex;align-items:center;gap:.5rem;margin-left:auto}@media only screen and (max-width: 980px){#top-right-menu .menu-collapsible{display:none !important}.pure-menu-horizontal{overflow-x:visible !important}#nav-menu{overflow-x:visible !important}}@media only screen and (min-width: 1025px){.hamburger-menu,.mobile-menu-drawer,.mobile-menu-overlay{display:none !important}}html[data-darkmode=true] .mobile-menu-drawer{box-shadow:-2px 0 8px rgba(0,0,0,.4)}#search-modal .modal-body{padding:2rem 1.5rem}#search-modal .modal-body .pure-control-group{padding-bottom:0}#search-modal .modal-body .pure-control-group label{display:block;margin-bottom:.5rem;font-size:.9rem;font-weight:600;color:var(--color-text)}#search-modal .modal-body .pure-control-group #search-modal-input{width:100%;max-width:100%;box-sizing:border-box;padding:.6rem .8rem;font-size:1rem;border:1px solid var(--color-border-input);border-radius:4px;background-color:var(--color-background-input);color:var(--color-text-input);box-shadow:inset 0 1px 3px var(--color-shadow-input);transition:border-color .2s ease,box-shadow .2s ease}#search-modal .modal-body .pure-control-group #search-modal-input:focus{outline:none;border-color:var(--color-link);box-shadow:0 0 0 3px rgba(27,152,248,.1)}#search-modal .modal-body .pure-control-group #search-modal-input::placeholder{color:var(--color-text-input-placeholder);opacity:.7}html[data-darkmode=true] #search-modal #search-modal-input:focus{box-shadow:0 0 0 3px rgba(89,189,251,.15)}#llm-diff-summary-area{margin:.6rem 0 .4rem;padding:.65rem .9rem;background:linear-gradient(135deg, rgba(120, 80, 200, 0.18), rgba(80, 160, 220, 0.14));border-left:3px solid rgba(140,90,220,.8);border-radius:0 4px 4px 0;min-width:0;max-width:100%;box-sizing:border-box;overflow:hidden}#llm-diff-summary-area .llm-diff-summary-label{display:block;font-size:.7rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;opacity:.55;margin-bottom:.25rem}#llm-diff-summary-area .llm-diff-summary-text{margin:0;font-size:.9rem;line-height:1.5;white-space:pre-wrap;overflow-wrap:break-word;word-break:break-word}.llm-diff-summary-prompt{margin:.4em 0 0;font-size:.78rem;font-style:italic;overflow:hidden;max-height:3.8em;animation:llm-prompt-reveal .7s ease-out both}.llm-diff-summary-prompt .llm-diff-summary-prompt-text{display:block;opacity:.55;mask-image:linear-gradient(to bottom, rgba(0, 0, 0, 0.85) 30%, rgba(0, 0, 0, 0) 100%);-webkit-mask-image:linear-gradient(to bottom, rgba(0, 0, 0, 0.85) 30%, rgba(0, 0, 0, 0) 100%);white-space:pre-wrap;overflow-wrap:break-word;line-height:1.45}@keyframes llm-prompt-reveal{from{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:translateY(0)}}.llm-diff-summary-loading{opacity:.5;font-style:italic;animation:llm-pulse 1.4s ease-in-out infinite;font-weight:bold}@keyframes llm-pulse{0%,100%{opacity:.5}50%{opacity:.2}}.llm-budget-exceeded,.llm-error{color:#c0392b;font-weight:600;font-style:normal;opacity:1}.toggle-ai-mode{opacity:.4;transition:opacity .2s ease,filter .2s ease;display:inline-flex;align-items:center;color:var(--color-text-menu-link)}.toggle-ai-mode svg{height:1.2rem;width:1.2rem}.toggle-ai-mode .ai-mode-label{font-size:.75rem;font-weight:600;letter-spacing:.04em;line-height:1}html[data-ai-mode=true] .toggle-ai-mode{opacity:1;filter:drop-shadow(0 0 4px rgba(160, 100, 255, 0.7))}.btn-label-summary{display:none}html[data-ai-mode=true] body.llm-configured .btn-label-history{display:none}html[data-ai-mode=true] body.llm-configured .btn-label-summary{display:inline}.ai-inline-summary-row td{white-space:normal !important;word-break:break-word;padding:.5rem 1rem .6rem 1.4rem !important;background:linear-gradient(135deg, #f0ebff, #eaf0ff) !important;border-top:1px solid #c4b5fd !important;border-left:3px solid #8b5cf6 !important;color:#1a0640 !important;line-height:1.5}html[data-darkmode=true] .ai-inline-summary-row td{background:linear-gradient(135deg, #1c0d35, #0d1535) !important;border-top:1px solid #3b1f6e !important;border-left-color:#8b5cf6 !important;color:#e9d5ff !important}.ai-inline-summary-row .ai-inline-summary-content{display:flex;gap:.5rem;align-items:flex-start}.ai-inline-summary-row .ai-inline-summary-content .ai-inline-spinner{flex-shrink:0;animation:llm-pulse 1.4s ease-in-out infinite}.ai-inline-summary-row .ai-inline-summary-content .ai-inline-body{display:flex;flex-direction:column;min-width:0}.ai-inline-summary-row .ai-inline-summary-content .ai-inline-text{font-style:italic;opacity:.75;white-space:pre-wrap}.ai-inline-summary-row .ai-inline-summary-content.loaded .ai-inline-spinner{animation:none}.ai-inline-summary-row .ai-inline-summary-content.loaded .ai-inline-text{font-style:normal;opacity:1}.ai-inline-summary-row .ai-inline-history-link{display:inline-block;margin-top:.4rem;font-size:.78rem;font-weight:700;opacity:.7;white-space:nowrap}.ai-inline-summary-row .ai-inline-history-link:hover{opacity:1}.ai-inline-summary-row .ai-inline-error{color:#c0392b}.ai-inline-summary-row .ai-inline-prompt{display:block;margin-top:.3em;font-size:.75rem;font-style:italic;overflow:hidden;max-height:3.6em;animation:llm-prompt-reveal .6s ease-out both;mask-image:linear-gradient(to bottom, rgba(0, 0, 0, 0.7) 30%, rgba(0, 0, 0, 0) 100%);-webkit-mask-image:linear-gradient(to bottom, rgba(0, 0, 0, 0.7) 30%, rgba(0, 0, 0, 0) 100%);opacity:.55;line-height:1.4;white-space:pre-wrap;overflow-wrap:break-word}.action-sidebar-item{position:relative}.action-sidebar-item .notification-bubble{position:absolute;top:8px;left:8px;min-width:18px;height:18px;background:#f44;color:#fff;font-size:10px;font-weight:700;line-height:18px;text-align:center;border-radius:9px;padding:0 2px;box-shadow:0 2px 4px rgba(0,0,0,.3);pointer-events:none;transition:all .2s ease;display:none}.action-sidebar-item .notification-bubble.red-bubble{background:#f44}.action-sidebar-item .notification-bubble.blue-bubble{background:#4a9eff;color:#fff}.action-sidebar-item .notification-bubble.visible{display:block}.action-sidebar-item .notification-bubble.pulse{animation:bubblePulse .4s ease-out}.action-sidebar-item .notification-bubble.large-number{font-size:8px;min-width:20px;height:20px;line-height:20px;border-radius:10px}@keyframes bubblePulse{0%{transform:scale(1)}50%{transform:scale(1.3)}100%{transform:scale(1)}}html[data-darkmode=true] .notification-bubble{box-shadow:0 2px 6px rgba(0,0,0,.6)}.notification-add-buttons{margin-bottom:.5rem;display:flex;align-items:center;flex-wrap:wrap;gap:.4rem}.notification-add-buttons .add-destination-inline{display:inline-flex;align-items:center;gap:.3rem}.notification-add-buttons .add-destination-inline input[type=email]{margin:0;min-width:16rem}#notification-add-email-preset{padding-top:.55rem;padding-bottom:.55rem}#notification-recipients{padding-bottom:.55rem}.notification-recipients{display:flex;flex-wrap:wrap;gap:.4rem;margin-bottom:.5rem}.notification-recipients .notification-chip{display:inline-flex;align-items:center;gap:.35rem;padding:.2rem .5rem;line-height:1.4;border:1px solid var(--color-border-notification);border-radius:var(--common-round-border);max-width:100%}.notification-recipients .notification-chip .notification-chip-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:22rem}.notification-recipients .notification-chip .notification-chip-remove{cursor:pointer;font-weight:bold;opacity:.55;text-decoration:none}.notification-recipients .notification-chip .notification-chip-remove:hover{opacity:1}.notifications-wrapper{padding-top:.5rem}.notifications-wrapper #notification-test-log{margin-top:1rem;padding:1rem;white-space:pre-wrap;word-break:break-word;overflow-wrap:break-word;max-width:100%;box-sizing:border-box;max-height:12rem;overflow-y:scroll;border:1px solid var(--color-border-notification);border-radius:5px}#notification-error-log{border:1px solid var(--color-border-notification);padding:1rem;border-radius:5px;overflow-wrap:break-word}#restock-diff{width:100%;box-sizing:border-box}#restock-diff-summary{margin-top:.6rem;display:flex;align-items:center;gap:.6rem;flex-wrap:wrap}.restock-latest-price{font-size:1.4rem;font-weight:700}.restock-badge{display:inline-block;padding:.15rem .55rem;border-radius:1rem;font-size:.8rem;font-weight:600;white-space:nowrap}.restock-badge.in-stock{background:rgba(31,164,99,.15);color:#1fa463}.restock-badge.out-of-stock{background:rgba(231,76,60,.15);color:#e74c3c}#restock-tabs{background:var(--color-background);color:var(--color-text);padding:1rem;border-radius:5px}#restock-tabs .tab-pane-inner#screenshot{text-align:center}#restock-tabs .tab-pane-inner#screenshot img{max-width:99%}#restock-graph{margin-top:1rem;box-sizing:border-box}@media(min-width: 1200px){#restock-graph{max-width:80%;margin-left:auto;margin-right:auto}}.js-restock-graph{position:relative;width:100%}.js-restock-graph svg{display:block;max-width:100%}.js-restock-graph .rg-axis{stroke:var(--color-border-notification, rgba(127, 127, 127, 0.4));stroke-width:1}.js-restock-graph .rg-label{fill:currentColor;opacity:.7;font-size:12px}.js-restock-graph .rg-line{stroke:currentColor;opacity:.55}.js-restock-graph .rg-dot{stroke:var(--color-background, #fff);stroke-width:1.5}.js-restock-graph .rg-legend{display:flex;justify-content:center;gap:1.1rem;margin-top:.4rem;font-size:.78rem;opacity:.85}.js-restock-graph .rg-legend .rg-legend-item{display:inline-flex;align-items:center;gap:.35rem}.js-restock-graph .rg-legend .rg-legend-dot{width:9px;height:9px;border-radius:50%;display:inline-block}.js-restock-graph .rg-legend .rg-legend-dot.in{background:#1fa463}.js-restock-graph .rg-legend .rg-legend-dot.out{background:#e74c3c}.js-restock-graph .rg-header{display:flex;align-items:center;justify-content:space-between;gap:.5rem;flex-wrap:wrap;margin-bottom:.5rem}.js-restock-graph .rg-stats{font-size:.8rem;opacity:.7;text-align:right;margin-left:auto}.js-restock-graph .rg-band{fill:rgba(120,130,150,.14)}.js-restock-graph .rg-avg-line{stroke:var(--color-text, #555);opacity:.45;stroke-width:1}.js-restock-graph .rg-avg-text{opacity:.5}.rg-status{display:inline-flex;align-items:baseline;gap:.4rem;padding:.2rem .6rem;border-radius:1rem;font-size:.85rem}.rg-status .rg-status-label{font-weight:700}.rg-status .rg-status-sub{font-size:.78rem;opacity:.8}.rg-status.rg-status-low{background:rgba(31,164,99,.16);color:#1fa463}.rg-status.rg-status-typical{background:rgba(120,130,150,.16);color:var(--color-text, #555)}.rg-status.rg-status-high{background:rgba(231,76,60,.16);color:#e74c3c}.rg-tooltip{position:absolute;display:none;pointer-events:none;z-index:5;transform:translateY(-50%);white-space:nowrap;background:var(--color-background, #fff);color:var(--color-text, #222);border:1px solid var(--color-border-notification, rgba(127, 127, 127, 0.4));border-radius:5px;padding:4px 8px;font-size:12px;line-height:1.4;box-shadow:0 2px 6px rgba(0,0,0,.15)}#restock-history-table{margin-left:auto;margin-right:auto}#restock-history-table td,#restock-history-table th{text-align:left}.restock-table-toolbar{display:flex;align-items:center;justify-content:center;gap:.75rem;margin-bottom:.5rem}html[data-ai-mode=true] body.llm-configured tr.processor-restock_diff .btn-label-history{display:inline}html[data-ai-mode=true] body.llm-configured tr.processor-restock_diff .btn-label-summary{display:none}.restock-inline-row td{white-space:normal !important;word-break:break-word;padding:.6rem 1rem .8rem 1.4rem !important;background:linear-gradient(135deg, #e6fbf0, #e8f6ff) !important;border-top:1px solid #9fe3c2 !important;border-left:3px solid #1fa463 !important;color:#06281a !important;line-height:1.5}html[data-darkmode=true] .restock-inline-row td{background:linear-gradient(135deg, #0c2a1c, #0d2230) !important;border-top:1px solid #1f5e40 !important;border-left-color:#1fa463 !important;color:#d6ffe9 !important}.restock-inline-row .restock-inline-graph{width:100%;min-height:40px}.restock-inline-row .restock-inline-history-link{display:inline-block;margin-top:.5rem;font-size:.78rem;font-weight:700;opacity:.75;white-space:nowrap}.restock-inline-row .restock-inline-history-link:hover{opacity:1}.restock-inline-row .restock-inline-error{color:#c0392b}.toast-container{position:fixed;display:flex;flex-direction:column;gap:.75rem;pointer-events:none;z-index:10000}.toast-container.toast-top-right{top:20px;right:20px}.toast-container.toast-top-center{top:100px;left:50%;transform:translateX(-50%)}.toast-container.toast-top-left{top:20px;left:20px}.toast-container.toast-bottom-right{bottom:20px;right:20px}.toast-container.toast-bottom-center{bottom:20px;left:50%;transform:translateX(-50%)}.toast-container.toast-bottom-left{bottom:20px;left:20px}.toast{position:relative;display:flex;align-items:center;gap:.75rem;min-width:300px;max-width:500px;padding:1rem 1.25rem;background:var(--color-background);border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.15),0 0 0 1px rgba(0,0,0,.05);pointer-events:auto;overflow:hidden;opacity:0;transform:translateY(-50px);transition:all .3s cubic-bezier(0.68, -0.55, 0.265, 1.55);font-family:inherit}.toast.toast-show{opacity:1;transform:translateY(0)}.toast.toast-hide{opacity:0;transform:translateY(-50px) scale(0.95)}.toast.toast-success{border-left:4px solid #10b981}.toast.toast-success .toast-icon{color:#10b981}.toast.toast-error{border-left:4px solid #ef4444}.toast.toast-error .toast-icon{color:#ef4444}.toast.toast-warning{border-left:4px solid #f59e0b}.toast.toast-warning .toast-icon{color:#f59e0b}.toast.toast-info{border-left:4px solid #3b82f6}.toast.toast-info .toast-icon{color:#3b82f6}.toast.toast-default{border-left:4px solid var(--color-grey-500)}.toast-icon{flex-shrink:0;width:24px;height:24px}.toast-icon svg{width:100%;height:100%}.toast-message{flex:1;font-size:.875rem;line-height:1.5;color:var(--color-text);word-break:break-word;font-family:inherit}.toast-close{flex-shrink:0;width:24px;height:24px;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0);border:none;border-radius:4px;color:var(--color-grey-500);font-size:1.5rem;line-height:1;cursor:pointer;transition:all .2s ease;padding:0;margin-left:.25rem}.toast-close:hover{background:var(--color-grey-800);color:var(--color-text)}.toast-close:active{transform:scale(0.95)}.toast-progress{position:absolute;bottom:0;left:0;right:0;height:3px;background:currentColor;opacity:.3;transform-origin:left;transition:transform linear}html[data-darkmode=true] .toast{background:var(--color-grey-300);box-shadow:0 4px 12px rgba(0,0,0,.4),0 0 0 1px hsla(0,0%,100%,.05)}html[data-darkmode=true] .toast-close:hover{background:var(--color-grey-400)}@media only screen and (max-width: 768px){.toast-container{left:50% !important;right:auto !important;top:80px !important;transform:translateX(-50%) !important;align-items:center}.toast-container.toast-bottom-right,.toast-container.toast-bottom-center,.toast-container.toast-bottom-left{top:auto !important;bottom:80px !important}.toast{min-width:auto;max-width:none;width:80vw;transform:translateY(-100px)}.toast.toast-show{transform:translateY(0)}.toast.toast-hide{transform:translateY(-100px) scale(0.95)}}@media(prefers-reduced-motion: reduce){.toast{transition:opacity .2s ease;transform:none !important}.toast.toast-show{opacity:1}.toast.toast-hide{opacity:0}}.login-form{min-height:52vh;display:flex;align-items:center;justify-content:center;padding:2rem 1rem}.login-form .inner{background:var(--color-background);border-radius:16px;box-shadow:0 10px 40px rgba(0,0,0,.08),0 2px 8px rgba(0,0,0,.04);padding:3rem 2.5rem;width:100%;max-width:420px;position:relative;overflow:hidden;transition:transform .3s ease,box-shadow .3s ease}.login-form .inner:hover{box-shadow:0 15px 50px rgba(0,0,0,.12),0 5px 15px rgba(0,0,0,.06)}.login-form form{margin:0}.login-form fieldset{border:none;padding:0;margin:0}.login-form .pure-control-group{margin-bottom:1.75rem}.login-form .pure-control-group:last-of-type{margin-bottom:0;margin-top:2rem}.login-form label{display:block;margin-bottom:.5rem;font-weight:600;font-size:.9rem;color:var(--color-text);letter-spacing:.01em}.login-form input[type=password]{width:100%;padding:.875rem 1rem;border:2px solid var(--color-grey-800);border-radius:8px;font-size:1rem;background:var(--color-background-input);color:var(--color-text-input);transition:all .2s ease;box-sizing:border-box}.login-form input[type=password]:focus{outline:none;border-color:var(--color-link);box-shadow:0 0 0 3px rgba(27,152,248,.1);transform:translateY(-1px)}.login-form input[type=password]::placeholder{color:var(--color-text-input-placeholder)}.login-form button[type=submit]{width:100%;padding:.875rem 1.5rem;font-size:1rem;font-weight:600;border-radius:8px;border:none;background:var(--color-background-button-primary);color:var(--color-text-button);cursor:pointer;transition:all .2s ease;box-shadow:0 2px 8px rgba(27,152,248,.2)}.login-form button[type=submit]:hover{box-shadow:0 4px 12px rgba(27,152,248,.3);background:#06c}.login-form button[type=submit]:active{transform:translateY(0);box-shadow:0 2px 4px rgba(27,152,248,.2)}.content-main>ul.messages{position:fixed;top:120px;left:50%;transform:translateX(-50%);list-style:none;padding:0;margin:0;z-index:1000;min-width:300px;max-width:500px}.content-main>ul.messages li{padding:1rem 1.25rem;border-radius:8px;font-size:.95rem;line-height:1.5;font-weight:500;box-shadow:0 4px 12px rgba(0,0,0,.15);animation:slideDown .3s ease-out;border:2px solid rgba(0,0,0,0)}.content-main>ul.messages li.error{background:#fee;border:2px solid #ef4444;color:#991b1b;font-weight:600}.content-main>ul.messages li.success{background:#f0fdf4;border:2px solid #10b981;color:#166534}.content-main>ul.messages li.info,.content-main>ul.messages li.message{background:#eff6ff;border:2px solid #3b82f6;color:#1e40af}@keyframes slideDown{from{opacity:0;transform:translateY(-20px)}to{opacity:1;transform:translateY(0)}}html[data-darkmode=true] .login-form .inner{box-shadow:0 10px 40px rgba(0,0,0,.4),0 2px 8px rgba(0,0,0,.2)}html[data-darkmode=true] .login-form .inner:hover{box-shadow:0 15px 50px rgba(0,0,0,.5),0 5px 15px rgba(0,0,0,.3)}html[data-darkmode=true] .login-form input[type=password]{border-color:var(--color-grey-400)}html[data-darkmode=true] .login-form input[type=password]:focus{border-color:var(--color-link)}html[data-darkmode=true] .content-main>ul.messages li{box-shadow:0 4px 12px rgba(0,0,0,.4)}html[data-darkmode=true] .content-main>ul.messages li.error{background:#4a1d1d;border-color:#ef4444;color:#fca5a5}html[data-darkmode=true] .content-main>ul.messages li.success{background:#1a3a2a;border-color:#10b981;color:#86efac}html[data-darkmode=true] .content-main>ul.messages li.info,html[data-darkmode=true] .content-main>ul.messages li.message{background:#1e3a5f;border-color:#3b82f6;color:#93c5fd}@media only screen and (max-width: 768px){.login-form{min-height:auto;padding:1rem .5rem;padding-top:5rem}.login-form .inner{padding:2rem 1.5rem;border-radius:12px}.content-main>ul.messages{top:70px;left:10px;right:10px;transform:none;min-width:auto}}body.wrapped-tabs .tabs ul{grid-template-columns:repeat(auto-fill, minmax(var(--tab-width, 180px), 1fr));grid-auto-flow:row;grid-auto-columns:unset;gap:0;column-gap:5px}body.wrapped-tabs .tabs ul li{border-radius:0}.tabs ul{margin:0px;padding:0px;display:grid;grid-auto-flow:column;grid-auto-columns:max-content;gap:5px;list-style:none}.tabs ul li{white-space:nowrap;color:var(--color-text-tab);border-top-left-radius:5px;border-top-right-radius:5px;background-color:var(--color-background-tab)}.tabs ul li:not(.active):hover{background-color:var(--color-background-tab-hover)}.tabs ul li.active,.tabs ul li :target{background-color:var(--color-background)}.tabs ul li.active a,.tabs ul li :target a{color:var(--color-text-tab-active);font-weight:bold}.tabs ul li a{display:block;padding:.7em;color:var(--color-text-tab)}.stab-shell{display:flex;align-items:stretch;background:var(--color-background);border:1px solid rgba(0,0,0,.08);border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,.05);margin-bottom:1.5rem}.stab-nav{display:flex;flex-direction:column;width:11rem;flex-shrink:0;padding:.75rem 0;gap:1px;background:linear-gradient(180deg, rgba(0, 0, 0, 0.03) 0%, rgba(0, 0, 0, 0.05) 100%);border-right:1px solid rgba(0,0,0,.07)}.stab-btn{position:relative;display:flex;align-items:center;gap:.5rem;padding:.65rem .9rem .65rem 1rem;width:100%;background:none;border:none;border-left:3px solid rgba(0,0,0,0);border-radius:0;cursor:pointer;font:inherit;color:var(--color-text);text-align:left;opacity:.65;transition:background .12s ease,opacity .12s ease,border-color .12s ease,color .12s ease}.stab-btn:hover{background:rgba(0,0,0,.04);opacity:.85}.stab-btn.active{border-left-color:var(--color-menu-accent);background:rgba(237,89,0,.07);color:var(--color-menu-accent);font-weight:700;opacity:1}.stab-btn .stab-icon{display:inline-flex;align-items:center;justify-content:center;width:1.1rem;flex-shrink:0;opacity:.8}.stab-btn .stab-icon svg{width:.95rem;height:.95rem;stroke:currentColor;fill:none}.stab-body{flex:1;min-width:0;padding:1.4rem 1.6rem;overflow-x:hidden}.stab-pane{visibility:hidden;height:0;overflow:hidden}.stab-pane.active{visibility:visible;height:auto;overflow:visible;animation:stab-enter .16s ease both}@keyframes stab-enter{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:translateY(0)}}.stab-overview-hero{margin-bottom:1.5rem}.stab-overview-hero h3{margin:0 0 .3rem;font-size:1.05rem}.stab-overview-hero .stab-overview-glyph{color:var(--color-menu-accent);margin-right:.2rem}.stab-overview-hero .stab-overview-glyph svg{width:1.1rem;height:1.1rem;stroke:currentColor;fill:none;vertical-align:-0.15em}.stab-overview-hero p{margin:0;font-size:.88rem;color:var(--color-text-input-description);max-width:44rem;line-height:1.55}.stab-overview-features{display:flex;flex-direction:column;gap:.7rem;margin-bottom:1.6rem}.stab-overview-feature{display:flex;gap:.85rem;align-items:flex-start;padding:.75rem 1rem;border-radius:6px;background:rgba(0,0,0,.022);border:1px solid rgba(0,0,0,.05);transition:background .12s ease}.stab-overview-feature:hover{background:rgba(0,0,0,.035)}.stab-overview-feature .stab-overview-icon{width:1.6rem;flex-shrink:0;padding-top:.05rem;opacity:.7;display:flex;justify-content:center}.stab-overview-feature .stab-overview-icon svg{width:1.3rem;height:1.3rem;stroke:currentColor;fill:none}.stab-overview-feature .stab-overview-text>strong{display:block;margin-bottom:.2rem}.stab-overview-feature .stab-overview-text p{color:var(--color-text-input-description)}.stab-overview-disclaimer{display:flex;gap:.75rem;align-items:flex-start;margin:0 0 1.1rem;padding:.85rem 1rem;border-radius:6px;border:1px solid rgba(211,136,0,.35);background:rgba(255,180,0,.07)}.stab-overview-disclaimer .stab-disclaimer-icon{flex-shrink:0;padding-top:.05rem;color:#c07800}.stab-overview-disclaimer .stab-disclaimer-icon svg{width:1.2rem;height:1.2rem;stroke:currentColor;fill:none}.stab-overview-disclaimer .stab-disclaimer-body{font-size:.85rem;line-height:1.55}.stab-overview-disclaimer .stab-disclaimer-body>strong{display:block;margin-bottom:.35rem;color:#8a5500;font-size:.87rem}.stab-overview-disclaimer .stab-disclaimer-body p{margin:0 0 .45rem;color:var(--color-text-input-description)}.stab-overview-disclaimer .stab-disclaimer-body ul{margin:0 0 .6rem;padding-left:1.25rem;color:var(--color-text-input-description)}.stab-overview-disclaimer .stab-disclaimer-body ul li{margin-bottom:.2rem}.stab-overview-disclaimer .stab-disclaimer-check{display:flex;gap:.5rem;align-items:flex-start;cursor:pointer;font-size:.82rem;color:var(--color-text-input-description);font-weight:600}.stab-overview-disclaimer .stab-disclaimer-check input[type=checkbox]{flex-shrink:0;margin-top:.18rem;cursor:pointer}.stab-overview-cta{margin-top:.4rem;display:flex;align-items:center;gap:.8rem;flex-wrap:wrap}.stab-configured-badge{display:inline-flex;align-items:center;gap:.4rem;padding:.35rem .75rem;background:rgba(39,174,96,.09);border:1px solid rgba(39,174,96,.28);border-radius:4px;color:#2a7a4e;font-size:.82rem;font-weight:600}.stab-section-title{font-size:.72rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;opacity:.45;margin:1.4rem 0 .6rem}.stab-section-title:first-child{margin-top:0}@media(max-width: 600px){.stab-shell{flex-direction:column;min-height:unset}.stab-nav{width:100%;border-right:none;border-bottom:1px solid rgba(0,0,0,.07);padding:.4rem 0}.stab-body{padding-left:1rem}}.llm-usage-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(12rem, 1fr));gap:.9rem;margin-bottom:1.4rem}.llm-stat-card{padding:1rem 1.1rem .85rem;border-radius:7px;background:rgba(0,0,0,.025);border:1px solid rgba(0,0,0,.07)}.llm-stat-card .llm-stat-label{font-size:.7rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;opacity:.4;margin-bottom:.4rem}.llm-stat-card .llm-stat-value{font-size:1.65rem;font-weight:700;letter-spacing:-0.02em;line-height:1;margin-bottom:.25rem}.llm-stat-card .llm-stat-sub{font-size:.79rem;opacity:.5}.llm-stat-card .llm-stat-budget-text{font-size:.77rem;opacity:.55;margin-top:.3rem}.llm-stat-bar-wrap{height:4px;border-radius:2px;background:rgba(0,0,0,.1);overflow:hidden;margin-top:.65rem}.llm-stat-bar-fill{height:100%;border-radius:2px;transition:width .5s ease}.llm-stat-bar-fill.bar-ok{background:#27ae60}.llm-stat-bar-fill.bar-warn{background:#e67e22}.llm-stat-bar-fill.bar-over{background:#c0392b}.llm-usage-settings{border-top:1px solid rgba(0,0,0,.07);padding-top:.9rem;display:flex;flex-direction:column;gap:.65rem}.llm-usage-row{display:flex;align-items:baseline;gap:.9rem;flex-wrap:wrap}.llm-usage-row .llm-usage-row-label{font-size:.82rem;font-weight:600;opacity:.6;min-width:12rem;flex-shrink:0}.llm-usage-row .llm-usage-row-value{display:flex;align-items:baseline;gap:.5rem;flex-wrap:wrap;font-size:.88rem}.llm-field-hint{font-size:.8rem;opacity:.55}.llm-env-badge{font-size:.79rem;opacity:.6}.llm-budget-alert{color:#c0392b;font-weight:600;font-size:.88rem;margin:0 0 1rem}.llm-no-usage{opacity:.5;font-style:italic;font-size:.88rem;margin-bottom:1rem}html[data-darkmode=true] .stab-shell{border-color:hsla(0,0%,100%,.07);box-shadow:0 2px 8px rgba(0,0,0,.25)}html[data-darkmode=true] .stab-nav{background:linear-gradient(180deg, rgba(255, 255, 255, 0.025) 0%, rgba(255, 255, 255, 0.04) 100%);border-right-color:hsla(0,0%,100%,.07)}html[data-darkmode=true] .stab-btn:hover{background:hsla(0,0%,100%,.05)}html[data-darkmode=true] .stab-btn.active{background:rgba(237,89,0,.12)}html[data-darkmode=true] .stab-overview-feature{background:hsla(0,0%,100%,.025);border-color:hsla(0,0%,100%,.05)}html[data-darkmode=true] .stab-overview-feature:hover{background:hsla(0,0%,100%,.04)}html[data-darkmode=true] .stab-configured-badge{background:rgba(39,174,96,.1);border-color:rgba(39,174,96,.22);color:#5db880}html[data-darkmode=true] .stab-overview-disclaimer{border-color:rgba(255,190,50,.22);background:rgba(255,180,0,.05)}html[data-darkmode=true] .stab-overview-disclaimer .stab-disclaimer-icon{color:#c9963a}html[data-darkmode=true] .stab-overview-disclaimer .stab-disclaimer-body>strong{color:#c9a050}html[data-darkmode=true] .llm-stat-card{background:hsla(0,0%,100%,.03);border-color:hsla(0,0%,100%,.07)}html[data-darkmode=true] .llm-stat-bar-wrap{background:hsla(0,0%,100%,.1)}html[data-darkmode=true] .llm-usage-settings{border-top-color:hsla(0,0%,100%,.07)}body,.pure-table,.pure-table thead,.pure-table td,.pure-table th,.pure-form input,.pure-form textarea,.pure-form select,.edit-form .inner,.pure-menu-horizontal,footer,.sticky-tab,#diff-jump,.button-tag,#new-watch-form,#new-watch-form input:not(.pure-button),code,.messages li,#checkbox-operations,.inline-warning,a,.watch-controls img{transition:color .4s ease,background-color .4s ease,background .4s ease,border-color .4s ease,box-shadow .4s ease}body{color:var(--color-text);background:var(--color-background-page);font-family:Helvetica Neue,Helvetica,Lucida Grande,Arial,Ubuntu,Cantarell,Fira Sans,sans-serif}.app{display:flex;align-items:stretch;min-height:100vh}.app-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:.55rem}.content-wrapper{display:flex;width:100%;max-width:100%;position:relative;align-items:flex-start}@media only screen and (max-width: 980px){.content-wrapper{flex-direction:column}}.content-main{flex:1 1 auto;width:100%;min-width:0;display:flex;flex-direction:column;align-items:center}@media only screen and (min-width: 980px){.content-main{flex-direction:column}}.visually-hidden{clip:rect(0 0 0 0);clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}.status-icon{display:inline-block;height:1rem;vertical-align:middle}a{text-decoration:none;color:var(--color-link)}#search-result-info{color:#fff}button.toggle-button{vertical-align:middle;background:rgba(0,0,0,0);border:none;cursor:pointer;color:var(--color-text-menu-heading)}button.toggle-button svg{fill:currentColor}button.toggle-button svg.feather{fill:none;stroke:currentColor}button.toggle-button .icon-light{display:block}body.spinner-active #pure-menu-horizontal-spinner{animation:gradient 1s ease infinite}@keyframes gradient{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}#cdio-logo{color:var(--color-white);text-transform:uppercase}.pure-menu-link{color:var(--color-text-menu-link)}.pure-menu-link:hover{background-color:var(--color-background-menu-link-hover);color:var(--color-text-menu-link-hover)}.tab-pane-inner{scroll-margin-top:200px}section.content{padding-bottom:1em;padding-left:.55rem;padding-right:.55rem;flex-direction:column;display:flex;align-items:center;justify-content:flex-start}details summary{cursor:pointer;font-weight:600;color:var(--color-link);width:fit-content}details summary:hover{text-decoration:underline}code{background:var(--color-background-code);color:var(--color-text)}.inline-tag,.restock-label,.tracking-ldjson-price-data,.watch-tag-list,.processor-badge{white-space:nowrap;border-radius:5px;padding:2px 5px;margin-right:4px}.processor-badge{font-weight:900;text-decoration:none}.processor-badge:hover{text-decoration:none;opacity:.8;cursor:pointer}.processor-badge.active{outline:2px solid var(--color-link);outline-offset:1px}.watch-tag-list{color:var(--color-white);background:var(--color-text-watch-tag-list);text-decoration:none}.watch-tag-list:hover{text-decoration:none;opacity:.8;cursor:pointer}.watch-tag-list:visited{color:var(--color-white)}body:after{content:"";background:linear-gradient(130deg, var(--color-background-gradient-first), var(--color-background-gradient-second) 41.07%, var(--color-background-gradient-third) 84.05%)}body:after,body:before{display:block;position:fixed;top:0;left:0;width:100%;height:100vh;z-index:-1}body::after{opacity:.91}body::before{content:""}.button-small{font-size:85%}.button-xsmall{font-size:70%}.fetch-error{padding-top:1em;font-size:80%;max-width:400px;display:block}.pure-button-primary,a.pure-button-primary,.pure-button-selected,a.pure-button-selected{background-color:var(--color-background-button-primary)}.button-secondary{color:var(--color-text-button);border-radius:4px;text-shadow:0 1px 1px rgba(0,0,0,.2)}.button-success{background:var(--color-background-button-success)}.button-tag{background:var(--color-background-button-tag);color:var(--color-text-button);font-size:75%;border-radius:6px;margin-right:4px;margin-bottom:1px}.button-tag.active{background:var(--color-background-button-tag-active);font-weight:bold}.button-error{background:var(--color-background-button-error);color:var(--color-text-button-error)}.button-warning{background:var(--color-background-button-warning);color:var(--color-text-button-warning)}.button-secondary{background:var(--color-background-button-secondary)}.button-cancel{background:var(--color-background-button-cancel)}.messages li{list-style:none;padding:1em;border-radius:10px;color:var(--color-text-messages);font-weight:bold}.messages li.message{background:var(--color-background-messages-message)}.messages li.error{background:var(--color-background-messages-error)}.messages li.notice{background:var(--color-background-messages-notice)}.messages.with-share-link>*:hover{cursor:pointer}label:hover{cursor:pointer}.grey-form-border{border:1px solid var(--color-border-notification);padding:.5rem;border-radius:5px}#token-table.pure-table td,#token-table.pure-table th{font-size:80%}.pure-form input[type=text].transparent-field{background-color:var(--color-background-new-watch-input-transparent) !important;color:var(--color-white) !important;border:1px solid hsla(0,0%,100%,.2) !important;box-shadow:none !important;-webkit-box-shadow:none !important}.pure-form input[type=text].transparent-field::placeholder{opacity:.5;color:hsla(0,0%,100%,.7);font-weight:lighter}#new-watch-form{background:var(--color-background-new-watch-form);padding:1em;border-radius:10px;max-width:100%}#new-watch-form #url::placeholder{font-weight:bold}#new-watch-form input{display:inline-block}#new-watch-form input:not(.pure-button){background-color:var(--color-background-new-watch-input);color:var(--color-text-new-watch-input)}#new-watch-form .label{display:none}#new-watch-form legend{color:var(--color-text-legend);font-weight:bold}@media only screen and (min-width: 760px){#new-watch-form #watch-add-wrapper-zone{display:flex;gap:.3rem;flex-direction:row;min-width:70vw}}#new-watch-form #watch-add-wrapper-zone>span{flex-grow:0}#new-watch-form #watch-add-wrapper-zone>span input{width:100%;padding-right:1em}#new-watch-form #watch-add-wrapper-zone>span:first-child{flex-grow:1}@media only screen and (max-width: 760px){#new-watch-form #watch-add-wrapper-zone #url{width:100%}}#new-watch-form #watch-group-tag{font-size:.9rem;padding:.3rem;display:flex;align-items:center;gap:.5rem;color:var(--color-white)}#new-watch-form #watch-group-tag label,#new-watch-form #watch-group-tag input{margin:0}#new-watch-form #watch-group-tag input{flex:1}#diff-col{padding-left:40px}#diff-jump{position:fixed;left:0px;top:120px;background:var(--color-background);padding:10px;border-top-right-radius:5px;border-bottom-right-radius:5px;box-shadow:1px 1px 4px var(--color-shadow-jump)}#diff-jump a{color:var(--color-link);cursor:pointer;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;-o-user-select:none}footer{padding:10px;background:var(--color-background);color:var(--color-text-footer);text-align:center}#feed-icon{vertical-align:middle}#new-version-text a{color:var(--color-link-new-version)}.watch-controls{color:#f8321b}.watch-controls .state-on img{opacity:.8}.watch-controls img{opacity:.2}.watch-controls img:hover{transition:opacity .3s;opacity:.8}.monospaced-textarea textarea{width:100%;font-family:monospace;white-space:pre;overflow-wrap:normal;overflow-x:auto}.pure-form fieldset{padding-top:0px}.pure-form fieldset ul{padding-bottom:0px;margin-bottom:0px}.pure-form .pure-control-group,.pure-form .pure-group,.pure-form .pure-controls{padding-bottom:1em}.pure-form .pure-control-group div,.pure-form .pure-group div,.pure-form .pure-controls div{margin:0px}.pure-form .pure-control-group .checkbox>*,.pure-form .pure-group .checkbox>*,.pure-form .pure-controls .checkbox>*{display:inline;vertical-align:middle}.pure-form .pure-control-group .checkbox>label,.pure-form .pure-group .checkbox>label,.pure-form .pure-controls .checkbox>label{padding-left:5px}.pure-form .pure-control-group legend,.pure-form .pure-group legend,.pure-form .pure-controls legend{color:var(--color-text-legend)}.pure-form .error input{background-color:var(--color-error-input)}.pure-form ul.errors{padding:.5em .6em;border:1px solid var(--color-error-list);border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;box-sizing:border-box}.pure-form ul.errors li{margin-left:1em;color:var(--color-error-list)}.pure-form label{font-weight:bold}.pure-form textarea{width:100%}.pure-form .inline-radio ul{margin:0px;list-style:none}.pure-form .inline-radio ul li{display:flex;align-items:center;gap:1em}@media only screen and (max-width: 760px),(min-device-width: 768px)and (max-device-width: 980px){.edit-form{padding:.5em;margin:0}#nav-menu{overflow-x:scroll}}@media only screen and (max-width: 760px),(min-device-width: 768px)and (max-device-width: 980px){input[type=text]{width:100%}}.pure-table{border-color:var(--color-border-table-cell)}.pure-table thead{background-color:var(--color-background-table-thead);color:var(--color-text);border-bottom:1px solid var(--color-background-table-thead)}.pure-table td,.pure-table th{border-left-color:var(--color-border-table-cell)}.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form select,.pure-form textarea{border:var(--color-border-input);box-shadow:inset 0 1px 3px var(--color-shadow-input);background-color:var(--color-background-input);color:var(--color-text-input)}.pure-form input[type=color]:active,.pure-form input[type=date]:active,.pure-form input[type=datetime-local]:active,.pure-form input[type=datetime]:active,.pure-form input[type=email]:active,.pure-form input[type=month]:active,.pure-form input[type=number]:active,.pure-form input[type=password]:active,.pure-form input[type=search]:active,.pure-form input[type=tel]:active,.pure-form input[type=text]:active,.pure-form input[type=time]:active,.pure-form input[type=url]:active,.pure-form input[type=week]:active,.pure-form select:active,.pure-form textarea:active{background-color:var(--color-background-input)}input::placeholder,textarea::placeholder{color:var(--color-text-input-placeholder)}.m-d{min-width:100%}@media only screen and (min-width: 761px){.m-d{min-width:80%}}.pure-form-stacked>div:first-child{display:block}.tab-pane-inner{padding:0px}.tab-pane-inner:not(:target){display:none}.tab-pane-inner:target{display:block}.beta-logo{height:50px;right:-3px;top:-3px;position:absolute}#selector-header{padding-bottom:1em}.edit-form{max-width:95%}.edit-form .box-wrap{position:relative}.edit-form .inner{background:var(--color-background);padding:1.1rem}.edit-form #actions{display:block;background:var(--color-background)}.edit-form #actions .pure-control-group{display:flex;gap:.625em;flex-wrap:wrap}.edit-form .pure-form-message-inline{padding-left:0;color:var(--color-text-input-description)}.edit-form .pure-form-message-inline code{font-size:.875em}.border-fieldset{border:1px solid #ccc;padding:1rem;border-radius:5px;margin-bottom:1rem}.border-fieldset h3{margin-top:0}.border-fieldset fieldset:last-of-type{padding-bottom:0}.border-fieldset fieldset:last-of-type .pure-control-group{padding-bottom:0}ul{padding-left:1em;padding-top:0px;margin-top:4px}.time-check-widget tr{display:inline}.time-check-widget tr input[type=number]{width:5em}@media only screen and (max-width: 760px){.time-check-widget tbody{display:grid;grid-template-columns:auto 1fr auto 1fr;gap:.625em .3125em;align-items:center}.time-check-widget tr{display:contents}.time-check-widget tr th{text-align:right;padding-right:5px}.time-check-widget tr input[type=number]{width:100%;max-width:5em}}#webdriver_delay{width:5em}#api-key:hover{cursor:pointer}#api-key-copy{color:var(--color-api-key)}.button-green{background-color:var(--color-background-button-green)}.button-red{background-color:var(--color-background-button-red)}.noselect{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}[type=checkbox],[type=radio]{width:17px;height:17px;border-radius:5px;cursor:pointer}.inline-warning{border:1px solid var(--color-border-warning);padding:.5rem;border-radius:5px;color:var(--color-warning)}.inline-warning>span{display:inline-block;vertical-align:middle}.inline-warning img.inline-warning-icon{display:inline;height:26px;vertical-align:middle}.tracking-ldjson-price-data{background-color:var(--color-background-button-green);color:#000;opacity:.6}.ldjson-price-track-offer{font-weight:bold;font-style:italic}.ldjson-price-track-offer a.pure-button{border-radius:3px;padding:3px;background-color:var(--color-background-button-green)}.price-follow-tag-icon{display:inline-block;height:.8rem;vertical-align:middle}#quick-watch-processor-type ul#processor{color:#fff;padding-left:0px}#quick-watch-processor-type ul#processor li{list-style:none;font-size:.9rem;display:grid;grid-template-columns:auto 1fr;align-items:center;gap:.5rem;margin-bottom:.5rem}#quick-watch-processor-type label,#quick-watch-processor-type input{padding:0;margin:0}.restock-label.in-stock{background-color:#7a0cc5;color:#fff}.restock-label.not-in-stock{background-color:var(--color-background-button-cancel);color:#777}.restock-label.error{background-color:var(--color-background-button-error);color:#fff;opacity:.7}.restock-label.price{border:1px solid var(--color-background-button-cancel)}.restock-label svg{vertical-align:middle}.price-change{white-space:nowrap;font-weight:700;font-size:90%;margin-left:4px;vertical-align:middle}.price-change.down{color:var(--color-background-button-green)}.price-change.up{color:var(--color-background-button-error)}#chrome-extension-link{padding:9px;border:1px solid var(--color-grey-800);border-radius:10px;vertical-align:middle}#chrome-extension-link img{height:21px;padding:2px;vertical-align:middle}#realtime-conn-error{position:fixed;bottom:0;left:0;background:var(--color-warning);padding:10px;font-size:.8rem;color:#fff;opacity:.8;z-index:100}#bottom-horizontal-offscreen{position:fixed;bottom:0;left:0;right:0;width:100%;min-height:50px;max-height:50vh;background:hsla(0,0%,100%,.7215686275);border-top:1px solid var(--color-border-table-cell);padding:10px;box-shadow:0 -2px 10px rgba(0,0,0,.2);z-index:100;overflow-y:auto;transition:opacity .3s ease-in-out;scroll-margin-bottom:10px;display:flex;justify-content:center;align-items:center}ul#highlightSnippetActions{list-style:none}ul#highlightSnippetActions li{display:inline-block}@media only screen and (max-width: 768px){.box{padding:.25rem !important}}.box{color:var(--color-white);border-width:1px;border-style:dashed;border-color:hsla(0,0%,100%,.25);border-image:initial;background:hsla(0,0%,100%,.04);border-radius:var(--common-round-border);padding:1.1rem}header{color:var(--color-white)}#heart-us svg{display:inline-block;vertical-align:middle;cursor:pointer;width:1.4rem}textarea::placeholder{white-space:pre-wrap}
diff --git a/changedetectionio/templates/edit/include_llm_intent.html b/changedetectionio/templates/edit/include_llm_intent.html
index 0641d24bb..9c9535bdd 100644
--- a/changedetectionio/templates/edit/include_llm_intent.html
+++ b/changedetectionio/templates/edit/include_llm_intent.html
@@ -5,59 +5,130 @@
llm_configured — bool: LLM provider is configured in settings
form — the WTForms form (must have .llm_intent and .llm_change_summary fields)
- Optional (watch edit only):
+ Group/tag edit only:
+ llm_group_edit — bool: True when rendering the tag/group edit page. This is the ONLY
+ way to tell the two contexts apart — the tags blueprint also passes
+ the tag dict as `watch`, so `watch` is truthy in both.
+ form.llm_backend_profile is ternary here (On / Off / Leave it to each
+ watch) and is the group's only AI control; on a watch it is a checkbox.
+
+ Watch edit only:
watch — the Watch object (for processor check and prefilter display)
llm_group_overrides — dict returned by _resolve_llm_group_overrides():
{'llm_intent': {'value': str, 'group_name': str} | None,
- 'llm_change_summary': {'value': str, 'group_name': str} | None}
- Present only in watch edit context; absent in tag edit context.
+ 'llm_change_summary': {'value': str, 'group_name': str} | None,
+ 'llm_backend_profile': {'value': bool, 'group_name': str} | None}
+ The two prompt entries are non-None only when the watch has no own
+ value AND a linked group set to "On" has one — that is what puts
+ "From group '': " in the placeholder.
+ llm_backend_profile is non-None when a linked group has taken the
+ on/off decision (On or Off, not "leave it to each watch") — #4204.
- Usage in watch edit (edit.html):
- {% include "edit/include_llm_intent.html" %}
-
- Usage in tag edit (edit-tag.html):
- {% include "edit/include_llm_intent.html" %}
- (watch is not set → tag mode: no processor check, no examples, different description)
+ Usage (both): {% include "edit/include_llm_intent.html" %}
#}
-{% from '_helpers.html' import render_field %}
+{% from '_helpers.html' import render_field, render_checkbox_field, render_ternary_field %}
-{# Processor check only applies in watch-edit context (llm_group_overrides present). #}
-{# In tag/group edit context the AI section is always visible. #}
+{% set llm_group_mode = llm_group_edit|default(false) %}
+
+{# Processor check only applies in watch-edit context. #}
+{# In tag/group edit context the AI section is always visible. #}
{# Processors whose edit form carries the AI Intent / Change Summary fields (i.e. those whose
form inherits processor_text_json_diff_form). text_json_diff + restock_diff today. #}
-{% if llm_group_overrides is defined %}
- {% set show_ai_section = not watch.get('processor') or watch.get('processor') in ['text_json_diff', 'restock_diff'] %}
-{% else %}
+{% if llm_group_mode %}
{% set show_ai_section = true %}
+{% else %}
+ {% set show_ai_section = not watch.get('processor') or watch.get('processor') in ['text_json_diff', 'restock_diff'] %}
+{% endif %}
+
+{# An unrendered switch is absent from the POST, and WTForms reads that as off. So whenever we
+ do NOT render the AI switch (no LLM provider configured, or a processor whose form has no AI
+ fields) we carry its saved state through in a hidden input — otherwise merely saving the page
+ would silently switch AI off. #}
+{% if not (show_ai_section and llm_configured) %}
+ {% if llm_group_mode %}
+ {# Group: ternary, so preserve whichever of the three states it is in #}
+ {% if form.llm_backend_profile.data is not none %}{% endif %}
+ {% elif form.llm_backend_profile.data %}
+
+ {% endif %}
{% endif %}
{% if show_ai_section %}
{# ── Configured: show the intent + summary fields ────────────────── #}
{% if llm_configured %}
-
-
✨ {{ _('AI') }}
+
- {# — AI Change Intent — #}
-
{{ _('AI — Notify when…') }}
+{% if llm_group_mode %}
+{# The group's single AI control. "On" is also what makes the prompts below cascade to the
+ watches (tag_llm_applies_to_watches), so there is no second "does this override?" flag. #}
+
+ {{ render_ternary_field(form.llm_backend_profile) }}
+
+ {{ _('On – every watch in this group uses the AI settings below, unless it fills in its own. Off – no AI for any watch in this group. Leave it to each watch – this group has no say; each watch uses its own AI settings.')|safe }}
+
+
+{# The prompts below only mean something in the "On" state, so grey them out otherwise — the
+ radio counterpart of the toggleOpacity cue used by #overrides_watch on the restock tab. #}
+
+{% else %}
+{# Per-watch AI on/off (#4204). Resolution is global settings → group → watch, so a group that
+ has taken the decision (its AI setting is On or Off rather than "leave it to each watch")
+ owns this control: we disable it, show the state the group decided, and name the group.
+ The watch's own preference is not lost — because the field isn't user-writable in this
+ state, edit.py ignores whatever the POST says for it and keeps the stored value. #}
+{% set profile_group = llm_group_overrides.llm_backend_profile if llm_group_overrides is defined else none %}
+
+ {# Only the checkbox is dimmed — the explanation of *why* has to stay readable. #}
+
+ {% if profile_group %}
+ {# Show what the group decided, not this watch's now-inert own value. #}
+ {% set dummy = form.llm_backend_profile.__setattr__('checked', profile_group.value) %}
+ {{ render_checkbox_field(form.llm_backend_profile, disabled=True) }}
+ {% else %}
+ {{ render_checkbox_field(form.llm_backend_profile) }}
+ {% endif %}
+
+ {% if profile_group %}
+ {# The group name links to its edit page so the decision can be changed where it lives.
+ Built as escaped markup and passed into the sentence, so translators keep one string. #}
+ {%- set group_link -%}
+ {{ profile_group.group_name }}
+ {%- endset -%}
+
+ {% if profile_group.value %}
+ {{ _("Group %(name)s decides this: AI is ON for every watch in that group.", name=group_link) | safe }}
+ {% else %}
+ {{ _("Group %(name)s decides this: AI is OFF for every watch in that group.", name=group_link) | safe }}
+ {% endif %}
+
+ {% endif %}
+
+{% endif %}
+
+
- {% if watch is defined and watch %}
+ {% if not llm_group_mode %}
{{ _('Describe what you care about. The AI evaluates every detected change against this and only notifies you when it matches.') }}
{% else %}
{{ _('Set a change intent for all watches in this tag/group. Each watch can override with its own.') }}
{% endif %}
- {% if watch is defined and watch and llm_group_overrides is defined and llm_group_overrides.llm_intent %}
+ {% if not llm_group_mode and llm_group_overrides is defined and llm_group_overrides.llm_intent %}
{% set intent_placeholder = _("From group '%(name)s': %(value)s", name=llm_group_overrides.llm_intent.group_name, value=llm_group_overrides.llm_intent.value) %}
- {% elif watch is defined and watch %}
+ {% elif not llm_group_mode %}
{% set intent_placeholder = _('e.g. Alert me when the price drops below $300, or a new product is launched. Ignore footer and navigation changes.') %}
{% else %}
{% set intent_placeholder = _('e.g. Flag price changes or new product launches across all watches in this group') %}
{% endif %}
{{ render_field(form.llm_intent, placeholder=intent_placeholder, rows=5, class="pure-input-1") }}
- {% if watch is defined and watch %}
+ {% if not llm_group_mode %}
{{ _('Examples:') }}
@@ -67,19 +138,19 @@
{{ _('Only important if package versions change or a CVE is mentioned') }}
- {% if watch.get('llm_prefilter') %}
+ {% if watch is defined and watch.get('llm_prefilter') %}
- {% if watch is defined and watch %}
+ {% if not llm_group_mode %}
{{ _('When a change is detected, the AI describes it according to your instructions and replaces %(diff)s in your notification. Use %(raw_diff)s if you still want the original diff.',
diff='{{diff}}', raw_diff='{{raw_diff}}') | safe }}
{% else %}
@@ -87,34 +158,30 @@
{% endif %}
- {% if watch is defined and watch and llm_group_overrides is defined and llm_group_overrides.llm_change_summary %}
+ {% if not llm_group_mode and llm_group_overrides is defined and llm_group_overrides.llm_change_summary %}
{% set summary_placeholder = _("From group '%(name)s': %(value)s", name=llm_group_overrides.llm_change_summary.group_name, value=llm_group_overrides.llm_change_summary.value) %}
{% else %}
{% set summary_placeholder = form.llm_change_summary.render_kw['placeholder'] %}
{% endif %}
{{ render_field(form.llm_change_summary, placeholder=summary_placeholder, rows=5, class="pure-input-1") }}
-
- {% for subfield in form.llm_change_summary_mode %}
-
- {% endfor %}
+
+
+ {{ render_field(form.llm_change_summary_mode) }}
+
+ {% if not llm_group_mode %}
+ {{ _('Appending keeps the prompt inherited from the group or from global settings and adds your text after it, so later edits to that prompt still reach this watch.') }}
+ {% else %}
+ {{ _('Appending keeps the prompt inherited from global settings and adds your text after it, so later edits to that prompt still reach this group.') }}
+ {% endif %}
+
-
- {% if watch is defined and watch %}
- {{ _('Appending keeps the prompt inherited from the group or from global settings and adds your text after it, so later edits to that prompt still reach this watch.') }}
- {% else %}
- {{ _('Appending keeps the prompt inherited from global settings and adds your text after it, so later edits to that prompt still reach this group.') }}
- {% endif %}
-
- {% if watch is defined and watch %}
+
+
+
+ {% if not llm_group_mode %}
{{ _('Examples:') }}
@@ -125,13 +192,14 @@
{% endif %}
+
{# ── Not configured: greyed-out prompt to configure ──────────────── #}
{% else %}
✨ {{ _('AI') }}
- {% if watch is defined and watch %}
+ {% if not llm_group_mode %}
{{ _('Configure an AI / LLM provider in Settings → AI / LLM to enable AI Change Intent and AI Change Summary.',
url=url_for('settings.settings_page') + '#ai') | safe }}
{% else %}
diff --git a/changedetectionio/tests/llm/test_evaluator.py b/changedetectionio/tests/llm/test_evaluator.py
index 398797494..6b55ffac6 100644
--- a/changedetectionio/tests/llm/test_evaluator.py
+++ b/changedetectionio/tests/llm/test_evaluator.py
@@ -22,6 +22,19 @@ def _make_datastore(llm_cfg=None, tags=None):
return ds
+def _make_tag(ai=True, **fields):
+ """Build a tag dict.
+
+ `ai` is the group's single AI control (llm_backend_profile): True = on, and its AI
+ settings apply to its watches; False = off for every watch in the group; None = the
+ group has no say. Defaults to True because most cases here are about what a group set
+ to "On" does.
+ """
+ tag = {'title': 'grp', 'llm_backend_profile': ai}
+ tag.update(fields)
+ return tag
+
+
def _make_watch(llm_intent='', llm_change_summary='', tags=None, uuid='test-uuid-1234'):
w = {}
w['llm_intent'] = llm_intent
@@ -43,7 +56,7 @@ class TestResolveIntent:
def test_watch_intent_takes_priority(self):
from changedetectionio.llm.evaluator import resolve_intent
- tag = {'title': 'mygroup', 'llm_intent': 'group intent'}
+ tag = _make_tag(title='mygroup', llm_intent='group intent')
ds = _make_datastore(tags={'tag-1': tag})
watch = _make_watch(llm_intent='watch intent', tags=['tag-1'])
@@ -54,7 +67,7 @@ class TestResolveIntent:
def test_tag_intent_used_when_watch_has_none(self):
from changedetectionio.llm.evaluator import resolve_intent
- tag = {'title': 'pricing-group', 'llm_intent': 'flag price drops'}
+ tag = _make_tag(title='pricing-group', llm_intent='flag price drops')
ds = _make_datastore(tags={'tag-1': tag})
watch = _make_watch(llm_intent='', tags=['tag-1'])
@@ -73,10 +86,10 @@ class TestResolveIntent:
assert source == ''
def test_tag_applied_to_all_watches_in_group(self):
- """Tag intent propagates to every watch in the tag (no opt-in needed)."""
+ """An opted-in tag's intent propagates to every watch in the tag."""
from changedetectionio.llm.evaluator import resolve_intent
- tag = {'title': 'job-board', 'llm_intent': 'new engineering jobs'}
+ tag = _make_tag(title='job-board', llm_intent='new engineering jobs')
ds = _make_datastore(tags={'tag-1': tag})
# Three different watches, all in the tag, none have their own intent
@@ -103,6 +116,114 @@ class TestResolveIntent:
assert intent == ''
+# ---------------------------------------------------------------------------
+# The group's AI setting gates the whole cascade
+# ---------------------------------------------------------------------------
+
+class TestGroupAiSettingGatesTheCascade:
+ def test_only_the_on_state_hands_settings_down(self):
+ from changedetectionio.llm.evaluator import tag_llm_applies_to_watches
+ assert tag_llm_applies_to_watches(_make_tag(ai=True)) is True
+ # "Off" suppresses AI rather than lending prompts; "leave it to each watch" and tags
+ # predating the setting (and missing tags) hand nothing down either
+ assert tag_llm_applies_to_watches(_make_tag(ai=False)) is False
+ assert tag_llm_applies_to_watches(_make_tag(ai=None)) is False
+ assert tag_llm_applies_to_watches({'title': 'legacy'}) is False
+ assert tag_llm_applies_to_watches(None) is False
+
+ def test_intent_not_inherited_when_group_is_off(self):
+ from changedetectionio.llm.evaluator import resolve_intent
+ tag = _make_tag(ai=False, title='pricing-group', llm_intent='flag price drops')
+ ds = _make_datastore(tags={'tag-1': tag})
+ watch = _make_watch(llm_intent='', tags=['tag-1'])
+ assert resolve_intent(watch, ds) == ('', '')
+
+ def test_field_not_inherited_when_group_is_off(self):
+ from changedetectionio.llm.evaluator import resolve_llm_field
+ tag = _make_tag(ai=False, llm_change_summary='list new events')
+ ds = _make_datastore(tags={'t1': tag})
+ watch = _make_watch(llm_change_summary='', tags=['t1'])
+ assert resolve_llm_field(watch, ds, 'llm_change_summary') == ('', '')
+
+ def test_summary_prompt_not_inherited_when_group_is_off(self):
+ from changedetectionio.llm.evaluator import get_effective_summary_prompt
+ tag = _make_tag(ai=False, llm_change_summary='TAG')
+ ds = _make_datastore(llm_cfg={'change_summary_default': 'GLOBAL'}, tags={'t1': tag})
+ watch = _make_watch(llm_change_summary='', tags=['t1'])
+ assert get_effective_summary_prompt(watch, ds) == 'GLOBAL'
+
+ def test_first_group_set_to_on_wins_over_an_earlier_one_that_is_off(self):
+ """A group that isn't "On" is skipped entirely, not treated as "found, but empty"."""
+ from changedetectionio.llm.evaluator import resolve_intent
+ ds = _make_datastore(tags={
+ 'ignored': _make_tag(ai=False, title='ignored-group', llm_intent='IGNORED'),
+ 'used': _make_tag(ai=True, title='used-group', llm_intent='USED'),
+ })
+ watch = _make_watch(llm_intent='', tags=['ignored', 'used'])
+ assert resolve_intent(watch, ds) == ('USED', 'used-group')
+
+
+# ---------------------------------------------------------------------------
+# llm_enabled_for_watch — per-watch / per-group AI on-off switch (#4204)
+# ---------------------------------------------------------------------------
+
+class TestLlmEnabledForWatch:
+ def test_enabled_by_default(self):
+ """Watches predating the switch (no key at all) keep AI on."""
+ from changedetectionio.llm.evaluator import llm_enabled_for_watch
+ ds = _make_datastore()
+ assert llm_enabled_for_watch(_make_watch(), ds) == (True, 'watch')
+
+ def test_watch_switch_off(self):
+ from changedetectionio.llm.evaluator import llm_enabled_for_watch
+ ds = _make_datastore()
+ watch = _make_watch()
+ watch['llm_backend_profile'] = False
+ assert llm_enabled_for_watch(watch, ds) == (False, 'watch')
+
+ def test_group_switch_overrides_watch_off(self):
+ """"The group setting overrides any watch on/off" — group ON beats watch OFF."""
+ from changedetectionio.llm.evaluator import llm_enabled_for_watch
+ tag = _make_tag(ai=True, title='ai-group')
+ ds = _make_datastore(tags={'t1': tag})
+ watch = _make_watch(tags=['t1'])
+ watch['llm_backend_profile'] = False
+ assert llm_enabled_for_watch(watch, ds) == (True, 'ai-group')
+
+ def test_group_switch_overrides_watch_on(self):
+ from changedetectionio.llm.evaluator import llm_enabled_for_watch
+ tag = _make_tag(ai=False, title='no-ai-group')
+ ds = _make_datastore(tags={'t1': tag})
+ watch = _make_watch(tags=['t1'])
+ watch['llm_backend_profile'] = True
+ assert llm_enabled_for_watch(watch, ds) == (False, 'no-ai-group')
+
+ def test_group_leaving_it_to_each_watch_does_not_decide(self):
+ from changedetectionio.llm.evaluator import llm_enabled_for_watch
+ ds = _make_datastore(tags={'t1': _make_tag(ai=None, title='undecided-group')})
+ watch = _make_watch(tags=['t1'])
+ watch['llm_backend_profile'] = False
+ assert llm_enabled_for_watch(watch, ds) == (False, 'watch')
+
+ def test_group_without_the_key_leaves_it_to_the_watch(self):
+ """Tags predating the setting behave like "leave it to each watch"."""
+ from changedetectionio.llm.evaluator import llm_enabled_for_watch
+ ds = _make_datastore(tags={'t1': {'title': 'legacy'}})
+ watch = _make_watch(tags=['t1'])
+ watch['llm_backend_profile'] = False
+ assert llm_enabled_for_watch(watch, ds) == (False, 'watch')
+
+ def test_first_deciding_group_wins(self):
+ """An undecided group is skipped; the next group with an opinion decides."""
+ from changedetectionio.llm.evaluator import llm_enabled_for_watch
+ ds = _make_datastore(tags={
+ 'a': _make_tag(ai=None, title='undecided-group'),
+ 'b': _make_tag(ai=False, title='no-ai-group'),
+ })
+ watch = _make_watch(tags=['a', 'b'])
+ assert llm_enabled_for_watch(watch, ds) == (False, 'no-ai-group')
+
+
# ---------------------------------------------------------------------------
# get_llm_config
# ---------------------------------------------------------------------------
@@ -386,7 +507,7 @@ class TestTokenBudget:
class TestResolveLlmField:
def test_watch_value_takes_priority(self):
from changedetectionio.llm.evaluator import resolve_llm_field
- tag = {'title': 'mygroup', 'llm_change_summary': 'tag summary prompt'}
+ tag = _make_tag(title='mygroup', llm_change_summary='tag summary prompt')
ds = _make_datastore(tags={'tag-1': tag})
watch = _make_watch(llm_change_summary='watch summary prompt', tags=['tag-1'])
value, source = resolve_llm_field(watch, ds, 'llm_change_summary')
@@ -395,7 +516,7 @@ class TestResolveLlmField:
def test_tag_value_used_when_watch_empty(self):
from changedetectionio.llm.evaluator import resolve_llm_field
- tag = {'title': 'events-group', 'llm_change_summary': 'list new events'}
+ tag = _make_tag(title='events-group', llm_change_summary='list new events')
ds = _make_datastore(tags={'tag-1': tag})
watch = _make_watch(llm_change_summary='', tags=['tag-1'])
value, source = resolve_llm_field(watch, ds, 'llm_change_summary')
@@ -413,7 +534,7 @@ class TestResolveLlmField:
def test_works_for_llm_intent_field_too(self):
"""resolve_llm_field is generic — works for llm_intent same as llm_change_summary."""
from changedetectionio.llm.evaluator import resolve_llm_field
- tag = {'title': 'grp', 'llm_intent': 'flag price drops'}
+ tag = _make_tag(llm_intent='flag price drops')
ds = _make_datastore(tags={'t1': tag})
watch = _make_watch(llm_intent='', tags=['t1'])
value, source = resolve_llm_field(watch, ds, 'llm_intent')
@@ -469,7 +590,7 @@ class TestSummariseChange:
def test_cascades_from_tag(self):
"""llm_change_summary on a tag propagates to watches in that tag."""
from changedetectionio.llm.evaluator import summarise_change
- tag = {'title': 'events', 'llm_change_summary': 'Translate events to English'}
+ tag = _make_tag(title='events', llm_change_summary='Translate events to English')
ds = _make_datastore(llm_cfg={'model': 'gpt-4o-mini'}, tags={'tag-1': tag})
watch = _make_watch(llm_change_summary='', tags=['tag-1'])
with patch('changedetectionio.llm.client.completion',
@@ -552,7 +673,7 @@ class TestSummaryCacheKey:
def test_get_effective_prompt_cascades_from_tag(self):
from changedetectionio.llm.evaluator import get_effective_summary_prompt
- tag = {'title': 'grp', 'llm_change_summary': 'tag-level prompt'}
+ tag = _make_tag(llm_change_summary='tag-level prompt')
ds = _make_datastore(tags={'t1': tag})
watch = _make_watch(llm_change_summary='', tags=['t1'])
assert get_effective_summary_prompt(watch, ds) == 'tag-level prompt'
@@ -592,7 +713,7 @@ class TestSummaryPromptAppendMode:
def test_watch_append_targets_the_tag_prompt_when_a_tag_supplies_one(self):
"""The watch appends to what it would otherwise have inherited — here the tag."""
from changedetectionio.llm.evaluator import get_effective_summary_prompt
- tag = {'title': 'grp', 'llm_change_summary': 'TAG'}
+ tag = _make_tag(llm_change_summary='TAG')
ds = _make_datastore(llm_cfg={'change_summary_default': 'GLOBAL'}, tags={'t1': tag})
watch = _make_watch(llm_change_summary='WATCH', tags=['t1'])
watch['llm_change_summary_mode'] = 'append'
@@ -600,7 +721,7 @@ class TestSummaryPromptAppendMode:
def test_tag_and_watch_can_both_append_forming_a_chain(self):
from changedetectionio.llm.evaluator import get_effective_summary_prompt
- tag = {'title': 'grp', 'llm_change_summary': 'TAG', 'llm_change_summary_mode': 'append'}
+ tag = _make_tag(llm_change_summary='TAG', llm_change_summary_mode='append')
ds = _make_datastore(llm_cfg={'change_summary_default': 'GLOBAL'}, tags={'t1': tag})
watch = _make_watch(llm_change_summary='WATCH', tags=['t1'])
watch['llm_change_summary_mode'] = 'append'
@@ -608,7 +729,7 @@ class TestSummaryPromptAppendMode:
def test_tag_appends_while_watch_replaces(self):
from changedetectionio.llm.evaluator import get_effective_summary_prompt
- tag = {'title': 'grp', 'llm_change_summary': 'TAG', 'llm_change_summary_mode': 'append'}
+ tag = _make_tag(llm_change_summary='TAG', llm_change_summary_mode='append')
ds = _make_datastore(llm_cfg={'change_summary_default': 'GLOBAL'}, tags={'t1': tag})
watch = _make_watch(llm_change_summary='WATCH', tags=['t1'])
assert get_effective_summary_prompt(watch, ds) == 'WATCH'
diff --git a/changedetectionio/tests/test_llm_change_summary.py b/changedetectionio/tests/test_llm_change_summary.py
index f094d6291..01162612d 100644
--- a/changedetectionio/tests/test_llm_change_summary.py
+++ b/changedetectionio/tests/test_llm_change_summary.py
@@ -75,9 +75,10 @@ def test_llm_change_summary_cascades_from_tag(
_set_response(datastore_path, HTML_V1)
test_url = url_for('test_endpoint', _external=True)
- # Create a tag with llm_change_summary
+ # Create a tag with llm_change_summary, AI set to On so its watches inherit it
tag_uuid = ds.add_tag('events-group')
ds.data['settings']['application']['tags'][tag_uuid]['llm_change_summary'] = 'Summarise new events'
+ ds.data['settings']['application']['tags'][tag_uuid]['llm_backend_profile'] = True
# Watch in that tag, no own summary prompt
uuid = ds.add_watch(url=test_url)
@@ -281,6 +282,7 @@ def test_tag_prompt_overrides_global_default(
tag_uuid = ds.add_tag('my-group')
ds.data['settings']['application']['tags'][tag_uuid]['llm_change_summary'] = 'Tag: bullet points.'
+ ds.data['settings']['application']['tags'][tag_uuid]['llm_backend_profile'] = True
uuid = ds.add_watch(url='https://example.com')
watch = ds.data['watching'][uuid]
@@ -306,6 +308,7 @@ def test_watch_prompt_overrides_tag_and_global(
tag_uuid = ds.add_tag('my-group')
ds.data['settings']['application']['tags'][tag_uuid]['llm_change_summary'] = 'Tag prompt.'
+ ds.data['settings']['application']['tags'][tag_uuid]['llm_backend_profile'] = True
uuid = ds.add_watch(url='https://example.com')
watch = ds.data['watching'][uuid]
diff --git a/changedetectionio/tests/test_llm_group_overrides.py b/changedetectionio/tests/test_llm_group_overrides.py
index a32c95df2..6680a02ca 100644
--- a/changedetectionio/tests/test_llm_group_overrides.py
+++ b/changedetectionio/tests/test_llm_group_overrides.py
@@ -1,18 +1,27 @@
#!/usr/bin/env python3
"""
-Tests for group/tag LLM field overrides on the watch edit page.
+Tests for the AI/LLM settings a group hands to its watches.
-When a watch's first linked tag has llm_intent or llm_change_summary set
-and the watch itself has no own value, the watch edit form should render
-the relevant textarea as readonly with a "From group '': "
-placeholder.
+A group has exactly ONE AI control on its edit page — the ternary llm_backend_profile
+("AI for watches in this group"):
-When the watch has its own value, the textarea is editable as normal.
+ On the group's llm_intent / llm_change_summary apply to every watch in
+ it (unless the watch fills in its own), and AI is on for all of them
+ Off AI off for every watch in the group; its prompts are never used
+ Leave it to each the group has no say; each watch's own AI settings apply
-The evaluator cascade (resolve_llm_field) is already tested in the
-evaluator unit tests; these tests focus on the UI and form behaviour.
+Only "On" makes anything cascade, so it decides both:
+
+ * whether the evaluator inherits the group's prompts (resolve_llm_field /
+ get_effective_summary_prompt), and
+ * whether the watch edit page shows the inherited value as a
+ "From group '': " placeholder.
+
+So every UI assertion here is paired: group On → "From group ..." visible, group Off or
+undecided → not a trace of it. On a watch the same field is a plain on/off checkbox (#4204).
"""
+import html
import json
from flask import url_for
@@ -20,6 +29,42 @@ from flask import url_for
from changedetectionio.tests.util import live_server_setup, delete_all_watches
+# The exact rendered string under test, from templates/edit/include_llm_intent.html:
+# {% set intent_placeholder = _("From group '%(name)s': %(value)s", ...) %}
+def _from_group_text(name, value):
+ return f"From group '{name}': {value}"
+
+
+def _page_text(res):
+ """Response body with HTML entities resolved, so we can match the placeholder as written."""
+ return html.unescape(res.data.decode('utf-8', errors='replace'))
+
+
+def _input_tags(body, name):
+ """Every whole tag carrying name="", in document order."""
+ tags = []
+ pos = body.find(f'name="{name}"')
+ while pos != -1:
+ start = body.rfind('', pos)
+ tags.append(body[start:end + 1])
+ pos = body.find(f'name="{name}"', end)
+ return tags
+
+
+def _input_tag(body, name):
+ """Return the first tag carrying name="", or '' if there isn't one."""
+ tags = _input_tags(body, name)
+ return tags[0] if tags else ''
+
+
+def _checkbox_is_checked(body, name):
+ """True when that checkbox renders as checked (attribute order is not guaranteed)."""
+ tag = _input_tag(body, name)
+ assert tag, f'no in the page'
+ return 'checked' in tag
+
+
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
@@ -51,14 +96,20 @@ def _api_token(client):
# Tag setup
# ---------------------------------------------------------------------------
-def _add_tag_with_llm(datastore, title, llm_intent='', llm_change_summary=''):
- """Create a tag with LLM fields set directly in the datastore."""
+def _add_tag_with_llm(datastore, title, llm_intent='', llm_change_summary='', ai=True):
+ """Create a tag with LLM fields set directly in the datastore.
+
+ `ai` is the group's single AI control (llm_backend_profile): True = On, False = Off,
+ None = leave it to each watch. Defaults to True because most cases here are about what
+ a group set to "On" hands down.
+ """
tag_uuid = datastore.add_tag(title)
tag = datastore.data['settings']['application']['tags'][tag_uuid]
if llm_intent:
tag['llm_intent'] = llm_intent
if llm_change_summary:
tag['llm_change_summary'] = llm_change_summary
+ tag['llm_backend_profile'] = ai
return tag_uuid
@@ -72,16 +123,123 @@ def _link_watch_to_tag(datastore, watch_uuid, tag_uuid):
# ---------------------------------------------------------------------------
-# Watch edit page — llm_intent group override
+# The group's one AI control — ternary on a group, checkbox on a watch
# ---------------------------------------------------------------------------
-def test_watch_edit_shows_llm_intent_placeholder_from_group(
+def test_group_edit_page_has_the_ternary_ai_control(
client, live_server, measure_memory_usage, datastore_path):
"""
- When a watch has no own llm_intent but its first tag does,
- the edit page must show "From group" + group name + group value in the
- placeholder so the user sees the inherited value but can still type to override.
- The field must NOT be readonly.
+ The group edit page must offer all three states — without them there is no way to turn
+ group-wide AI settings on, or to switch AI off for a whole group (#4204).
+ """
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+
+ tag_uuid = ds.add_tag('Ternary Group')
+
+ res = client.get(url_for('tags.form_tag_edit', uuid=tag_uuid))
+ assert res.status_code == 200
+ body = res.data.decode('utf-8', errors='replace')
+ text = _page_text(res)
+
+ assert 'name="llm_backend_profile"' in body, \
+ "group edit page is missing the 'AI for watches in this group' control"
+ for value in ('true', 'false', 'none'):
+ assert f'name="llm_backend_profile" value="{value}"' in body, \
+ f"group AI control is missing its '{value}' option"
+ assert 'AI for watches in this group' in text
+ assert 'Leave it to each watch' in text
+ # New groups start undecided, so they never touch their watches
+ assert 'id="llm_backend_profile_none" checked' in body, \
+ "a new group must default to 'Leave it to each watch'"
+
+ delete_all_watches(client)
+
+
+def test_watch_edit_page_has_a_plain_ai_checkbox(
+ client, live_server, measure_memory_usage, datastore_path):
+ """A watch gets a simple on/off, not the group's three-way choice."""
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+ api_token = _api_token(client)
+ watch_uuid = _create_watch(client, url_for('test_endpoint', _external=True), api_token)
+
+ res = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid))
+ assert res.status_code == 200
+ body = res.data.decode('utf-8', errors='replace')
+
+ assert 'name="llm_intent"' in body # AI section is rendered...
+ assert 'type="checkbox"' in _input_tag(body, 'llm_backend_profile')
+ assert 'Leave it to each watch' not in _page_text(res), \
+ "the group's three-way AI choice must not appear on a watch"
+
+ delete_all_watches(client)
+
+
+def test_group_edit_page_never_shows_the_from_group_placeholder(
+ client, live_server, measure_memory_usage, datastore_path):
+ """
+ "From group ..." describes something inherited by a watch; the group edit page is
+ where the value is authored, so it must show group-flavoured copy instead.
+ """
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+
+ tag_uuid = _add_tag_with_llm(ds, 'Authoring Group', llm_intent='Group intent value')
+
+ res = client.get(url_for('tags.form_tag_edit', uuid=tag_uuid))
+ assert res.status_code == 200
+ text = _page_text(res)
+
+ assert 'From group' not in text
+ # Group copy, not the per-watch copy (both live in the same shared include)
+ assert 'Set a change intent for all watches in this tag/group' in text
+ assert 'Describe what you care about' not in text
+
+ delete_all_watches(client)
+
+
+def test_group_edit_form_saves_and_reloads_each_ai_state(
+ client, live_server, measure_memory_usage, datastore_path):
+ """All three states round-trip through the real form, and the prompt is kept regardless."""
+ res = client.post(url_for('tags.form_tag_add'), data={'name': 'Saved Group'}, follow_redirects=True)
+ assert b'Tag added' in res.data
+
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+ tag_uuid = list(ds.data['settings']['application']['tags'].keys())[0]
+
+ for posted, expected in (('true', True), ('false', False), ('none', None)):
+ res = client.post(
+ url_for('tags.form_tag_edit_submit', uuid=tag_uuid),
+ data={'title': 'Saved Group',
+ 'llm_intent': 'Only notify me about price drops',
+ 'llm_backend_profile': posted},
+ follow_redirects=True,
+ )
+ assert b'Updated' in res.data
+ tag = ds.data['settings']['application']['tags'][tag_uuid]
+ assert tag.get('llm_backend_profile') is expected, f"posting {posted!r} should store {expected!r}"
+ # The prompt is always kept — the AI state only decides whether it is used
+ assert tag.get('llm_intent') == 'Only notify me about price drops'
+
+ # ..and the reloaded page comes back on the same option
+ body = client.get(url_for('tags.form_tag_edit', uuid=tag_uuid)).data.decode('utf-8', errors='replace')
+ assert f'id="llm_backend_profile_{posted}" checked' in body, \
+ f"saved state {posted!r} must render as the selected option"
+
+ delete_all_watches(client)
+
+
+# ---------------------------------------------------------------------------
+# Watch edit page — llm_intent group override, gated on the checkbox
+# ---------------------------------------------------------------------------
+
+def test_watch_edit_shows_llm_intent_placeholder_when_group_overrides_enabled(
+ client, live_server, measure_memory_usage, datastore_path):
+ """
+ Group override ON + watch has no own llm_intent → the edit page shows
+ "From group '': " as the placeholder, and the field stays editable.
"""
ds = client.application.config.get('DATASTORE')
_configure_llm(ds)
@@ -89,34 +247,64 @@ def test_watch_edit_shows_llm_intent_placeholder_from_group(
test_url = url_for('test_endpoint', _external=True)
watch_uuid = _create_watch(client, test_url, api_token)
- tag_uuid = _add_tag_with_llm(ds, 'Price Watchers', llm_intent='Notify only when price drops')
+ tag_uuid = _add_tag_with_llm(ds, 'Price Watchers',
+ llm_intent='Notify only when price drops',
+ ai=True)
_link_watch_to_tag(ds, watch_uuid, tag_uuid)
res = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid))
assert res.status_code == 200
- body = res.data.decode('utf-8', errors='replace')
+ text = _page_text(res)
- assert 'name="llm_intent"' in body
-
- # Placeholder must contain "From group", the tag name, and the value
- assert 'From group' in body
- assert 'Price Watchers' in body
- assert 'Notify only when price drops' in body
+ assert 'name="llm_intent"' in text
+ assert _from_group_text('Price Watchers', 'Notify only when price drops') in text, \
+ "watch edit must show the inherited group intent as a 'From group ...' placeholder"
# Field must be editable — no readonly attribute
- intent_pos = body.find('name="llm_intent"')
- snippet = body[max(0, intent_pos - 50): intent_pos + 300]
+ intent_pos = text.find('name="llm_intent"')
+ snippet = text[max(0, intent_pos - 50): intent_pos + 300]
assert 'readonly' not in snippet, \
f"llm_intent must be editable when group sets it; snippet: {snippet!r}"
delete_all_watches(client)
+def test_watch_edit_hides_llm_intent_placeholder_when_group_overrides_disabled(
+ client, live_server, measure_memory_usage, datastore_path):
+ """
+ Same group, same intent, checkbox OFF → no "From group ..." anywhere, and the
+ generic example placeholder is used instead. This is the pairing that makes the
+ checkbox meaningful.
+ """
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+ api_token = _api_token(client)
+ test_url = url_for('test_endpoint', _external=True)
+
+ watch_uuid = _create_watch(client, test_url, api_token)
+ tag_uuid = _add_tag_with_llm(ds, 'Price Watchers',
+ llm_intent='Notify only when price drops',
+ ai=False)
+ _link_watch_to_tag(ds, watch_uuid, tag_uuid)
+
+ res = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid))
+ assert res.status_code == 200
+ text = _page_text(res)
+
+ assert 'From group' not in text, \
+ "group AI settings must not leak into the watch unless the group is set to On"
+ assert 'Notify only when price drops' not in text
+ # Falls back to the normal per-watch example placeholder
+ assert 'e.g. Alert me when the price drops below $300' in text
+
+ delete_all_watches(client)
+
+
def test_watch_edit_llm_intent_shows_own_value_not_group_placeholder(
client, live_server, measure_memory_usage, datastore_path):
"""
- When a watch has its own llm_intent, the textarea body shows the watch's value
- and the placeholder does NOT say "From group" (the group value is irrelevant).
+ When the watch has its own llm_intent, the textarea body shows the watch's value
+ and the placeholder does NOT say "From group" — even with the group opted in.
"""
ds = client.application.config.get('DATASTORE')
_configure_llm(ds)
@@ -124,33 +312,32 @@ def test_watch_edit_llm_intent_shows_own_value_not_group_placeholder(
test_url = url_for('test_endpoint', _external=True)
watch_uuid = _create_watch(client, test_url, api_token)
- tag_uuid = _add_tag_with_llm(ds, 'Deals Group', llm_intent='Tag intent: notify on any deal')
+ tag_uuid = _add_tag_with_llm(ds, 'Deals Group',
+ llm_intent='Tag intent: notify on any deal',
+ ai=True)
_link_watch_to_tag(ds, watch_uuid, tag_uuid)
ds.data['watching'][watch_uuid]['llm_intent'] = 'My own watch intent'
res = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid))
assert res.status_code == 200
- body = res.data.decode('utf-8', errors='replace')
+ text = _page_text(res)
# Watch's own value in the textarea body
- assert 'My own watch intent' in body
+ assert 'My own watch intent' in text
# No group placeholder — the watch has its own value
- assert 'From group' not in body
+ assert 'From group' not in text
delete_all_watches(client)
# ---------------------------------------------------------------------------
-# Watch edit page — llm_change_summary group override
+# Watch edit page — llm_change_summary group override, gated on the checkbox
# ---------------------------------------------------------------------------
-def test_watch_edit_shows_llm_change_summary_placeholder_from_group(
+def test_watch_edit_shows_llm_change_summary_placeholder_when_group_overrides_enabled(
client, live_server, measure_memory_usage, datastore_path):
- """
- When a watch has no own llm_change_summary but its first tag does,
- the edit page shows the group value as placeholder (editable, not readonly).
- """
+ """Group override ON → the group summary prompt shows as placeholder (editable)."""
ds = client.application.config.get('DATASTORE')
_configure_llm(ds)
api_token = _api_token(client)
@@ -159,31 +346,58 @@ def test_watch_edit_shows_llm_change_summary_placeholder_from_group(
watch_uuid = _create_watch(client, test_url, api_token)
tag_uuid = _add_tag_with_llm(
ds, 'Summary Group',
- llm_change_summary='List new items as bullet points. Translate to English.'
+ llm_change_summary='List new items as bullet points. Translate to English.',
+ ai=True,
)
_link_watch_to_tag(ds, watch_uuid, tag_uuid)
res = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid))
assert res.status_code == 200
- body = res.data.decode('utf-8', errors='replace')
+ text = _page_text(res)
- assert 'Summary Group' in body
- assert 'List new items as bullet points' in body
+ assert _from_group_text('Summary Group',
+ 'List new items as bullet points. Translate to English.') in text
# Field must be editable
- summary_pos = body.find('name="llm_change_summary"')
+ summary_pos = text.find('name="llm_change_summary"')
assert summary_pos != -1
- snippet = body[max(0, summary_pos - 50): summary_pos + 300]
+ snippet = text[max(0, summary_pos - 50): summary_pos + 300]
assert 'readonly' not in snippet, \
f"llm_change_summary must be editable; snippet: {snippet!r}"
delete_all_watches(client)
+def test_watch_edit_hides_llm_change_summary_placeholder_when_group_overrides_disabled(
+ client, live_server, measure_memory_usage, datastore_path):
+ """Group override OFF → no "From group ..." for the summary prompt either."""
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+ api_token = _api_token(client)
+ test_url = url_for('test_endpoint', _external=True)
+
+ watch_uuid = _create_watch(client, test_url, api_token)
+ tag_uuid = _add_tag_with_llm(
+ ds, 'Summary Group',
+ llm_change_summary='List new items as bullet points. Translate to English.',
+ ai=False,
+ )
+ _link_watch_to_tag(ds, watch_uuid, tag_uuid)
+
+ res = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid))
+ assert res.status_code == 200
+ text = _page_text(res)
+
+ assert 'From group' not in text
+ assert 'List new items as bullet points' not in text
+
+ delete_all_watches(client)
+
+
def test_watch_edit_llm_change_summary_shows_own_value_not_group_placeholder(
client, live_server, measure_memory_usage, datastore_path):
"""
- When a watch has its own llm_change_summary, the textarea body shows the watch's
+ When the watch has its own llm_change_summary, the textarea body shows the watch's
value and no group placeholder appears.
"""
ds = client.application.config.get('DATASTORE')
@@ -192,17 +406,18 @@ def test_watch_edit_llm_change_summary_shows_own_value_not_group_placeholder(
test_url = url_for('test_endpoint', _external=True)
watch_uuid = _create_watch(client, test_url, api_token)
- tag_uuid = _add_tag_with_llm(ds, 'Summary Group', llm_change_summary='Tag summary prompt')
+ tag_uuid = _add_tag_with_llm(ds, 'Summary Group', llm_change_summary='Tag summary prompt',
+ ai=True)
_link_watch_to_tag(ds, watch_uuid, tag_uuid)
ds.data['watching'][watch_uuid]['llm_change_summary'] = 'My own summary prompt'
res = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid))
assert res.status_code == 200
- body = res.data.decode('utf-8', errors='replace')
+ text = _page_text(res)
- assert 'My own summary prompt' in body
- assert 'From group' not in body
+ assert 'My own summary prompt' in text
+ assert 'From group' not in text
delete_all_watches(client)
@@ -230,8 +445,7 @@ def test_watch_edit_no_tag_fields_are_editable(
# Neither textarea should be readonly
for field in ('llm_intent', 'llm_change_summary'):
pos = body.find(f'name="{field}"')
- if pos == -1:
- continue # field might not render if LLM section hidden for some reason
+ assert pos != -1, f"{field} textarea missing from watch edit page"
snippet = body[max(0, pos - 50): pos + 300]
assert 'readonly' not in snippet, \
f"{field} textarea must not be readonly with no tags; snippet: {snippet!r}"
@@ -242,14 +456,14 @@ def test_watch_edit_no_tag_fields_are_editable(
# ---------------------------------------------------------------------------
-# Evaluator cascade — group value used when watch has none
+# Evaluator cascade — gated on the same checkbox as the UI
# ---------------------------------------------------------------------------
-def test_resolve_llm_field_uses_tag_value_when_watch_has_none(
+def test_resolve_llm_field_uses_tag_value_when_group_overrides_enabled(
client, live_server, measure_memory_usage, datastore_path):
"""
- resolve_llm_field returns the tag's value (and tag name as source) when
- the watch has no own value.
+ resolve_llm_field returns the tag's value (and tag name as source) when the watch
+ has no own value and the group is opted in.
"""
from changedetectionio.llm.evaluator import resolve_llm_field
@@ -258,7 +472,8 @@ def test_resolve_llm_field_uses_tag_value_when_watch_has_none(
test_url = url_for('test_endpoint', _external=True)
watch_uuid = _create_watch(client, test_url, api_token)
- tag_uuid = _add_tag_with_llm(ds, 'Cascade Group', llm_intent='Group-level intent')
+ tag_uuid = _add_tag_with_llm(ds, 'Cascade Group', llm_intent='Group-level intent',
+ ai=True)
_link_watch_to_tag(ds, watch_uuid, tag_uuid)
watch = ds.data['watching'][watch_uuid]
@@ -270,10 +485,33 @@ def test_resolve_llm_field_uses_tag_value_when_watch_has_none(
delete_all_watches(client)
+def test_resolve_llm_field_ignores_tag_value_when_group_overrides_disabled(
+ client, live_server, measure_memory_usage, datastore_path):
+ """The UI hint and the evaluator agree: no opt-in, no inheritance."""
+ from changedetectionio.llm.evaluator import resolve_llm_field
+
+ ds = client.application.config.get('DATASTORE')
+ api_token = _api_token(client)
+ test_url = url_for('test_endpoint', _external=True)
+
+ watch_uuid = _create_watch(client, test_url, api_token)
+ tag_uuid = _add_tag_with_llm(ds, 'Cascade Group', llm_intent='Group-level intent',
+ ai=False)
+ _link_watch_to_tag(ds, watch_uuid, tag_uuid)
+
+ watch = ds.data['watching'][watch_uuid]
+ value, source = resolve_llm_field(watch, ds, 'llm_intent')
+
+ assert value == ''
+ assert source == ''
+
+ delete_all_watches(client)
+
+
def test_resolve_llm_field_uses_watch_value_over_tag(
client, live_server, measure_memory_usage, datastore_path):
"""
- resolve_llm_field prefers the watch's own value over the tag's.
+ resolve_llm_field prefers the watch's own value over the tag's, opted in or not.
"""
from changedetectionio.llm.evaluator import resolve_llm_field
@@ -282,7 +520,8 @@ def test_resolve_llm_field_uses_watch_value_over_tag(
test_url = url_for('test_endpoint', _external=True)
watch_uuid = _create_watch(client, test_url, api_token)
- tag_uuid = _add_tag_with_llm(ds, 'Override Group', llm_intent='Tag intent')
+ tag_uuid = _add_tag_with_llm(ds, 'Override Group', llm_intent='Tag intent',
+ ai=True)
_link_watch_to_tag(ds, watch_uuid, tag_uuid)
ds.data['watching'][watch_uuid]['llm_intent'] = 'Watch-level intent'
@@ -303,8 +542,8 @@ def test_resolve_llm_field_uses_watch_value_over_tag(
def test_watch_edit_independent_field_overrides(
client, live_server, measure_memory_usage, datastore_path):
"""
- llm_intent can come from a group (readonly) while llm_change_summary
- is editable (watch has its own), and vice versa.
+ llm_intent can be inherited from an opted-in group while llm_change_summary
+ is the watch's own value.
"""
ds = client.application.config.get('DATASTORE')
_configure_llm(ds)
@@ -316,6 +555,7 @@ def test_watch_edit_independent_field_overrides(
ds, 'Mixed Group',
llm_intent='Group intent here',
llm_change_summary='Group summary here',
+ ai=True,
)
_link_watch_to_tag(ds, watch_uuid, tag_uuid)
@@ -324,21 +564,22 @@ def test_watch_edit_independent_field_overrides(
res = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid))
assert res.status_code == 200
- body = res.data.decode('utf-8', errors='replace')
+ text = _page_text(res)
# llm_intent: group placeholder visible (watch has no own value)
- assert 'Group intent here' in body
- intent_pos = body.find('name="llm_intent"')
+ assert _from_group_text('Mixed Group', 'Group intent here') in text
+ intent_pos = text.find('name="llm_intent"')
assert intent_pos != -1
- intent_snippet = body[max(0, intent_pos - 50): intent_pos + 300]
+ intent_snippet = text[max(0, intent_pos - 50): intent_pos + 300]
assert 'readonly' not in intent_snippet, \
f"llm_intent must be editable even when group sets it; snippet: {intent_snippet!r}"
- # llm_change_summary: watch own value shown in body, no group placeholder
- assert 'My own summary' in body
- summary_pos = body.find('name="llm_change_summary"')
+ # llm_change_summary: watch own value shown in body, no group placeholder for it
+ assert 'My own summary' in text
+ assert _from_group_text('Mixed Group', 'Group summary here') not in text
+ summary_pos = text.find('name="llm_change_summary"')
assert summary_pos != -1
- summary_snippet = body[max(0, summary_pos - 50): summary_pos + 300]
+ summary_snippet = text[max(0, summary_pos - 50): summary_pos + 300]
assert 'readonly' not in summary_snippet, \
f"llm_change_summary should be editable; snippet: {summary_snippet!r}"
@@ -409,7 +650,7 @@ def test_tag_edit_page_shows_prompt_mode_radio(
def test_tag_append_mode_persists_and_applies(
client, live_server, measure_memory_usage, datastore_path):
- """A group set to append adds its text to the global prompt for its watches."""
+ """An opted-in group set to append adds its text to the global prompt for its watches."""
from changedetectionio.llm.evaluator import get_effective_summary_prompt
ds = client.application.config.get('DATASTORE')
@@ -420,7 +661,8 @@ def test_tag_append_mode_persists_and_applies(
test_url = url_for('test_endpoint', _external=True)
watch_uuid = _create_watch(client, test_url, api_token)
- tag_uuid = _add_tag_with_llm(ds, 'Append Group', llm_change_summary='Group extra line.')
+ tag_uuid = _add_tag_with_llm(ds, 'Append Group', llm_change_summary='Group extra line.',
+ ai=True)
ds.data['settings']['application']['tags'][tag_uuid]['llm_change_summary_mode'] = 'append'
_link_watch_to_tag(ds, watch_uuid, tag_uuid)
@@ -428,3 +670,348 @@ def test_tag_append_mode_persists_and_applies(
assert get_effective_summary_prompt(watch, ds) == 'GLOBAL RULES\n\nGroup extra line.'
delete_all_watches(client)
+
+
+def test_tag_append_mode_ignored_when_group_overrides_disabled(
+ client, live_server, measure_memory_usage, datastore_path):
+ """Without the opt-in the group's appended text never reaches the effective prompt."""
+ from changedetectionio.llm.evaluator import get_effective_summary_prompt
+
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+ ds.data['settings']['application']['llm']['change_summary_default'] = 'GLOBAL RULES'
+
+ api_token = _api_token(client)
+ test_url = url_for('test_endpoint', _external=True)
+ watch_uuid = _create_watch(client, test_url, api_token)
+
+ tag_uuid = _add_tag_with_llm(ds, 'Append Group', llm_change_summary='Group extra line.',
+ ai=False)
+ ds.data['settings']['application']['tags'][tag_uuid]['llm_change_summary_mode'] = 'append'
+ _link_watch_to_tag(ds, watch_uuid, tag_uuid)
+
+ watch = ds.data['watching'][watch_uuid]
+ assert get_effective_summary_prompt(watch, ds) == 'GLOBAL RULES'
+
+ delete_all_watches(client)
+
+
+# ---------------------------------------------------------------------------
+# AI on/off per watch, and per group when the group overrides — #4204
+# ---------------------------------------------------------------------------
+
+def test_watch_edit_has_ai_enabled_checkbox(
+ client, live_server, measure_memory_usage, datastore_path):
+ """Every watch gets its own AI on/off switch, on by default."""
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+ api_token = _api_token(client)
+ watch_uuid = _create_watch(client, url_for('test_endpoint', _external=True), api_token)
+
+ res = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid))
+ body = res.data.decode('utf-8', errors='replace')
+
+ assert 'name="llm_backend_profile"' in body, \
+ "watch edit page is missing the AI on/off checkbox (#4204)"
+ assert _checkbox_is_checked(body, 'llm_backend_profile'), \
+ "AI should default to on for a new watch"
+ # No group involved, so no override note
+ assert 'overrides this' not in _page_text(res)
+
+ delete_all_watches(client)
+
+
+def test_group_edit_can_switch_ai_off_for_the_whole_group(
+ client, live_server, measure_memory_usage, datastore_path):
+ """The group's control has an explicit "Off for every watch" state — the #4204 ask."""
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+ tag_uuid = ds.add_tag('AI Toggle Group')
+
+ res = client.get(url_for('tags.form_tag_edit', uuid=tag_uuid))
+ assert 'Off for every watch' in _page_text(res), \
+ "group edit page cannot switch AI off for all of its watches (#4204)"
+
+ delete_all_watches(client)
+
+
+def test_group_edit_greys_out_the_prompts_unless_ai_is_on(
+ client, live_server, measure_memory_usage, datastore_path):
+ """
+ Same cue as the restock group override (#overrides_watch + toggleOpacity): the prompts
+ only mean something in the "On" state, so they are greyed out otherwise. The state
+ changes without a reload, so this pins the JS wiring rather than the opacity value.
+ """
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+ tag_uuid = ds.add_tag('Dimmed Group')
+
+ res = client.get(url_for('tags.form_tag_edit', uuid=tag_uuid))
+ body = res.data.decode('utf-8', errors='replace')
+
+ assert "toggleOpacityByRadioValue('llm_backend_profile', 'true'" in body, \
+ "group edit page lost the wiring that greys out the AI prompts"
+ # ..and the elements it drives are all present
+ for element_id in ('llm_backend_profile_true', 'change-intent-notify-me-when', 'change-summary'):
+ assert f'id="{element_id}"' in body, f"#{element_id} missing — opacity toggle would be a no-op"
+
+ delete_all_watches(client)
+
+
+def test_watch_edit_shows_which_group_decided_the_ai_switch(
+ client, live_server, measure_memory_usage, datastore_path):
+ """
+ A group that has taken the decision decides for its watches, so the watch edit page says
+ which group is in charge and what it decided — and that explanation must stay readable
+ (only the checkbox it describes is dimmed).
+ """
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+ api_token = _api_token(client)
+ watch_uuid = _create_watch(client, url_for('test_endpoint', _external=True), api_token)
+
+ tag_uuid = _add_tag_with_llm(ds, 'Tech news', ai=False)
+ _link_watch_to_tag(ds, watch_uuid, tag_uuid)
+
+ res = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid))
+ text = _page_text(res)
+ assert 'decides this: AI is OFF for every watch in that group.' in text
+
+ # The group name links to that group's edit page, straight to its AI tab
+ tag_edit_url = url_for('tags.form_tag_edit', uuid=tag_uuid)
+ assert f'Tech news' in text, \
+ "the group name in the note must link to the group's edit page"
+
+ # The note itself is not inside the dimmed wrapper
+ note_pos = text.find('decides this: AI is OFF')
+ dimmed_pos = text.find('style="opacity: 0.6;"', text.find('id="llm-ai-enabled-row"'))
+ assert dimmed_pos != -1, "the overridden checkbox should be dimmed"
+ assert text.find('
', dimmed_pos) < note_pos, \
+ "the 'Group X decides this' note must not be greyed out with the checkbox"
+
+ # Group switched to On → the note reflects that, still linked
+ ds.data['settings']['application']['tags'][tag_uuid]['llm_backend_profile'] = True
+ text = _page_text(client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid)))
+ assert 'decides this: AI is ON for every watch in that group.' in text
+ assert f'Tech news' in text
+
+ # ..and the watch's own checkbox is disabled, showing what the group decided
+ body = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid)).data.decode('utf-8', errors='replace')
+ checkbox = _input_tags(body, 'llm_backend_profile')[0]
+ assert 'disabled' in checkbox, "the group decides, so the watch's own checkbox must be disabled"
+ assert 'checked' in checkbox, "disabled checkbox must show the state the group decided (ON)"
+
+ ds.data['settings']['application']['tags'][tag_uuid]['llm_backend_profile'] = False
+ body = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid)).data.decode('utf-8', errors='replace')
+ checkbox = _input_tags(body, 'llm_backend_profile')[0]
+ assert 'disabled' in checkbox and 'checked' not in checkbox, \
+ "disabled checkbox must show the state the group decided (OFF)"
+
+ # ..and with the group leaving it to each watch, the watch is on its own again
+ ds.data['settings']['application']['tags'][tag_uuid]['llm_backend_profile'] = None
+ res = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid))
+ assert 'decides this' not in _page_text(res)
+
+ delete_all_watches(client)
+
+
+def test_watch_own_ai_switch_survives_being_overridden_by_a_group(
+ client, live_server, measure_memory_usage, datastore_path):
+ """
+ While a group decides, the watch's checkbox is disabled — and a disabled checkbox is not
+ POSTed, which for a checkbox reads as "off". Saving the watch must therefore NOT quietly
+ rewrite its own preference: it has to come back unchanged once the group stops deciding.
+ """
+ from changedetectionio.llm.evaluator import llm_enabled_for_watch
+
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+ api_token = _api_token(client)
+ test_url = url_for('test_endpoint', _external=True)
+ watch_uuid = _create_watch(client, test_url, api_token)
+
+ # Watch says AI on (the default); the group overrules it with "off"
+ tag_uuid = _add_tag_with_llm(ds, 'Deciding Group', ai=False)
+ _link_watch_to_tag(ds, watch_uuid, tag_uuid)
+ assert ds.data['watching'][watch_uuid].get('llm_backend_profile') is True
+ assert llm_enabled_for_watch(ds.data['watching'][watch_uuid], ds) == (False, 'Deciding Group')
+
+ # Save the page exactly as the browser would: the disabled checkbox sends nothing at all
+ res = client.post(
+ url_for('ui.ui_edit.edit_page', uuid=watch_uuid),
+ data={'url': test_url, 'fetch_backend': 'html_requests',
+ 'time_between_check_use_default': 'y'},
+ follow_redirects=True,
+ )
+ assert b'Updated watch' in res.data
+
+ watch = ds.data['watching'][watch_uuid]
+ assert watch.get('llm_backend_profile') is True, \
+ "saving while a group decides must not overwrite the watch's own AI preference"
+ # Not even a hand-crafted POST can write it while it isn't user-editable
+ res = client.post(
+ url_for('ui.ui_edit.edit_page', uuid=watch_uuid),
+ data={'url': test_url, 'fetch_backend': 'html_requests',
+ 'time_between_check_use_default': 'y', 'llm_backend_profile': ''},
+ follow_redirects=True,
+ )
+ assert b'Updated watch' in res.data
+ watch = ds.data['watching'][watch_uuid]
+ assert watch.get('llm_backend_profile') is True
+ # The group still wins for now...
+ assert llm_enabled_for_watch(watch, ds) == (False, 'Deciding Group')
+ # ..and when the group stops deciding, the watch's untouched preference applies again
+ ds.data['settings']['application']['tags'][tag_uuid]['llm_backend_profile'] = None
+ assert llm_enabled_for_watch(watch, ds) == (True, 'watch')
+
+ delete_all_watches(client)
+
+
+def test_watch_ai_switch_saves_via_edit_form(
+ client, live_server, measure_memory_usage, datastore_path):
+ """Turning AI off on a watch persists, and turning it back on works."""
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+ api_token = _api_token(client)
+ test_url = url_for('test_endpoint', _external=True)
+ watch_uuid = _create_watch(client, test_url, api_token)
+
+ from changedetectionio.llm.evaluator import llm_enabled_for_watch
+
+ # Unchecked checkbox is simply absent from the POST
+ res = client.post(
+ url_for('ui.ui_edit.edit_page', uuid=watch_uuid),
+ data={'url': test_url, 'fetch_backend': 'html_requests',
+ 'time_between_check_use_default': 'y'},
+ follow_redirects=True,
+ )
+ assert b'Updated watch' in res.data
+ watch = ds.data['watching'][watch_uuid]
+ assert watch.get('llm_backend_profile') is False
+ assert llm_enabled_for_watch(watch, ds) == (False, 'watch')
+
+ res = client.post(
+ url_for('ui.ui_edit.edit_page', uuid=watch_uuid),
+ data={'url': test_url, 'fetch_backend': 'html_requests',
+ 'time_between_check_use_default': 'y', 'llm_backend_profile': 'y'},
+ follow_redirects=True,
+ )
+ assert b'Updated watch' in res.data
+ watch = ds.data['watching'][watch_uuid]
+ assert watch.get('llm_backend_profile') is True
+ assert llm_enabled_for_watch(watch, ds) == (True, 'watch')
+
+ delete_all_watches(client)
+
+
+def test_group_ai_switch_saves_and_decides_for_its_watches(
+ client, live_server, measure_memory_usage, datastore_path):
+ """
+ Group form set to "Off for every watch" → every watch in the group is off, whatever the
+ watch itself says. This is the #4204 "turn AI off for a whole group" flow.
+ """
+ from changedetectionio.llm.evaluator import llm_enabled_for_watch
+
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+ api_token = _api_token(client)
+ watch_uuid = _create_watch(client, url_for('test_endpoint', _external=True), api_token)
+
+ res = client.post(url_for('tags.form_tag_add'), data={'name': 'Budget Group'}, follow_redirects=True)
+ assert b'Tag added' in res.data
+ tag_uuid = [u for u, t in ds.data['settings']['application']['tags'].items()
+ if t.get('title') == 'Budget Group'][0]
+
+ res = client.post(
+ url_for('tags.form_tag_edit_submit', uuid=tag_uuid),
+ data={'title': 'Budget Group', 'llm_backend_profile': 'false'},
+ follow_redirects=True,
+ )
+ assert b'Updated' in res.data
+ tag = ds.data['settings']['application']['tags'][tag_uuid]
+ assert tag.get('llm_backend_profile') is False
+
+ _link_watch_to_tag(ds, watch_uuid, tag_uuid)
+ watch = ds.data['watching'][watch_uuid]
+ assert watch.get('llm_backend_profile') is True # the watch itself still says "on"
+ assert llm_enabled_for_watch(watch, ds) == (False, 'Budget Group'), \
+ "a group set to Off must win over the watch's own AI switch"
+
+ delete_all_watches(client)
+
+
+def test_group_ai_state_survives_a_save_with_no_llm_configured(
+ client, live_server, measure_memory_usage, datastore_path):
+ """
+ With no provider configured the AI control isn't rendered, and an unrendered control is
+ indistinguishable from "off" in a POST — so the page must carry the saved state in a
+ hidden input, otherwise merely saving the group would switch AI off.
+ """
+ ds = client.application.config.get('DATASTORE')
+ # deliberately NOT calling _configure_llm
+
+ res = client.post(url_for('tags.form_tag_add'), data={'name': 'Unconfigured Group'}, follow_redirects=True)
+ assert b'Tag added' in res.data
+ tag_uuid = [u for u, t in ds.data['settings']['application']['tags'].items()
+ if t.get('title') == 'Unconfigured Group'][0]
+ ds.data['settings']['application']['tags'][tag_uuid]['llm_backend_profile'] = True
+
+ res = client.get(url_for('tags.form_tag_edit', uuid=tag_uuid))
+ body = res.data.decode('utf-8', errors='replace')
+ assert 'name="llm_intent"' not in body, "AI fields should not render without a provider"
+ hidden = _input_tag(body, 'llm_backend_profile')
+ assert 'type="hidden"' in hidden and 'value="true"' in hidden, \
+ "the group AI state must be preserved in a hidden input when the AI section is not rendered"
+
+ # Submit exactly what that page would send
+ res = client.post(
+ url_for('tags.form_tag_edit_submit', uuid=tag_uuid),
+ data={'title': 'Unconfigured Group', 'llm_backend_profile': 'true'},
+ follow_redirects=True,
+ )
+ assert b'Updated' in res.data
+
+ tag = ds.data['settings']['application']['tags'][tag_uuid]
+ assert tag.get('llm_backend_profile') is True, \
+ "saving with the AI section hidden must not switch AI off"
+
+ delete_all_watches(client)
+
+
+# ---------------------------------------------------------------------------
+# End-to-end through the real forms — the path the user actually clicks
+# ---------------------------------------------------------------------------
+
+def test_group_override_round_trip_through_both_forms(
+ client, live_server, measure_memory_usage, datastore_path):
+ """
+ Set the group to On with an intent via the group form, tag a watch with it, and the watch
+ edit page shows the inherited value as its placeholder. This is the flow that was broken:
+ the group had no way to switch group-wide AI settings on at all.
+ """
+ ds = client.application.config.get('DATASTORE')
+ _configure_llm(ds)
+ api_token = _api_token(client)
+ test_url = url_for('test_endpoint', _external=True)
+ watch_uuid = _create_watch(client, test_url, api_token)
+
+ res = client.post(url_for('tags.form_tag_add'), data={'name': 'E2E Group'}, follow_redirects=True)
+ assert b'Tag added' in res.data
+ tag_uuid = [u for u, t in ds.data['settings']['application']['tags'].items()
+ if t.get('title') == 'E2E Group'][0]
+
+ res = client.post(
+ url_for('tags.form_tag_edit_submit', uuid=tag_uuid),
+ data={'title': 'E2E Group',
+ 'llm_intent': 'Only tell me about stock changes',
+ 'llm_backend_profile': 'true'},
+ follow_redirects=True,
+ )
+ assert b'Updated' in res.data
+
+ _link_watch_to_tag(ds, watch_uuid, tag_uuid)
+
+ res = client.get(url_for('ui.ui_edit.edit_page', uuid=watch_uuid))
+ assert _from_group_text('E2E Group', 'Only tell me about stock changes') in _page_text(res)
+
+ delete_all_watches(client)
diff --git a/changedetectionio/translations/cs/LC_MESSAGES/messages.mo b/changedetectionio/translations/cs/LC_MESSAGES/messages.mo
index 0371ed5ee..792bac3ea 100644
Binary files a/changedetectionio/translations/cs/LC_MESSAGES/messages.mo and b/changedetectionio/translations/cs/LC_MESSAGES/messages.mo differ
diff --git a/changedetectionio/translations/cs/LC_MESSAGES/messages.po b/changedetectionio/translations/cs/LC_MESSAGES/messages.po
index 9091de16f..9120231d3 100644
--- a/changedetectionio/translations/cs/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/cs/LC_MESSAGES/messages.po
@@ -1332,6 +1332,22 @@ msgstr ""
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "Musí být hexadecimální barva, například #4f8ef7"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr ""
@@ -3181,16 +3197,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "Použít globální nastavení pro čas mezi kontrolou a plánovačem."
#: changedetectionio/forms.py
-msgid "AI Change Intent"
-msgstr "AI záměr změny"
+msgid "AI Change Intent - Notify me when.."
+msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr "AI souhrn změny"
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3201,6 +3217,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "CSS/JSONPath/JQ/xPath filtry"
@@ -4528,11 +4548,20 @@ msgid "Enter search term..."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
-msgstr "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
+msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4618,6 +4647,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr "AI"
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/de/LC_MESSAGES/messages.po b/changedetectionio/translations/de/LC_MESSAGES/messages.po
index db1286c5c..f5fdd3c6e 100644
--- a/changedetectionio/translations/de/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/de/LC_MESSAGES/messages.po
@@ -1348,6 +1348,22 @@ msgstr ""
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "Muss eine Hex-Farbe sein, zum Beispiel #4f8ef7"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr ""
@@ -3230,16 +3246,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "Verwenden Sie globale Einstellungen für die Zeit zwischen Prüfung und Planer."
#: changedetectionio/forms.py
-msgid "AI Change Intent"
+msgid "AI Change Intent - Notify me when.."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3250,6 +3266,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "CSS/xPath-Filter"
@@ -4582,11 +4602,20 @@ msgid "Enter search term..."
msgstr "Suchbegriff eingeben..."
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4672,6 +4701,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr ""
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/en_GB/LC_MESSAGES/messages.po b/changedetectionio/translations/en_GB/LC_MESSAGES/messages.po
index b6c73fe81..dda350109 100644
--- a/changedetectionio/translations/en_GB/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/en_GB/LC_MESSAGES/messages.po
@@ -1330,6 +1330,22 @@ msgstr ""
msgid "Must be a hex colour, for example #4f8ef7"
msgstr ""
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr ""
@@ -3173,16 +3189,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr ""
#: changedetectionio/forms.py
-msgid "AI Change Intent"
+msgid "AI Change Intent - Notify me when.."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3193,6 +3209,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr ""
@@ -4520,11 +4540,20 @@ msgid "Enter search term..."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4610,6 +4639,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr ""
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/en_US/LC_MESSAGES/messages.po b/changedetectionio/translations/en_US/LC_MESSAGES/messages.po
index 0a674a6fa..1c0ca7f50 100644
--- a/changedetectionio/translations/en_US/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/en_US/LC_MESSAGES/messages.po
@@ -1330,6 +1330,22 @@ msgstr ""
msgid "Must be a hex colour, for example #4f8ef7"
msgstr ""
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr ""
@@ -3173,16 +3189,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr ""
#: changedetectionio/forms.py
-msgid "AI Change Intent"
+msgid "AI Change Intent - Notify me when.."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3193,6 +3209,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr ""
@@ -4520,11 +4540,20 @@ msgid "Enter search term..."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4610,6 +4639,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr ""
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/es/LC_MESSAGES/messages.po b/changedetectionio/translations/es/LC_MESSAGES/messages.po
index c6b0db8ad..7e13c25b6 100644
--- a/changedetectionio/translations/es/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/es/LC_MESSAGES/messages.po
@@ -1368,6 +1368,22 @@ msgstr ""
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "Debe ser un color hexadecimal, por ejemplo #4f8ef7"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr ""
@@ -3246,16 +3262,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "Utilice la configuración global para el tiempo entre la verificación y el programador."
#: changedetectionio/forms.py
-msgid "AI Change Intent"
+msgid "AI Change Intent - Notify me when.."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3266,6 +3282,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "Filtros CSS/JSONPath/JQ/XPath"
@@ -4597,11 +4617,20 @@ msgid "Enter search term..."
msgstr "Introduzca el término de búsqueda..."
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4687,6 +4716,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr ""
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/fr/LC_MESSAGES/messages.po b/changedetectionio/translations/fr/LC_MESSAGES/messages.po
index 5923fc495..1dfb0cda4 100644
--- a/changedetectionio/translations/fr/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/fr/LC_MESSAGES/messages.po
@@ -1336,6 +1336,22 @@ msgstr ""
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "Doit être une couleur hexadécimale, par exemple #4f8ef7"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr ""
@@ -3186,16 +3202,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "Utilisez les paramètres globaux pour le temps entre la vérification et le programmateur."
#: changedetectionio/forms.py
-msgid "AI Change Intent"
+msgid "AI Change Intent - Notify me when.."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3206,6 +3222,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "Filtre CSS/JSONPath/JQ/XPath"
@@ -4535,11 +4555,20 @@ msgid "Enter search term..."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4625,6 +4654,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr ""
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/it/LC_MESSAGES/messages.po b/changedetectionio/translations/it/LC_MESSAGES/messages.po
index ae18711ea..640d7c0d1 100644
--- a/changedetectionio/translations/it/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/it/LC_MESSAGES/messages.po
@@ -1332,6 +1332,22 @@ msgstr ""
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "Deve essere un colore esadecimale, ad esempio #4f8ef7"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr ""
@@ -3175,16 +3191,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "Usa impostazioni globali per intervallo controlli e pianificazione."
#: changedetectionio/forms.py
-msgid "AI Change Intent"
+msgid "AI Change Intent - Notify me when.."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3195,6 +3211,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "Filtri CSS/JSONPath/JQ/XPath"
@@ -4522,11 +4542,20 @@ msgid "Enter search term..."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4612,6 +4641,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr ""
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/ja/LC_MESSAGES/messages.po b/changedetectionio/translations/ja/LC_MESSAGES/messages.po
index 96adf9586..3f947220a 100644
--- a/changedetectionio/translations/ja/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/ja/LC_MESSAGES/messages.po
@@ -1337,6 +1337,22 @@ msgstr "タグの色"
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "16進数のカラーコードを指定してください(例: #4f8ef7)"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr "タグ名"
@@ -3192,16 +3208,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "チェック間隔とスケジューラーにはグローバル設定を使用する。"
#: changedetectionio/forms.py
-msgid "AI Change Intent"
+msgid "AI Change Intent - Notify me when.."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3212,6 +3228,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "CSS/JSONPath/JQ/XPath フィルタ"
@@ -4553,11 +4573,20 @@ msgid "Enter search term..."
msgstr "検索語を入力..."
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4643,6 +4672,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr ""
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/ko/LC_MESSAGES/messages.mo b/changedetectionio/translations/ko/LC_MESSAGES/messages.mo
index 8470818a6..16c4b5a45 100644
Binary files a/changedetectionio/translations/ko/LC_MESSAGES/messages.mo and b/changedetectionio/translations/ko/LC_MESSAGES/messages.mo differ
diff --git a/changedetectionio/translations/ko/LC_MESSAGES/messages.po b/changedetectionio/translations/ko/LC_MESSAGES/messages.po
index 831d07504..b91ca21eb 100644
--- a/changedetectionio/translations/ko/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/ko/LC_MESSAGES/messages.po
@@ -1338,6 +1338,22 @@ msgstr "태그 색상"
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "16진수 색상이어야 합니다. 예: #4f8ef7"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr "태그 이름"
@@ -3183,16 +3199,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "확인 간격 및 예약 실행에 전역 설정 사용"
#: changedetectionio/forms.py
-msgid "AI Change Intent"
-msgstr "AI 판단 기준"
+msgid "AI Change Intent - Notify me when.."
+msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr "AI 변경 요약"
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3203,6 +3219,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "CSS/JSONPath/JQ/XPath 필터"
@@ -4530,12 +4550,21 @@ msgid "Enter search term..."
msgstr "검색어를 입력하세요..."
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
-msgstr "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
+msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
-msgstr "AI - 다음 경우 알림"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
+msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
msgid ""
@@ -4620,6 +4649,10 @@ msgstr "추가되거나 취소된 이벤트를 요약하세요. 최대 두 문
msgid "Describe the price change: old price, new price, percentage difference."
msgstr "가격 변경을 설명하세요: 이전 가격, 새 가격, 비율 차이."
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr "AI"
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/messages.pot b/changedetectionio/translations/messages.pot
index 3a040829f..dc3c06558 100644
--- a/changedetectionio/translations/messages.pot
+++ b/changedetectionio/translations/messages.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: changedetection.io 0.55.8\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2026-08-26 11:14+0200\n"
+"POT-Creation-Date: 2026-09-01 19:10+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -1329,6 +1329,22 @@ msgstr ""
msgid "Must be a hex colour, for example #4f8ef7"
msgstr ""
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr ""
@@ -3172,16 +3188,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr ""
#: changedetectionio/forms.py
-msgid "AI Change Intent"
+msgid "AI Change Intent - Notify me when.."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3192,6 +3208,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr ""
@@ -4519,11 +4539,20 @@ msgid "Enter search term..."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4609,6 +4638,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr ""
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/pl/LC_MESSAGES/messages.mo b/changedetectionio/translations/pl/LC_MESSAGES/messages.mo
index 467254d5a..1686f7003 100644
Binary files a/changedetectionio/translations/pl/LC_MESSAGES/messages.mo and b/changedetectionio/translations/pl/LC_MESSAGES/messages.mo differ
diff --git a/changedetectionio/translations/pl/LC_MESSAGES/messages.po b/changedetectionio/translations/pl/LC_MESSAGES/messages.po
index 452f591be..819e032f0 100644
--- a/changedetectionio/translations/pl/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/pl/LC_MESSAGES/messages.po
@@ -1451,6 +1451,22 @@ msgstr "Kolor etykiety"
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "Musi być kolorem szesnastkowym, na przykład #4f8ef7"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr "Nazwa tagu"
@@ -3332,16 +3348,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "W przypadku funkcji sprawdzania i harmonogramu należy stosować ustawienia globalne dotyczące czasu."
#: changedetectionio/forms.py
-msgid "AI Change Intent"
-msgstr "Intencja zmian AI"
+msgid "AI Change Intent - Notify me when.."
+msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr "Podsumowanie zmian AI"
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr "Jak ten prompt łączy się z odziedziczonym"
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3352,6 +3368,10 @@ msgstr "Zastąp odziedziczony prompt"
msgid "Append to the inherited prompt"
msgstr "Dołącz do odziedziczonego promptu"
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "Filtry CSS/JSONPath/JQ/XPath"
@@ -4702,12 +4722,21 @@ msgid "Enter search term..."
msgstr "Wpisz szukane hasło..."
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
-msgstr "Sztuczna inteligencja"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
+msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
-msgstr "Sztuczna inteligencja — Powiadom, gdy…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
+msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
msgid ""
@@ -4804,6 +4833,10 @@ msgstr "Podsumuj, jakie wydarzenia zostały dodane lub odwołane. Maksymalnie dw
msgid "Describe the price change: old price, new price, percentage difference."
msgstr "Opisz zmianę ceny: stara cena, nowa cena, różnica procentowa."
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr "Sztuczna inteligencja"
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/pt_BR/LC_MESSAGES/messages.po b/changedetectionio/translations/pt_BR/LC_MESSAGES/messages.po
index 161d22b34..29b4e4617 100644
--- a/changedetectionio/translations/pt_BR/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/pt_BR/LC_MESSAGES/messages.po
@@ -1355,6 +1355,22 @@ msgstr ""
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "Deve ser uma cor hexadecimal, por exemplo #4f8ef7"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr ""
@@ -3223,16 +3239,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "Usar configurações globais para o tempo entre verificações e agendador."
#: changedetectionio/forms.py
-msgid "AI Change Intent"
+msgid "AI Change Intent - Notify me when.."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3243,6 +3259,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "Filtros CSS/JSONPath/JQ/XPath"
@@ -4572,11 +4592,20 @@ msgid "Enter search term..."
msgstr "Digite o termo de busca..."
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4662,6 +4691,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr ""
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/ru/LC_MESSAGES/messages.mo b/changedetectionio/translations/ru/LC_MESSAGES/messages.mo
index c36789720..f331881ec 100644
Binary files a/changedetectionio/translations/ru/LC_MESSAGES/messages.mo and b/changedetectionio/translations/ru/LC_MESSAGES/messages.mo differ
diff --git a/changedetectionio/translations/ru/LC_MESSAGES/messages.po b/changedetectionio/translations/ru/LC_MESSAGES/messages.po
index 923ddfb98..893081494 100644
--- a/changedetectionio/translations/ru/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/ru/LC_MESSAGES/messages.po
@@ -1419,6 +1419,22 @@ msgstr "Цвет тега"
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "Должен быть шестнадцатеричный цвет, например #4f8ef7"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr "Имя тега"
@@ -3291,16 +3307,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "Используйте глобальные настройки времени между проверкой и планировщиком."
#: changedetectionio/forms.py
-msgid "AI Change Intent"
-msgstr "Изменение намерения ИИ"
+msgid "AI Change Intent - Notify me when.."
+msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr "Обзор изменений ИИ"
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3311,6 +3327,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "Фильтры CSS/JSONPath/JQ/XPath"
@@ -4650,12 +4670,21 @@ msgid "Enter search term..."
msgstr "Введите поисковый запрос..."
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
-msgstr "ИИ"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
+msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
-msgstr "AI — Сообщить, когда…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
+msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
msgid ""
@@ -4750,6 +4779,10 @@ msgstr "Подведите итог, какие события были доба
msgid "Describe the price change: old price, new price, percentage difference."
msgstr "Опишите изменение цены: старая цена, новая цена, процентная разница."
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr "ИИ"
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/tr/LC_MESSAGES/messages.po b/changedetectionio/translations/tr/LC_MESSAGES/messages.po
index d69da53fc..95f686a8c 100644
--- a/changedetectionio/translations/tr/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/tr/LC_MESSAGES/messages.po
@@ -1365,6 +1365,22 @@ msgstr ""
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "Onaltılık bir renk olmalıdır, örneğin #4f8ef7"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr ""
@@ -3226,16 +3242,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "Kontrol arası süre ve zamanlayıcı için genel ayarları kullanın."
#: changedetectionio/forms.py
-msgid "AI Change Intent"
+msgid "AI Change Intent - Notify me when.."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3246,6 +3262,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "CSS/JSONPath/JQ/XPath Filtreleri"
@@ -4577,11 +4597,20 @@ msgid "Enter search term..."
msgstr "Arama terimini girin..."
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4667,6 +4696,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr ""
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/uk/LC_MESSAGES/messages.po b/changedetectionio/translations/uk/LC_MESSAGES/messages.po
index feeeea748..f9f946ce6 100644
--- a/changedetectionio/translations/uk/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/uk/LC_MESSAGES/messages.po
@@ -1345,6 +1345,22 @@ msgstr ""
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "Має бути шістнадцятковий колір, наприклад #4f8ef7"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr ""
@@ -3205,16 +3221,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "Використовувати глобальні налаштування для часу між перевірками та планувальника."
#: changedetectionio/forms.py
-msgid "AI Change Intent"
+msgid "AI Change Intent - Notify me when.."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3225,6 +3241,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "Фільтри CSS/JSONPath/JQ/XPath"
@@ -4554,11 +4574,20 @@ msgid "Enter search term..."
msgstr "Введіть пошуковий запит..."
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4644,6 +4673,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr ""
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/zh/LC_MESSAGES/messages.po b/changedetectionio/translations/zh/LC_MESSAGES/messages.po
index 85afc8275..92f1aa2a5 100644
--- a/changedetectionio/translations/zh/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/zh/LC_MESSAGES/messages.po
@@ -1335,6 +1335,22 @@ msgstr ""
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "必须是十六进制颜色,例如 #4f8ef7"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr ""
@@ -3179,16 +3195,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "检查间隔与调度时间使用全局设置。"
#: changedetectionio/forms.py
-msgid "AI Change Intent"
+msgid "AI Change Intent - Notify me when.."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3199,6 +3215,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "CSS/JSONPath/JQ/XPath 过滤器"
@@ -4528,11 +4548,20 @@ msgid "Enter search term..."
msgstr "输入搜索关键词..."
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4618,6 +4647,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr ""
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/translations/zh_Hant_TW/LC_MESSAGES/messages.po b/changedetectionio/translations/zh_Hant_TW/LC_MESSAGES/messages.po
index a64bb70e1..2577b5484 100644
--- a/changedetectionio/translations/zh_Hant_TW/LC_MESSAGES/messages.po
+++ b/changedetectionio/translations/zh_Hant_TW/LC_MESSAGES/messages.po
@@ -1334,6 +1334,22 @@ msgstr ""
msgid "Must be a hex colour, for example #4f8ef7"
msgstr "必須是十六進位色碼,例如 #4f8ef7"
+#: changedetectionio/blueprint/tags/form.py
+msgid "AI for watches in this group"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "On, use the settings below"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Off for every watch"
+msgstr ""
+
+#: changedetectionio/blueprint/tags/form.py
+msgid "Leave it to each watch"
+msgstr ""
+
#: changedetectionio/blueprint/tags/form.py
msgid "Tag name"
msgstr ""
@@ -3179,16 +3195,16 @@ msgid "Use global settings for time between check and scheduler."
msgstr "檢查與排程時間使用全域設定。"
#: changedetectionio/forms.py
-msgid "AI Change Intent"
+msgid "AI Change Intent - Notify me when.."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html changedetectionio/blueprint/ui/templates/diff.html
-#: changedetectionio/forms.py changedetectionio/templates/edit/include_llm_intent.html
+#: changedetectionio/forms.py
msgid "AI Change Summary"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
-msgid "How this prompt combines with the inherited one"
+msgid "Change Summary prompt - Append or Replace the default?"
msgstr ""
#: changedetectionio/blueprint/tags/form.py changedetectionio/forms.py
@@ -3199,6 +3215,10 @@ msgstr ""
msgid "Append to the inherited prompt"
msgstr ""
+#: changedetectionio/forms.py
+msgid "AI enabled for this watch?"
+msgstr ""
+
#: changedetectionio/forms.py
msgid "CSS/JSONPath/JQ/XPath Filters"
msgstr "CSS / JSONPath / JQ / XPath 過濾器"
@@ -4526,11 +4546,20 @@ msgid "Enter search term..."
msgstr "輸入搜尋關鍵字 ..."
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI"
+msgid ""
+"On – every watch in this group uses the AI settings below, unless it fills in its own. "
+"Off – no AI for any watch in this group. Leave it to each watch – this "
+"group has no say; each watch uses its own AI settings."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
-msgid "AI — Notify when…"
+#, python-format
+msgid "Group %(name)s decides this: AI is ON for every watch in that group."
+msgstr ""
+
+#: changedetectionio/templates/edit/include_llm_intent.html
+#, python-format
+msgid "Group %(name)s decides this: AI is OFF for every watch in that group."
msgstr ""
#: changedetectionio/templates/edit/include_llm_intent.html
@@ -4616,6 +4645,10 @@ msgstr ""
msgid "Describe the price change: old price, new price, percentage difference."
msgstr ""
+#: changedetectionio/templates/edit/include_llm_intent.html
+msgid "AI"
+msgstr ""
+
#: changedetectionio/templates/edit/include_llm_intent.html
#, python-format
msgid ""
diff --git a/changedetectionio/worker.py b/changedetectionio/worker.py
index 52b91cbcd..e63975d2f 100644
--- a/changedetectionio/worker.py
+++ b/changedetectionio/worker.py
@@ -455,15 +455,20 @@ async def async_update_worker(worker_id, q, notification_q, app, datastore, exec
if changed_detected and watch.history_n >= 1:
try:
from changedetectionio.llm.evaluator import (
- evaluate_change, resolve_intent, resolve_llm_field,
- summarise_change, _runtime_llm_config,
+ evaluate_change, llm_enabled_for_watch, resolve_intent,
+ resolve_llm_field, summarise_change, _runtime_llm_config,
)
+ # Per-watch (or group-wide) AI on/off — #4204. Checked before
+ # any diff work so a switched-off watch costs nothing.
+ _llm_on, _llm_on_source = llm_enabled_for_watch(watch, datastore)
+ if not _llm_on:
+ logger.debug(f"LLM disabled for {uuid} (by {_llm_on_source}) — skipping AI intent/summary")
# _runtime_llm_config returns None (and logs a debug skip
# message) when the master 'llm_enabled' toggle is off, so
# the whole block — diff computation, status minitext, and
# the two executor dispatches — is skipped, not just the
# inner LLM lookups.
- _llm_cfg = _runtime_llm_config(datastore)
+ _llm_cfg = _runtime_llm_config(datastore) if _llm_on else None
if _llm_cfg:
# Compute unified diff once — used by both intent and summary
_watch_dates = list(watch.history.keys())
diff --git a/docs/api-spec.yaml b/docs/api-spec.yaml
index 2fc7e03e4..ed91e1b1e 100644
--- a/docs/api-spec.yaml
+++ b/docs/api-spec.yaml
@@ -606,6 +606,17 @@ components:
description: Logic operator - ALL (match all conditions) or ANY (match any condition)
# AI / LLM
+ llm_backend_profile:
+ type: [boolean, 'null']
+ default: true
+ description: |
+ Whether AI/LLM features are enabled. On a watch this is a plain on/off (default true).
+ On a tag/group it is ternary and is that group's only AI control:
+ - true: AI on for every watch in the group, and the group's `llm_intent` /
+ `llm_change_summary` are inherited by its watches (unless a watch sets its own)
+ - false: AI off for every watch in the group
+ - null (group default): the group has no say; each watch uses its own AI settings
+ (Reserved for selecting a named LLM profile in future - currently a plain on/off.)
llm_intent:
type: string
maxLength: 2000