From f4647a87134e4dcd196f4d079d6cf68e41f980f9 Mon Sep 17 00:00:00 2001 From: fahimreza-dev Date: Wed, 14 Oct 2026 04:46:21 +0600 Subject: [PATCH 1/3] feat(safety): Allow, Ask first or Off for each tool (#364) Ask first holds a call until the owner approves it under Needs your OK; a yes in the chat is not enough. Like rehearsal, it wraps each changing tool's execute callback at registration, so the tool's own code never runs before the approval, including tools that sometimes skip their own preview (remove-block on one block). After the approval the app's confirm runs the call once, and the tool's own preview step is answered by it. - Off is the existing single-tool switch. - A changing tool that arrives after this ships (a later update, a plugin activated later) asks first until the owner chooses (R3). Every tool that exists on the first run keeps exactly what it does. - Recommended set: one click, only tightens, turns on drafts-only. - Context names the tools that ask first. Modes are protected options. - A spent, made-up or other tool's token never reaches the tool's code. Refs #364 Co-Authored-By: Claude Opus 5.5 --- includes/admin/class-saddle-rest.php | 12 +- .../admin/class-saddle-tool-modes-rest.php | 111 +++++ includes/class-saddle-approval.php | 254 ++++++++-- includes/class-saddle-context.php | 5 + includes/class-saddle-rehearsal.php | 2 +- includes/class-saddle-settings-guard.php | 2 + includes/class-saddle-tool-modes.php | 416 ++++++++++++++++ saddle.php | 4 + tests/approvals-owner-test.php | 3 +- tests/tool-modes-test.php | 464 ++++++++++++++++++ uninstall.php | 2 + 11 files changed, 1237 insertions(+), 38 deletions(-) create mode 100644 includes/admin/class-saddle-tool-modes-rest.php create mode 100644 includes/class-saddle-tool-modes.php create mode 100644 tests/tool-modes-test.php diff --git a/includes/admin/class-saddle-rest.php b/includes/admin/class-saddle-rest.php index 9edc260a..524295fc 100644 --- a/includes/admin/class-saddle-rest.php +++ b/includes/admin/class-saddle-rest.php @@ -1072,8 +1072,9 @@ private static function category_for( $short, $name ) { * @return WP_REST_Response */ public static function get_capabilities() { - $catalog = array(); - $abilities = function_exists( 'wp_get_abilities' ) ? wp_get_abilities() : array(); + $catalog = array(); + $abilities = function_exists( 'wp_get_abilities' ) ? wp_get_abilities() : array(); + $recommended = Saddle_Tool_Modes::recommended(); foreach ( $abilities as $key => $ability ) { $name = is_string( $key ) ? $key : $ability->get_name(); @@ -1109,6 +1110,13 @@ public static function get_capabilities() { 'lane' => $lane, 'category' => self::category_for( $short, $name ), 'enabled' => Saddle_Capabilities::is_ability_enabled( $short ), + // Allow, Ask first or Off (#364). Ask first is offered only for a + // tool that changes something; `new` marks one that arrived as + // Ask first because the owner has not chosen for it yet. + 'mode' => Saddle_Tool_Modes::mode( $short ), + 'askable' => ! $readonly, + 'new' => Saddle_Tool_Modes::is_new( $short ), + 'recommended' => in_array( $short, $recommended, true ), // False when the tool cannot run on this site: its plugin is // not active, or its service has no key. Such a tool is also // left out of tools/list. diff --git a/includes/admin/class-saddle-tool-modes-rest.php b/includes/admin/class-saddle-tool-modes-rest.php new file mode 100644 index 00000000..545e52de --- /dev/null +++ b/includes/admin/class-saddle-tool-modes-rest.php @@ -0,0 +1,111 @@ + WP_REST_Server::CREATABLE, + 'callback' => array( __CLASS__, 'set_mode' ), + 'permission_callback' => array( 'Saddle_REST_Admin', 'can_manage' ), + 'args' => array( + 'tool' => array( + 'type' => 'string', + 'required' => true, + ), + 'mode' => array( + 'type' => 'string', + 'required' => true, + 'enum' => array( Saddle_Tool_Modes::ALLOW, Saddle_Tool_Modes::ASK, Saddle_Tool_Modes::OFF ), + ), + ), + ) + ); + + register_rest_route( + Saddle_REST_Admin::REST_NAMESPACE, + '/tool-modes/recommended', + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => array( __CLASS__, 'apply_recommended' ), + 'permission_callback' => array( 'Saddle_REST_Admin', 'can_manage' ), + ) + ); + } + + /** + * POST /tool-modes — set one tool's mode. + * + * @param WP_REST_Request $request The request. + * @return WP_REST_Response|WP_Error + */ + public static function set_mode( WP_REST_Request $request ) { + $tool = (string) $request['tool']; + $mode = Saddle_Tool_Modes::set_mode( $tool, (string) $request['mode'] ); + if ( is_wp_error( $mode ) ) { + return $mode; + } + + return new WP_REST_Response( + array( + 'tool' => sanitize_key( $tool ), + 'mode' => $mode, + ), + 200 + ); + } + + /** + * POST /tool-modes/recommended — apply the recommended set. It only + * tightens, so applying it twice changes nothing the second time. + * + * @return WP_REST_Response + */ + public static function apply_recommended() { + $moved = Saddle_Tool_Modes::apply_recommended(); + + if ( class_exists( 'Saddle_Log' ) ) { + Saddle_Log::record( + array( + 'action' => 'tool-modes-recommended', + 'target' => '', + 'summary' => sprintf( + /* translators: %d: number of tools. */ + _n( + 'You applied the recommended set: %d tool now asks you first, and publishing needs your OK.', + 'You applied the recommended set: %d tools now ask you first, and publishing needs your OK.', + count( $moved ), + 'saddle' + ), + count( $moved ) + ), + ) + ); + } + + return new WP_REST_Response( + array( + 'moved' => $moved, + 'drafts_only' => Saddle_Capabilities::is_drafts_only(), + ), + 200 + ); + } +} diff --git a/includes/class-saddle-approval.php b/includes/class-saddle-approval.php index bdea679b..cf2021da 100644 --- a/includes/class-saddle-approval.php +++ b/includes/class-saddle-approval.php @@ -45,10 +45,121 @@ class Saddle_Approval { const META_DECIDED_BY = '_saddle_decided_by'; const META_DECIDED_AT = '_saddle_decided_at'; const META_TIER = '_saddle_tier'; + const META_TOOL = '_saddle_tool'; const DECISION_APPROVED = 'approved'; const DECISION_REJECTED = 'rejected'; + /** + * The tool whose owner-approved call is running now, or null. Inside that + * run its own preview step is already answered (see run_approved()). + * + * @var string|null + */ + private static $approved_tool = null; + + /** + * Run a call the owner approved under Needs your OK and the app then + * confirmed (#364). A preview the tool's own code asks for on the way is + * answered by that approval, so the owner is not asked twice. + * + * @param string $tool Tool name without the namespace. + * @param callable $run The call. + * @return mixed What the call returns. + */ + public static function run_approved( $tool, callable $run ) { + $before = self::$approved_tool; + self::$approved_tool = (string) $tool; + try { + return call_user_func( $run ); + } finally { + self::$approved_tool = $before; + } + } + + /** + * The calling app's connection id (`key:`, `oauth:`), or '' + * for the owner's own browser session. + * + * @return string + */ + public static function connection() { + return self::current_connection(); + } + + /** + * The tool a live token belongs to, without spending it, or '' when no + * live token matches. + * + * @param string $token Candidate token. + * @return string + */ + public static function token_tool( $token ) { + $action = self::token_action( $token ); + if ( '' === $action ) { + return ''; + } + $query = new WP_Query( + array( + 'post_type' => self::CPT, + 'title' => self::hash_token( (string) $token ), + 'post_status' => 'publish', + 'posts_per_page' => 1, + 'fields' => 'ids', + 'no_found_rows' => true, + ) + ); + $tool = $query->posts ? (string) get_post_meta( (int) $query->posts[0], self::META_TOOL, true ) : ''; + + return '' !== $tool ? $tool : str_replace( '_', '-', $action ); + } + + /** + * The action a live token was issued for, without spending it, or ''. + * + * @param string $token Candidate token. + * @return string + */ + public static function token_action( $token ) { + $query = new WP_Query( + array( + 'post_type' => self::CPT, + 'title' => self::hash_token( (string) $token ), + 'post_status' => 'publish', + 'posts_per_page' => 1, + 'fields' => 'ids', + 'no_found_rows' => true, + 'update_post_term_cache' => false, + ) + ); + + return $query->posts ? (string) get_post_meta( (int) $query->posts[0], '_saddle_action', true ) : ''; + } + + /** + * The tool a gate call belongs to: `tool` when given, else the action + * with the older underscore spelling ("delete_post") made a tool name. + * + * @param array $args Gate arguments. + * @return string + */ + private static function tool_of( array $args ) { + $tool = isset( $args['tool'] ) && '' !== (string) $args['tool'] ? (string) $args['tool'] : (string) $args['action']; + return str_replace( '_', '-', $tool ); + } + + /** + * Whether a request waits for the owner, not the chat: its tool is set to + * Ask first and an app (not the owner's own browser) made it. + * + * @param string $tool Tool name without the namespace. + * @param string $connection Connection id, '' for a browser session. + * @return bool + */ + private static function waits_for_owner( $tool, $connection ) { + return '' !== (string) $connection && class_exists( 'Saddle_Tool_Modes' ) && Saddle_Tool_Modes::asks_first( (string) $tool ); + } + /** * Register the token-storage CPT. Hidden from every UI and export. */ @@ -111,6 +222,8 @@ public static function register_cpt() { * Needs your OK can leave out a request the app * can no longer run. Defaults to `action`. * @type array $input The ability's input (read for `confirm_token`). + * @type bool $log Optional. False when the code being run logs + * its own change. Default true. * @type callable $execute Zero-arg callable that performs the mutation * and returns the result (or WP_Error). * } @@ -138,38 +251,70 @@ public static function gate( array $args ) { if ( is_wp_error( $ok ) ) { return $ok; } - $result = call_user_func( $execute ); - - // Log the executed destructive action (not the preview). A WP_Error - // result is logged too — the executor may have partially mutated - // before failing, and a confirmed destructive call with no audit - // trail is worse than a noisy one. - if ( class_exists( 'Saddle_Log' ) ) { - $summary = isset( $args['summary'] ) ? (string) $args['summary'] : ''; - if ( is_wp_error( $result ) ) { - $summary = sprintf( - /* translators: 1: original summary, 2: error message. */ - __( '%1$s. Failed after confirmation: %2$s', 'saddle' ), - rtrim( $summary, '. ' ), - $result->get_error_message() - ); - } else { - $done = self::done_line( isset( $args['done'] ) ? $args['done'] : null, $result ); - $summary = '' !== $done ? $done : $summary; - } - Saddle_Log::record( - array( - 'action' => $action, - 'target' => $target, - 'summary' => $summary, - ) + return self::run_confirmed( $args, $action, $target, $execute ); + } + + // The owner approved this very call under Needs your OK and the app + // confirmed it (#364), so the tool's own preview step is answered. + if ( null !== self::$approved_tool && self::tool_of( $args ) === self::$approved_tool ) { + return self::run_confirmed( $args, $action, $target, $execute ); + } + + return self::preview( $args, $action, $target, $bind ); + } + + /** + * Run a confirmed action and record it. + * + * @param array $args The gate arguments. + * @param string $action Action identifier. + * @param string $target Target identifier. + * @param callable $execute The mutation. + * @return mixed The executor's result. + */ + private static function run_confirmed( array $args, $action, $target, $execute ) { + $result = call_user_func( $execute ); + + // Log the executed destructive action (not the preview). A WP_Error + // result is logged too — the executor may have partially mutated + // before failing, and a confirmed destructive call with no audit + // trail is worse than a noisy one. + if ( class_exists( 'Saddle_Log' ) && ( ! isset( $args['log'] ) || false !== $args['log'] ) ) { + $summary = isset( $args['summary'] ) ? (string) $args['summary'] : ''; + if ( is_wp_error( $result ) ) { + $summary = sprintf( + /* translators: 1: original summary, 2: error message. */ + __( '%1$s. Failed after confirmation: %2$s', 'saddle' ), + rtrim( $summary, '. ' ), + $result->get_error_message() ); + } else { + $done = self::done_line( isset( $args['done'] ) ? $args['done'] : null, $result ); + $summary = '' !== $done ? $done : $summary; } - - return $result; + Saddle_Log::record( + array( + 'action' => $action, + 'target' => $target, + 'summary' => $summary, + ) + ); } - // Dry-run path: issue a token, return a preview, mutate nothing. + return $result; + } + + /** + * The dry run: issue a token, return a preview, mutate nothing. + * + * @param array $args The gate arguments. + * @param string $action Action identifier. + * @param string $target Target identifier. + * @param string $bind Bound parameter. + * @return array|WP_Error + */ + private static function preview( array $args, $action, $target, $bind ) { + $tool = self::tool_of( $args ); $new_token = self::issue_token( $action, $target, @@ -178,12 +323,31 @@ public static function gate( array $args ) { 'summary' => isset( $args['summary'] ) ? (string) $args['summary'] : '', 'preview' => isset( $args['preview'] ) ? $args['preview'] : null, 'tier' => self::needed_tier( isset( $args['tool'] ) ? (string) $args['tool'] : $action ), + 'tool' => $tool, ) ); if ( is_wp_error( $new_token ) ) { return $new_token; } + if ( self::waits_for_owner( $tool, self::current_connection() ) ) { + return array( + 'requires_confirmation' => true, + 'needs_owner_approval' => true, + 'confirm_token' => $new_token, + 'expires_in_seconds' => self::TOKEN_TTL, + 'action' => $action, + 'summary' => isset( $args['summary'] ) ? (string) $args['summary'] : '', + 'preview' => isset( $args['preview'] ) ? $args['preview'] : null, + 'approve_at' => admin_url( 'admin.php?page=saddle' ), + 'instructions' => sprintf( + /* translators: %s: address of the Saddle Dashboard in wp-admin. */ + __( 'Nothing has changed. The site owner set this tool to ask them first, so it waits for their OK, not for a yes in this chat. Tell the user it is waiting under Needs your OK on the Saddle Dashboard (%s). After the owner approves it there, call this tool again with the same arguments plus "confirm_token" set to the value above. Until then that call is refused. The token is single-use and expires in 15 minutes.', 'saddle' ), + admin_url( 'admin.php?page=saddle' ) + ), + ); + } + return array( 'requires_confirmation' => true, 'confirm_token' => $new_token, @@ -265,6 +429,7 @@ private static function issue_token( $action, $target = '', $bind = '', array $d update_post_meta( $post_id, self::META_PREVIEW, wp_slash( false === $preview ? 'null' : $preview ) ); update_post_meta( $post_id, self::META_DECISION, '' ); update_post_meta( $post_id, self::META_TIER, isset( $detail['tier'] ) ? (string) $detail['tier'] : '' ); + update_post_meta( $post_id, self::META_TOOL, isset( $detail['tool'] ) ? (string) $detail['tool'] : '' ); return $token; } @@ -411,6 +576,21 @@ public static function consume_token( $token, $action, $target = '', $bind = '', ); } + // A tool set to Ask first runs only on the owner's word (#364). Checked + // before the token is spent, so the same token works once they approve. + $stored_tool = (string) get_post_meta( $post_id, self::META_TOOL, true ); + if ( '' === $decision && self::waits_for_owner( '' !== $stored_tool ? $stored_tool : str_replace( '_', '-', (string) $stored_action ), $stored_conn ) ) { + return new WP_Error( + 'saddle_awaiting_owner', + sprintf( + /* translators: %s: address of the Saddle Dashboard in wp-admin. */ + __( 'The site owner has not approved this yet. It is waiting under Needs your OK on the Saddle Dashboard (%s). Ask them to approve it there, then call again with the same token. Don’t ask for a new preview.', 'saddle' ), + admin_url( 'admin.php?page=saddle' ) + ), + array( 'status' => 403 ) + ); + } + // Spend it, and only go on if THIS request spent it. Two requests from // the owning app can both find the token above. wp_delete_post() // returns false unless its own DELETE removed the row, and the database @@ -589,7 +769,7 @@ public static function gc() { * A request whose app no longer has the access level its tool needs is * left out: approving it could not make it run. * - * @return array[] `id`, `app`, `connection`, `tool`, `target`, `summary`, + * @return array[] `id`, `app`, `connection`, `waits`, `tool`, `target`, `summary`, * `preview`, `created_at`, `expires_at`. */ public static function pending() { @@ -622,13 +802,19 @@ public static function pending() { if ( '' !== (string) get_post_meta( $post->ID, self::META_DECISION, true ) || self::out_of_reach( $post->ID ) ) { continue; } - $expires = (int) get_post_meta( $post->ID, '_saddle_expires', true ); - $preview = json_decode( (string) get_post_meta( $post->ID, self::META_PREVIEW, true ), true ); - $rows[] = array( + $expires = (int) get_post_meta( $post->ID, '_saddle_expires', true ); + $preview = json_decode( (string) get_post_meta( $post->ID, self::META_PREVIEW, true ), true ); + $connection = (string) get_post_meta( $post->ID, self::META_CONNECTION, true ); + $action = (string) get_post_meta( $post->ID, '_saddle_action', true ); + $tool = (string) get_post_meta( $post->ID, self::META_TOOL, true ); + $rows[] = array( 'id' => (int) $post->ID, 'app' => (string) get_post_meta( $post->ID, self::META_APP, true ), - 'connection' => (string) get_post_meta( $post->ID, self::META_CONNECTION, true ), - 'tool' => (string) get_post_meta( $post->ID, '_saddle_action', true ), + 'connection' => $connection, + // True when only the owner's OK lets it run (Ask first, #364); + // otherwise the app may also confirm after a yes in the chat. + 'waits' => self::waits_for_owner( '' !== $tool ? $tool : str_replace( '_', '-', $action ), $connection ), + 'tool' => $action, 'target' => (string) get_post_meta( $post->ID, '_saddle_target', true ), 'summary' => (string) get_post_meta( $post->ID, self::META_SUMMARY, true ), 'preview' => $preview, diff --git a/includes/class-saddle-context.php b/includes/class-saddle-context.php index cb33036e..f79b0bf6 100644 --- a/includes/class-saddle-context.php +++ b/includes/class-saddle-context.php @@ -136,6 +136,11 @@ public static function system_context() { if ( 'read' !== $tier ) { $lines[] = '- ' . __( 'Every change you make is recorded. If one went wrong, saddle/recall-changes lists it with an id, and saddle/undo-changes puts back what it replaced (preview first, then confirm).', 'saddle' ); + + $asking = class_exists( 'Saddle_Tool_Modes' ) ? Saddle_Tool_Modes::context_line() : ''; + if ( '' !== $asking ) { + $lines[] = '- ' . $asking; + } } foreach ( self::withheld_tools_lines() as $line ) { diff --git a/includes/class-saddle-rehearsal.php b/includes/class-saddle-rehearsal.php index b58b2ecb..6b7ec37b 100644 --- a/includes/class-saddle-rehearsal.php +++ b/includes/class-saddle-rehearsal.php @@ -101,7 +101,7 @@ public static function rehearse( $name, $input ) { * @param array $input Call input. * @return int */ - private static function target_id( array $input ) { + public static function target_id( array $input ) { foreach ( array( 'id', 'post_id', 'page_id' ) as $key ) { if ( isset( $input[ $key ] ) && is_numeric( $input[ $key ] ) ) { return (int) $input[ $key ]; diff --git a/includes/class-saddle-settings-guard.php b/includes/class-saddle-settings-guard.php index fae7af1d..3c96184b 100644 --- a/includes/class-saddle-settings-guard.php +++ b/includes/class-saddle-settings-guard.php @@ -28,6 +28,8 @@ class Saddle_Settings_Guard { 'saddle_drafts_only', 'saddle_enabled_integrations', 'saddle_disabled_abilities', + 'saddle_ask_abilities', + 'saddle_known_abilities', 'saddle_tier_domain', 'saddle_enforce_tier_domain', 'saddle_core_major_updates', diff --git a/includes/class-saddle-tool-modes.php b/includes/class-saddle-tool-modes.php new file mode 100644 index 00000000..cacf0288 --- /dev/null +++ b/includes/class-saddle-tool-modes.php @@ -0,0 +1,416 @@ +`. + */ + const ACTION_PREFIX = 'ask:'; + + /** + * Destructive tools the recommended set leaves at Allow: they change one + * part of a page, and the page's own undo puts it back. + */ + const IN_PAGE = array( 'remove-block', 'divi-remove-module', 'divi-swap-image' ); + + /** + * Hook the registration filter. Priority 5, before rehearsal's 10, so + * rehearsal wraps this one: with rehearsal on, nothing asks the owner to + * approve a change that would not be saved anyway. + */ + public static function init() { + add_filter( 'wp_register_ability_args', array( __CLASS__, 'wrap' ), 5, 2 ); + add_action( 'admin_init', array( __CLASS__, 'remember_known' ) ); + } + + /** + * Wrap a changing saddle/* tool's execute callback, and give a tool that + * asks first a `confirm_token` input if it has none. + * + * @param array $args Ability arguments. + * @param string $name Ability name. + * @return array + */ + public static function wrap( $args, $name ) { + if ( 0 !== strpos( (string) $name, 'saddle/' ) || ! is_array( $args ) || empty( $args['execute_callback'] ) || ! is_callable( $args['execute_callback'] ) ) { + return $args; + } + if ( ! empty( $args['meta']['annotations']['readonly'] ) ) { + return $args; + } + + $short = substr( (string) $name, strlen( 'saddle/' ) ); + + // The schema is built once per request, so it can follow the mode. The + // mode itself is read again on every call. + if ( self::asks_first_raw( $short ) && isset( $args['input_schema']['properties'] ) && is_array( $args['input_schema']['properties'] ) + && ! isset( $args['input_schema']['properties']['confirm_token'] ) ) { + $args['input_schema']['properties']['confirm_token'] = array( + 'type' => 'string', + 'description' => __( 'Only after the site owner approved this request: the token its preview returned.', 'saddle' ), + ); + } + + $inner = $args['execute_callback']; + $args['execute_callback'] = static function () use ( $inner, $short ) { + return self::run( $short, $inner, func_get_args() ); + }; + return $args; + } + + /** + * Run a call, or hold it for the owner when its tool asks first. + * + * @param string $short Tool name without the namespace. + * @param callable $inner The tool's own execute callback. + * @param array $call The arguments it was called with. + * @return mixed + */ + public static function run( $short, $inner, array $call ) { + // The owner's own browser session is never asked. + if ( ! self::asks_first( $short ) || '' === Saddle_Approval::connection() ) { + return call_user_func_array( $inner, $call ); + } + + $input = ( $call && is_array( $call[0] ) ) ? $call[0] : array(); + $token = ( isset( $input['confirm_token'] ) && is_string( $input['confirm_token'] ) ) ? trim( $input['confirm_token'] ) : ''; + + // A live token from this tool's own preview goes to the tool: its gate + // checks that the owner approved it before it runs anything. Any other + // token (spent, made up, or another tool's) goes to the gate below, + // which refuses it, so it can never reach the tool's code. + if ( '' !== $token ) { + $action = Saddle_Approval::token_action( $token ); + if ( '' !== $action && 0 !== strpos( $action, self::ACTION_PREFIX ) && Saddle_Approval::token_tool( $token ) === $short ) { + return call_user_func_array( $inner, $call ); + } + } + + $clean = $input; + unset( $clean['confirm_token'] ); + $payload = $clean; + ksort( $payload ); + + return Saddle_Approval::gate( + array( + 'action' => self::ACTION_PREFIX . $short, + 'tool' => $short, + 'target' => (string) Saddle_Rehearsal::target_id( $clean ), + 'bind' => hash( 'sha256', (string) wp_json_encode( $payload ) ), + 'summary' => self::summary( $short, $clean ), + 'preview' => $clean, + 'input' => $input, + // The tool logs its own change, and the owner's approval is + // logged when it is given. + 'log' => false, + 'execute' => static function () use ( $inner, $short, $clean ) { + return Saddle_Approval::run_approved( + $short, + static function () use ( $inner, $clean ) { + return call_user_func( $inner, $clean ); + } + ); + }, + ) + ); + } + + /** + * A tool's mode right now. + * + * @param string $short Tool name without the namespace. + * @return string One of the ALLOW, ASK, OFF constants. + */ + public static function mode( $short ) { + if ( ! Saddle_Capabilities::is_ability_enabled( $short ) ) { + return self::OFF; + } + return self::asks_first( $short ) ? self::ASK : self::ALLOW; + } + + /** + * Whether a tool asks the owner first: the owner set it so, or it is new. + * + * @param string $short Tool name without the namespace. + * @return bool + */ + public static function asks_first( $short ) { + if ( ! self::is_askable( $short ) ) { + return false; + } + return in_array( $short, self::ask_list(), true ) || self::is_new( $short ); + } + + /** + * Whether a changing tool arrived after the owner last had a say on the + * list, so it asks first until they choose (R3). + * + * @param string $short Tool name without the namespace. + * @return bool + */ + public static function is_new( $short ) { + $known = get_option( self::KNOWN_OPTION, false ); + if ( ! is_array( $known ) ) { + // The first time: everything that exists now keeps what it does. + self::remember_known(); + return false; + } + return self::is_askable( $short ) && ! in_array( $short, $known, true ); + } + + /** + * Whether a tool can ask first: it is registered and changes something. + * + * @param string $short Tool name without the namespace. + * @return bool + */ + public static function is_askable( $short ) { + $name = 'saddle/' . $short; + if ( ! function_exists( 'wp_get_abilities' ) || ! isset( wp_get_abilities()[ $name ] ) ) { + return false; + } + $meta = wp_get_ability( $name )->get_meta(); + return empty( $meta['annotations']['readonly'] ); + } + + /** + * The tools the owner set to Ask first. + * + * @return string[] + */ + public static function ask_list() { + $stored = get_option( self::ASK_OPTION, array() ); + return is_array( $stored ) ? array_values( array_filter( $stored, 'is_string' ) ) : array(); + } + + /** + * Set one tool's mode. Only the owner does this, in wp-admin; no tool can + * (`Saddle_Settings_Guard`). + * + * @param string $short Tool name without the namespace. + * @param string $mode One of the ALLOW, ASK, OFF constants. + * @return string|WP_Error The tool's mode afterwards. + */ + public static function set_mode( $short, $mode ) { + $short = sanitize_key( (string) $short ); + $name = 'saddle/' . $short; + if ( '' === $short || ! function_exists( 'wp_get_abilities' ) || ! isset( wp_get_abilities()[ $name ] ) ) { + return new WP_Error( 'saddle_unknown_tool', __( 'There is no tool by that name on this site.', 'saddle' ), array( 'status' => 404 ) ); + } + if ( ! in_array( $mode, array( self::ALLOW, self::ASK, self::OFF ), true ) ) { + return new WP_Error( 'saddle_bad_mode', __( 'A tool can be set to Allow, Ask first or Off.', 'saddle' ), array( 'status' => 400 ) ); + } + if ( self::ASK === $mode && ! self::is_askable( $short ) ) { + return new WP_Error( 'saddle_bad_mode', __( 'This tool only reads, so there is nothing to ask about. It can be Allow or Off.', 'saddle' ), array( 'status' => 400 ) ); + } + + $off = Saddle_Capabilities::disabled_abilities(); + $off = self::OFF === $mode ? array_merge( $off, array( $short ) ) : array_diff( $off, array( $short ) ); + Saddle_Capabilities::set_disabled_abilities( array_values( array_unique( $off ) ) ); + + $ask = self::ask_list(); + $ask = self::ASK === $mode ? array_merge( $ask, array( $short ) ) : array_diff( $ask, array( $short ) ); + update_option( self::ASK_OPTION, array_values( array_unique( $ask ) ), true ); + + if ( self::is_askable( $short ) ) { + self::remember_known( array( $short ) ); + } + + return self::mode( $short ); + } + + /** + * Record the changing tools that exist now as known, the first time, and + * add `$more` to the list after that. + * + * @param string[] $more Tools the owner just chose a mode for. + */ + public static function remember_known( $more = array() ) { + $known = get_option( self::KNOWN_OPTION, false ); + if ( ! is_array( $known ) ) { + $known = array_keys( array_filter( self::changing_tools() ) ); + } + $next = array_values( array_unique( array_merge( $known, is_array( $more ) ? $more : array() ) ) ); + if ( false === get_option( self::KNOWN_OPTION, false ) || $next !== $known ) { + update_option( self::KNOWN_OPTION, $next, false ); + } + } + + /** + * The recommended set: Ask first for what changes the site rather than + * one page's content (every Manage-the-site tool that changes something), + * and for what deletes or overwrites, except the in-page edits. + * + * @return string[] Tool names without the namespace. + */ + public static function recommended() { + $tools = array(); + foreach ( self::changing_tools() as $short => $meta ) { + $admin = isset( $meta['saddle']['tier'] ) && 'admin' === $meta['saddle']['tier']; + $destructive = ! empty( $meta['annotations']['destructive'] ); + if ( $admin || ( $destructive && ! in_array( $short, self::IN_PAGE, true ) ) ) { + $tools[] = $short; + } + } + sort( $tools ); + return $tools; + } + + /** + * Apply the recommended set. It only tightens: a tool at Allow moves to + * Ask first, a tool already Off stays Off, and "Publishing needs my OK" + * is turned on. + * + * @return string[] The tools it moved to Ask first. + */ + public static function apply_recommended() { + $moved = array(); + foreach ( self::recommended() as $short ) { + if ( self::ALLOW === self::mode( $short ) ) { + self::set_mode( $short, self::ASK ); + $moved[] = $short; + } + } + Saddle_Capabilities::set_drafts_only( true ); + return $moved; + } + + /** + * The context line that tells an app which of its tools ask the owner + * first, or '' when none do. Names only tools it can call now, at most + * ten, to stay inside the budget every session pays. + * + * @return string + */ + public static function context_line() { + $asking = array(); + foreach ( array_keys( self::changing_tools() ) as $short ) { + if ( self::asks_first( $short ) && Saddle_Capabilities::is_callable_now( 'saddle/' . $short ) ) { + $asking[] = 'saddle-' . $short; + } + } + if ( ! $asking ) { + return ''; + } + sort( $asking ); + + $names = implode( ', ', array_slice( $asking, 0, 10 ) ); + if ( count( $asking ) > 10 ) { + /* translators: 1: comma-separated tool names, 2: how many more. */ + $names = sprintf( __( '%1$s and %2$d more', 'saddle' ), $names, count( $asking ) - 10 ); + } + + return sprintf( + /* translators: %s: tool names, such as "saddle-delete-post, saddle-update-plugin". */ + __( 'These tools wait for the site owner’s OK in wp-admin before they run: %s. Call them as usual. When a result says "needs_owner_approval", tell the user it is waiting under Needs your OK, and call again with the token only after they approve.', 'saddle' ), + $names + ); + } + + /** + * Every changing saddle/* tool, with its meta. + * + * @return array Tool name without the namespace => meta. + */ + private static function changing_tools() { + $tools = array(); + foreach ( function_exists( 'wp_get_abilities' ) ? wp_get_abilities() : array() as $key => $ability ) { + $name = is_string( $key ) ? $key : $ability->get_name(); + $meta = $ability->get_meta(); + if ( 0 === strpos( $name, 'saddle/' ) && empty( $meta['annotations']['readonly'] ) ) { + $tools[ substr( $name, strlen( 'saddle/' ) ) ] = $meta; + } + } + return $tools; + } + + /** + * Whether a tool asks first, from the stored options alone. For the + * registration filter, which runs while abilities are being registered + * and so cannot look them up. + * + * @param string $short Tool name without the namespace. + * @return bool + */ + private static function asks_first_raw( $short ) { + if ( in_array( $short, self::ask_list(), true ) ) { + return true; + } + $known = get_option( self::KNOWN_OPTION, false ); + return is_array( $known ) && ! in_array( $short, $known, true ); + } + + /** + * What the owner reads under Needs your OK, and in the log once decided: + * "Create post", "Update page #12 “About”". + * + * @param string $short Tool name without the namespace. + * @param array $input The call, without its token. + * @return string + */ + private static function summary( $short, array $input ) { + $ability = function_exists( 'wp_get_ability' ) ? wp_get_ability( 'saddle/' . $short ) : null; + $label = $ability ? $ability->get_label() : $short; + $id = Saddle_Rehearsal::target_id( $input ); + $post = $id ? get_post( $id ) : null; + + if ( $post && current_user_can( 'read_post', $id ) && '' !== $post->post_title ) { + /* translators: 1: tool name, such as Update page, 2: id, 3: title. */ + return sprintf( __( '%1$s #%2$d “%3$s”', 'saddle' ), $label, $id, $post->post_title ); + } + if ( $id ) { + /* translators: 1: tool name, such as Update page, 2: id. */ + return sprintf( __( '%1$s #%2$d', 'saddle' ), $label, $id ); + } + return $label; + } +} diff --git a/saddle.php b/saddle.php index 7146154a..60aafd50 100644 --- a/saddle.php +++ b/saddle.php @@ -119,6 +119,7 @@ static function () { require_once SADDLE_DIR . 'includes/class-saddle-undo.php'; require_once SADDLE_DIR . 'includes/class-saddle-undo-steps.php'; require_once SADDLE_DIR . 'includes/class-saddle-rehearsal.php'; +require_once SADDLE_DIR . 'includes/class-saddle-tool-modes.php'; require_once SADDLE_DIR . 'includes/class-saddle-post-types.php'; require_once SADDLE_DIR . 'includes/class-saddle-update-runner.php'; require_once SADDLE_DIR . 'includes/class-saddle-install-runner.php'; @@ -179,6 +180,7 @@ static function () { require_once SADDLE_DIR . 'includes/admin/class-saddle-notices-admin.php'; require_once SADDLE_DIR . 'includes/admin/class-saddle-connections-rest.php'; require_once SADDLE_DIR . 'includes/admin/class-saddle-approvals-rest.php'; +require_once SADDLE_DIR . 'includes/admin/class-saddle-tool-modes-rest.php'; require_once SADDLE_DIR . 'includes/admin/class-saddle-services-rest.php'; require_once SADDLE_DIR . 'includes/admin/class-saddle-onboarding-rest.php'; require_once SADDLE_DIR . 'includes/admin/class-saddle-modules-rest.php'; @@ -309,6 +311,7 @@ public static function init() { Saddle_Access::register(); add_action( 'rest_api_init', array( 'Saddle_Connections_REST', 'register_routes' ) ); add_action( 'rest_api_init', array( 'Saddle_Approvals_REST', 'register_routes' ) ); + add_action( 'rest_api_init', array( 'Saddle_Tool_Modes_REST', 'register_routes' ) ); add_action( 'rest_api_init', array( 'Saddle_Services_REST', 'register_routes' ) ); add_action( 'rest_api_init', array( 'Saddle_Modules_REST', 'register_routes' ) ); @@ -327,6 +330,7 @@ public static function init() { // Rehearsal mode wraps every write tool at registration, so it has to // be hooked before wp_abilities_api_init fires. Saddle_Rehearsal::init(); + Saddle_Tool_Modes::init(); // The MCP surface and abilities require core's Abilities API (WP 6.9+). if ( self::abilities_api_available() ) { diff --git a/tests/approvals-owner-test.php b/tests/approvals-owner-test.php index 293a459c..316d3c5b 100644 --- a/tests/approvals-owner-test.php +++ b/tests/approvals-owner-test.php @@ -149,7 +149,8 @@ public function test_pending_approval_is_recorded_and_listed() { $this->assertSame( 'Publish “Spring sale”', $row['summary'] ); $this->assertSame( 'publish', $row['preview']['after']['status'] ); $this->assertSame( $row['created_at'] + Saddle_Approval::TOKEN_TTL, $row['expires_at'] ); - $this->assertEqualsCanonicalizing( array( 'id', 'app', 'connection', 'tool', 'target', 'summary', 'preview', 'created_at', 'expires_at' ), array_keys( $row ) ); + $this->assertFalse( $row['waits'], 'A tool at Allow can also be confirmed after a yes in the chat.' ); + $this->assertEqualsCanonicalizing( array( 'id', 'app', 'connection', 'waits', 'tool', 'target', 'summary', 'preview', 'created_at', 'expires_at' ), array_keys( $row ) ); } public function test_list_is_newest_first_and_admin_only() { diff --git a/tests/tool-modes-test.php b/tests/tool-modes-test.php new file mode 100644 index 00000000..85aebe21 --- /dev/null +++ b/tests/tool-modes-test.php @@ -0,0 +1,464 @@ +admin = self::factory()->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $this->admin ); + add_filter( 'wp_is_application_passwords_available', '__return_true' ); + unset( $GLOBALS['wp_rest_application_password_uuid'] ); + } + + public function tear_down() { + remove_filter( 'wp_is_application_passwords_available', '__return_true' ); + unset( $GLOBALS['wp_rest_application_password_uuid'] ); + delete_option( Saddle_Connections::OPTION ); + if ( wp_has_ability( 'saddle/test-arrived-later' ) ) { + global $wp_current_filter; + $wp_current_filter[] = 'wp_abilities_api_init'; + wp_unregister_ability( 'saddle/test-arrived-later' ); + array_pop( $wp_current_filter ); + } + wp_set_current_user( 0 ); + parent::tear_down(); + } + + /** + * A connected app: a Saddle key with a role, used for the calls after it. + */ + private function app( $role = 'write', $label = 'Claude Code' ) { + list( , $item ) = WP_Application_Passwords::create_new_application_password( $this->admin, array( 'name' => Saddle_REST_Admin::CLIENT_PREFIX . $label ) ); + Saddle_Access::set_role( 'key:' . $item['uuid'], $role ); + $GLOBALS['wp_rest_application_password_uuid'] = $item['uuid']; + + return $item['uuid']; + } + + private function call( $tool, array $input ) { + return wp_get_ability( 'saddle/' . $tool )->execute( $input ); + } + + private function owner_decides( $decision ) { + $uuid = isset( $GLOBALS['wp_rest_application_password_uuid'] ) ? $GLOBALS['wp_rest_application_password_uuid'] : null; + unset( $GLOBALS['wp_rest_application_password_uuid'] ); // The owner is in the browser. + + $pending = Saddle_Approval::pending(); + $this->assertCount( 1, $pending, 'One request waits under Needs your OK.' ); + $this->assertTrue( $pending[0]['waits'], 'It waits for the owner, not the chat.' ); + + $request = new WP_REST_Request( 'POST', '/saddle/v1/approvals/' . $pending[0]['id'] ); + $request->set_param( 'decision', $decision ); + $this->assertSame( 200, rest_do_request( $request )->get_status() ); + + $GLOBALS['wp_rest_application_password_uuid'] = $uuid; + return $pending[0]; + } + + private function posts_titled( $title ) { + return get_posts( + array( + 'title' => $title, + 'post_type' => 'post', + 'post_status' => 'any', + 'fields' => 'ids', + ) + ); + } + + /* -------- R3: nothing changes for a site that is updated -------- */ + + public function test_a_site_updated_in_place_can_do_exactly_what_it_could_before() { + Saddle_Tool_Modes::set_mode( 'delete-media', Saddle_Tool_Modes::OFF ); + delete_option( Saddle_Tool_Modes::KNOWN_OPTION ); // As on the first request after the update. + + foreach ( array_keys( wp_get_abilities() ) as $name ) { + if ( 0 !== strpos( $name, 'saddle/' ) ) { + continue; + } + $short = substr( $name, 7 ); + $this->assertFalse( Saddle_Tool_Modes::asks_first( $short ), $short . ' must not start asking after an update.' ); + $this->assertSame( + Saddle_Capabilities::is_ability_enabled( $short ) ? Saddle_Tool_Modes::ALLOW : Saddle_Tool_Modes::OFF, + Saddle_Tool_Modes::mode( $short ), + $short . ' keeps its on or off.' + ); + } + + $this->assertSame( Saddle_Tool_Modes::OFF, Saddle_Tool_Modes::mode( 'delete-media' ) ); + $this->assertContains( 'create-post', get_option( Saddle_Tool_Modes::KNOWN_OPTION ), 'The first look records every changing tool as known.' ); + $this->assertNotContains( 'get-post', get_option( Saddle_Tool_Modes::KNOWN_OPTION ), 'Read tools never ask, so they are not listed.' ); + } + + public function test_a_fresh_install_is_read_only_and_a_new_app_starts_read_only() { + $this->assertSame( 'read', get_option( Saddle_Capabilities::OPTION, Saddle_Capabilities::DEFAULT_TIER ) ); + + list( , $item ) = WP_Application_Passwords::create_new_application_password( $this->admin, array( 'name' => Saddle_REST_Admin::CLIENT_PREFIX . 'Claude' ) ); + $this->assertSame( 'read', Saddle_Access::role_for( 'key:' . $item['uuid'] ) ); + } + + public function test_a_tool_that_arrives_later_asks_first_until_the_owner_chooses() { + Saddle_Tool_Modes::remember_known(); + + // Core allows registering only inside wp_abilities_api_init. + global $wp_current_filter; + $wp_current_filter[] = 'wp_abilities_api_init'; + wp_register_ability( + 'saddle/test-arrived-later', + array( + 'label' => 'Arrived later', + 'description' => 'A tool from a plugin activated after the update.', + 'category' => 'saddle', + 'input_schema' => array( + 'type' => 'object', + 'properties' => array( 'title' => array( 'type' => 'string' ) ), + ), + 'execute_callback' => static function () { + return array( 'ran' => true ); + }, + 'permission_callback' => Saddle_Capabilities::permission( 'write', 'edit_posts', 'test-arrived-later' ), + 'meta' => saddle_ability_meta( false, false, false, 'write' ), + ) + ); + array_pop( $wp_current_filter ); + + $this->assertTrue( Saddle_Tool_Modes::is_new( 'test-arrived-later' ) ); + $this->assertSame( Saddle_Tool_Modes::ASK, Saddle_Tool_Modes::mode( 'test-arrived-later' ) ); + + Saddle_Tool_Modes::set_mode( 'test-arrived-later', Saddle_Tool_Modes::ALLOW ); + $this->assertFalse( Saddle_Tool_Modes::is_new( 'test-arrived-later' ) ); + $this->assertSame( Saddle_Tool_Modes::ALLOW, Saddle_Tool_Modes::mode( 'test-arrived-later' ) ); + } + + /* -------- Ask first on a tool with no preview of its own -------- */ + + public function test_ask_first_holds_a_create_until_the_owner_approves_then_runs_it_once() { + Saddle_Tool_Modes::set_mode( 'create-post', Saddle_Tool_Modes::ASK ); + $this->app( 'write' ); + $input = array( + 'title' => 'Spring sale', + 'content' => 'Ten percent off.', + ); + + $preview = $this->call( 'create-post', $input ); + $this->assertTrue( $preview['requires_confirmation'] ); + $this->assertTrue( $preview['needs_owner_approval'] ); + $this->assertStringContainsString( 'Needs your OK', $preview['instructions'] ); + $this->assertEmpty( $this->posts_titled( 'Spring sale' ), 'A preview creates nothing.' ); + + // A yes in the chat is not enough. + $early = $this->call( 'create-post', $input + array( 'confirm_token' => $preview['confirm_token'] ) ); + $this->assertWPError( $early ); + $this->assertSame( 'saddle_awaiting_owner', $early->get_error_code() ); + $this->assertEmpty( $this->posts_titled( 'Spring sale' ) ); + + $row = $this->owner_decides( 'approve' ); + $this->assertStringContainsString( 'Create', $row['summary'] ); + $this->assertSame( 'Spring sale', $row['preview']['title'] ); + + // The same token works now, exactly once. + $done = $this->call( 'create-post', $input + array( 'confirm_token' => $preview['confirm_token'] ) ); + $this->assertNotWPError( $done ); + $this->assertCount( 1, $this->posts_titled( 'Spring sale' ) ); + + $again = $this->call( 'create-post', $input + array( 'confirm_token' => $preview['confirm_token'] ) ); + $this->assertWPError( $again ); + $this->assertCount( 1, $this->posts_titled( 'Spring sale' ) ); + } + + /** + * A spent, made-up or other tool's token must not slip past Ask first + * into the tool's own code (found while building #364). + */ + public function test_a_token_that_is_not_this_requests_never_reaches_the_tool() { + Saddle_Tool_Modes::set_mode( 'create-post', Saddle_Tool_Modes::ASK ); + $this->app( 'write' ); + + $made_up = $this->call( + 'create-post', + array( + 'title' => 'Sneaky', + 'confirm_token' => str_repeat( 'a', 32 ), + ) + ); + $this->assertWPError( $made_up ); + $this->assertEmpty( $this->posts_titled( 'Sneaky' ) ); + + $post = self::factory()->post->create( array( 'post_title' => 'Other' ) ); + $foreign = $this->call( 'delete-post', array( 'id' => $post ) ); + $this->assertArrayHasKey( 'confirm_token', $foreign ); + + $swapped = $this->call( + 'create-post', + array( + 'title' => 'Sneaky', + 'confirm_token' => $foreign['confirm_token'], + ) + ); + $this->assertWPError( $swapped ); + $this->assertEmpty( $this->posts_titled( 'Sneaky' ) ); + } + + public function test_a_reject_stops_the_call() { + Saddle_Tool_Modes::set_mode( 'create-post', Saddle_Tool_Modes::ASK ); + $this->app( 'write' ); + $input = array( 'title' => 'Not wanted' ); + + $preview = $this->call( 'create-post', $input ); + $this->owner_decides( 'reject' ); + + $result = $this->call( 'create-post', $input + array( 'confirm_token' => $preview['confirm_token'] ) ); + $this->assertSame( 'saddle_rejected_by_owner', $result->get_error_code() ); + $this->assertEmpty( $this->posts_titled( 'Not wanted' ) ); + } + + public function test_the_approval_covers_only_the_call_that_was_previewed() { + Saddle_Tool_Modes::set_mode( 'create-post', Saddle_Tool_Modes::ASK ); + $this->app( 'write' ); + + $preview = $this->call( 'create-post', array( 'title' => 'Draft one' ) ); + $this->owner_decides( 'approve' ); + + $swapped = $this->call( + 'create-post', + array( + 'title' => 'Something else', + 'confirm_token' => $preview['confirm_token'], + ) + ); + $this->assertSame( 'saddle_token_bind_mismatch', $swapped->get_error_code() ); + $this->assertEmpty( $this->posts_titled( 'Something else' ) ); + } + + public function test_the_owners_own_browser_session_is_not_asked() { + Saddle_Tool_Modes::set_mode( 'create-post', Saddle_Tool_Modes::ASK ); + Saddle_Capabilities::set_tier( 'write' ); + + $result = $this->call( 'create-post', array( 'title' => 'From the browser' ) ); + + $this->assertArrayNotHasKey( 'requires_confirmation', (array) $result ); + $this->assertCount( 1, $this->posts_titled( 'From the browser' ) ); + Saddle_Capabilities::set_tier( 'read' ); + } + + public function test_a_tool_at_allow_runs_as_before() { + $this->app( 'write' ); + + $result = $this->call( 'create-post', array( 'title' => 'Allowed' ) ); + + $this->assertArrayNotHasKey( 'requires_confirmation', (array) $result ); + $this->assertCount( 1, $this->posts_titled( 'Allowed' ) ); + } + + /* -------- Ask first on a tool that previews by itself -------- */ + + public function test_ask_first_on_delete_post_asks_once_and_the_approval_answers_its_own_preview() { + $post = self::factory()->post->create( array( 'post_title' => 'Old news' ) ); + Saddle_Tool_Modes::set_mode( 'delete-post', Saddle_Tool_Modes::ASK ); + $this->app( 'write' ); + + $preview = $this->call( 'delete-post', array( 'id' => $post ) ); + $this->assertTrue( $preview['needs_owner_approval'] ); + $this->assertSame( 'publish', get_post_status( $post ) ); + + $early = $this->call( + 'delete-post', + array( + 'id' => $post, + 'confirm_token' => $preview['confirm_token'], + ) + ); + $this->assertSame( 'saddle_awaiting_owner', $early->get_error_code() ); + + $row = $this->owner_decides( 'approve' ); + $this->assertStringContainsString( 'Old news', $row['summary'] ); + + $done = $this->call( + 'delete-post', + array( + 'id' => $post, + 'confirm_token' => $preview['confirm_token'], + ) + ); + $this->assertNotWPError( $done ); + $this->assertArrayNotHasKey( 'requires_confirmation', (array) $done, 'The owner is not asked a second time.' ); + $this->assertSame( 'trash', get_post_status( $post ), 'Trashed, never deleted for good without force.' ); + } + + /** + * remove-block takes out a single block at once, with no preview. Ask + * first must hold it anyway: the tool's own code does not run. + */ + public function test_ask_first_holds_a_tool_that_would_have_run_without_a_preview() { + $page = self::factory()->post->create( + array( + 'post_type' => 'page', + 'post_content' => "\n

Keep me

\n", + ) + ); + Saddle_Tool_Modes::set_mode( 'remove-block', Saddle_Tool_Modes::ASK ); + $this->app( 'write' ); + + $result = $this->call( + 'remove-block', + array( + 'post_id' => $page, + 'address' => '0', + ) + ); + + $this->assertTrue( $result['needs_owner_approval'] ); + $this->assertStringContainsString( 'Keep me', get_post( $page )->post_content ); + } + + /* -------- rehearsal, context, schema -------- */ + + public function test_rehearsal_answers_before_anything_asks_the_owner() { + Saddle_Tool_Modes::set_mode( 'create-post', Saddle_Tool_Modes::ASK ); + Saddle_Capabilities::set_rehearsal( true ); + $this->app( 'write' ); + + $result = $this->call( 'create-post', array( 'title' => 'Practice' ) ); + + $this->assertTrue( $result['rehearsal'] ); + $this->assertEmpty( Saddle_Approval::pending(), 'Nothing waits for an OK that would save nothing.' ); + Saddle_Capabilities::set_rehearsal( false ); + } + + public function test_the_context_names_the_tools_that_ask_first() { + Saddle_Tool_Modes::set_mode( 'delete-post', Saddle_Tool_Modes::ASK ); + $this->app( 'write' ); + + $context = Saddle_Context::system_context(); + + $this->assertStringContainsString( 'saddle-delete-post', $context ); + $this->assertStringContainsString( 'needs_owner_approval', $context ); + } + + public function test_the_context_says_nothing_when_no_tool_asks_first() { + $this->app( 'write' ); + + $this->assertStringNotContainsString( 'needs_owner_approval', Saddle_Context::system_context() ); + } + + public function test_a_tool_that_asks_first_gets_a_confirm_token_input() { + update_option( Saddle_Tool_Modes::ASK_OPTION, array( 'create-post' ) ); + + $args = Saddle_Tool_Modes::wrap( + array( + 'input_schema' => array( + 'type' => 'object', + 'properties' => array( 'title' => array( 'type' => 'string' ) ), + ), + 'execute_callback' => '__return_true', + 'meta' => saddle_ability_meta( false, false, false, 'write' ), + ), + 'saddle/create-post' + ); + + $this->assertArrayHasKey( 'confirm_token', $args['input_schema']['properties'] ); + + $read = Saddle_Tool_Modes::wrap( + array( + 'input_schema' => array( + 'type' => 'object', + 'properties' => array(), + ), + 'execute_callback' => '__return_true', + 'meta' => saddle_ability_meta( true, false, true, 'read' ), + ), + 'saddle/get-post' + ); + $this->assertSame( '__return_true', $read['execute_callback'], 'A read tool is not wrapped.' ); + } + + /* -------- setting modes, and the recommended set -------- */ + + public function test_set_mode_refuses_ask_first_on_a_read_tool_and_an_unknown_tool() { + $this->assertSame( 'saddle_bad_mode', Saddle_Tool_Modes::set_mode( 'get-post', Saddle_Tool_Modes::ASK )->get_error_code() ); + $this->assertSame( 'saddle_unknown_tool', Saddle_Tool_Modes::set_mode( 'no-such-tool', Saddle_Tool_Modes::ALLOW )->get_error_code() ); + $this->assertSame( Saddle_Tool_Modes::OFF, Saddle_Tool_Modes::set_mode( 'get-post', Saddle_Tool_Modes::OFF ) ); + $this->assertSame( Saddle_Tool_Modes::ALLOW, Saddle_Tool_Modes::set_mode( 'get-post', Saddle_Tool_Modes::ALLOW ) ); + } + + public function test_off_is_the_existing_switch() { + Saddle_Tool_Modes::set_mode( 'create-post', Saddle_Tool_Modes::OFF ); + + $this->assertContains( 'create-post', Saddle_Capabilities::disabled_abilities() ); + $this->assertFalse( Saddle_Capabilities::is_ability_enabled( 'create-post' ) ); + + Saddle_Tool_Modes::set_mode( 'create-post', Saddle_Tool_Modes::ASK ); + $this->assertNotContains( 'create-post', Saddle_Capabilities::disabled_abilities() ); + $this->assertSame( Saddle_Tool_Modes::ASK, Saddle_Tool_Modes::mode( 'create-post' ) ); + } + + public function test_the_recommended_set_only_tightens() { + Saddle_Tool_Modes::set_mode( 'update-plugin', Saddle_Tool_Modes::OFF ); + $recommended = Saddle_Tool_Modes::recommended(); + + foreach ( array( 'delete-post', 'delete-page', 'delete-media', 'update-plugin', 'install-plugin', 'update-core', 'activate-plugin', 'update-option', 'set-auto-update' ) as $tool ) { + $this->assertContains( $tool, $recommended ); + } + foreach ( array( 'create-post', 'update-post', 'set-blocks', 'remove-block', 'get-post', 'list-plugins' ) as $tool ) { + $this->assertNotContains( $tool, $recommended ); + } + + $moved = Saddle_Tool_Modes::apply_recommended(); + + $this->assertContains( 'delete-post', $moved ); + $this->assertSame( Saddle_Tool_Modes::ASK, Saddle_Tool_Modes::mode( 'delete-post' ) ); + $this->assertSame( Saddle_Tool_Modes::OFF, Saddle_Tool_Modes::mode( 'update-plugin' ), 'Off stays Off.' ); + $this->assertSame( Saddle_Tool_Modes::ALLOW, Saddle_Tool_Modes::mode( 'create-post' ), 'Content edits stay Allow.' ); + $this->assertTrue( Saddle_Capabilities::is_drafts_only(), 'Publishing needs the owner’s OK.' ); + + $this->assertSame( array(), Saddle_Tool_Modes::apply_recommended(), 'A second time changes nothing.' ); + } + + public function test_no_tool_can_change_a_mode() { + $this->assertTrue( Saddle_Settings_Guard::is_protected_option( Saddle_Tool_Modes::ASK_OPTION ) ); + $this->assertTrue( Saddle_Settings_Guard::is_protected_option( Saddle_Tool_Modes::KNOWN_OPTION ) ); + } + + public function test_the_rest_routes_set_a_mode_and_apply_the_set() { + $request = new WP_REST_Request( 'POST', '/saddle/v1/tool-modes' ); + $request->set_param( 'tool', 'delete-post' ); + $request->set_param( 'mode', 'ask' ); + $response = rest_do_request( $request ); + + $this->assertSame( 200, $response->get_status() ); + $this->assertSame( 'ask', $response->get_data()['mode'] ); + + $caps = rest_do_request( new WP_REST_Request( 'GET', '/saddle/v1/capabilities' ) )->get_data()['capabilities']; + $row = wp_list_filter( $caps, array( 'short' => 'delete-post' ) ); + $row = reset( $row ); + $this->assertSame( 'ask', $row['mode'] ); + $this->assertTrue( $row['askable'] ); + $this->assertTrue( $row['recommended'] ); + + $applied = rest_do_request( new WP_REST_Request( 'POST', '/saddle/v1/tool-modes/recommended' ) ); + $this->assertSame( 200, $applied->get_status() ); + $this->assertTrue( $applied->get_data()['drafts_only'] ); + + wp_set_current_user( self::factory()->user->create( array( 'role' => 'editor' ) ) ); + $this->assertSame( 403, rest_do_request( $request )->get_status() ); + } +} diff --git a/uninstall.php b/uninstall.php index 5c4f6e4a..568c7354 100644 --- a/uninstall.php +++ b/uninstall.php @@ -19,6 +19,8 @@ 'saddle_onboarded', 'saddle_user_context', // Saddle_Context::USER_OPTION. 'saddle_disabled_abilities', // Saddle_Capabilities::DISABLED_OPTION. + 'saddle_ask_abilities', // Saddle_Tool_Modes::ASK_OPTION. + 'saddle_known_abilities', // Saddle_Tool_Modes::KNOWN_OPTION. 'saddle_drafts_only', // Saddle_Capabilities::DRAFTS_ONLY_OPTION. 'saddle_rehearsal', // Saddle_Capabilities::REHEARSAL_OPTION. 'saddle_paused', // Saddle_Capabilities::PAUSED_OPTION. From f0a937a027d626e49cd9b3f4e0494d87de24727f Mon Sep 17 00:00:00 2001 From: fahimreza-dev Date: Wed, 14 Oct 2026 04:51:37 +0600 Subject: [PATCH 2/3] feat(admin): choose Allow, Ask first or Off per tool in Settings (#364) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Settings → Advanced → "What each tool may do" replaces "Turn off single tools": each tool has Allow, Ask first or Off (read tools: Allow or Off), a New tag on a tool that arrived as Ask first, and a "Recommended set" row that applies the set in one click and reloads Safety, since it turns on "Publishing needs my OK". Needs your OK says when only the owner can approve a request. Readme feature line and 1.6.0 changelog. Refs #364 Co-Authored-By: Claude Opus 5.5 --- admin/build/index.asset.php | 2 +- admin/build/index.js | 13 +- admin/build/style-index-rtl.css | 2 +- admin/build/style-index.css | 2 +- admin/src/components/NeedsYourOk.jsx | 24 +- admin/src/components/SettingsPage.jsx | 178 +++++- admin/src/style.scss | 12 + admin/src/tools-list.js | 61 ++ languages/saddle.pot | 807 +++++++++++++++----------- readme.txt | 7 +- tests/js/tools-list.test.js | 36 ++ 11 files changed, 764 insertions(+), 380 deletions(-) diff --git a/admin/build/index.asset.php b/admin/build/index.asset.php index ee621fb6..43300e65 100644 --- a/admin/build/index.asset.php +++ b/admin/build/index.asset.php @@ -1 +1 @@ - array('react', 'react-dom', 'wp-api-fetch', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '2f4f15c59a7d84460eae'); + array('react', 'react-dom', 'wp-api-fetch', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => 'c87e0341ed14c197e13a'); diff --git a/admin/build/index.js b/admin/build/index.js index 18d5bd7d..9abb666b 100644 --- a/admin/build/index.js +++ b/admin/build/index.js @@ -85,7 +85,8 @@ (0,$p._n)("%d min ago","%d min ago",a,"saddle"),a);const r=Math.floor(a/60);return(0,$p.sprintf)(/* translators: %d: hours. */ /* translators: %d: hours. */ (0,$p._n)("%d hour ago","%d hours ago",r,"saddle"),r)}function kb(e){const t=String(e.summary||"").trim(),n=e.app||(0,$p.__)("An app","saddle");if(!t)return(0,$p.sprintf)(/* translators: %s: app name. */ /* translators: %s: app name. */ (0,$p.__)("%s wants to make a change","saddle"),n);const a=/^[A-Z][a-z]/.test(t)?t.charAt(0).toLowerCase()+t.slice(1):t;return(0,$p.sprintf)(/* translators: 1: app name, 2: what it wants to do, e.g. "publish “Spring sale”". */ /* translators: 1: app name, 2: what it wants to do, e.g. "publish “Spring sale”". */ -(0,$p.__)("%1$s wants to %2$s","saddle"),n,a)}function xb(e){return String(e).replace(/[_-]+/g," ").trim()}const Mb={plugin:"plugin_name",stylesheet:"theme_name"},Nb=["id","tool","address","spec","store"];function Eb(e){const t={type:(0,$p.__)("Type","saddle"),title:(0,$p.__)("Title","saddle"),status:(0,$p.__)("Status","saddle"),current_status:(0,$p.__)("Status now","saddle"),new_status:(0,$p.__)("New status","saddle"),recoverable:(0,$p.__)("Can be restored","saddle"),changes:(0,$p.__)("What changes","saddle"),mime_type:(0,$p.__)("File type","saddle"),url:(0,$p.__)("File","saddle"),menu:(0,$p.__)("Menu","saddle"),children:(0,$p.__)("Blocks inside","saddle"),children_move_up:(0,$p.__)("Sub-items that move up","saddle"),name:(0,$p.__)("Setting","saddle"),current_value:(0,$p.__)("Value now","saddle"),new_value:(0,$p.__)("New value","saddle"),module:(0,$p.__)("Module","saddle"),items:(0,$p.__)("Items","saddle"),note:(0,$p.__)("Note","saddle"),plugin_name:(0,$p.__)("Plugin","saddle"),theme_name:(0,$p.__)("Theme","saddle"),version:(0,$p.__)("Version","saddle")};return Object.prototype.hasOwnProperty.call(t,e)?t[e]:function(e){const t=xb(e);return t.charAt(0).toUpperCase()+t.slice(1)}(e)}const Ib=()=>({publish:(0,$p.__)("Published","saddle"),draft:(0,$p.__)("Draft","saddle"),"auto-draft":(0,$p.__)("Draft","saddle"),pending:(0,$p.__)("Pending review","saddle"),private:(0,$p.__)("Private","saddle"),future:(0,$p.__)("Scheduled","saddle"),trash:(0,$p.__)("In the trash","saddle")});function Cb(e,t){if("string"==typeof t){const n={status:Ib(),current_status:Ib(),new_status:Ib(),type:{post:(0,$p.__)("Post","saddle"),page:(0,$p.__)("Page","saddle"),attachment:(0,$p.__)("Media","saddle"),plugin:(0,$p.__)("Plugin","saddle"),theme:(0,$p.__)("Theme","saddle")}}[e]||null;if(n&&Object.prototype.hasOwnProperty.call(n,t))return n[t]}return"changes"===e&&Lb(t)?Object.keys(t).map(xb).join(", "):function(e){return"boolean"==typeof e?e?(0,$p.__)("Yes","saddle"):(0,$p.__)("No","saddle"):Array.isArray(e)&&e.every(e=>"object"!=typeof e||null===e)?e.join(", "):null!==e&&"object"==typeof e?JSON.stringify(e):String(e)}(t)}function Lb(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function Sb({onChange:e}){const[t,n]=(0,l.useState)([]),[r,s]=(0,l.useState)(0),[o,i]=(0,l.useState)(null),[c,d]=(0,l.useState)(null),u=(0,l.useCallback)(()=>{tm("approvals").then(e=>n([...e.approvals||[]].sort((e,t)=>t.created_at-e.created_at||t.id-e.id))).catch(()=>n([]))},[]);(0,l.useEffect)(u,[u]);const p=(t,a)=>{s(t.id),d(null),tm(`approvals/${t.id}`,{method:"POST",data:{decision:a}}).then(()=>{i(null),n(e=>e.filter(e=>e.id!==t.id)),e&&e()}).catch(e=>{d(e.message),u()}).finally(()=>s(0))};if(!t.length&&!c)return null;const m=o?function(e){const t=function(e){var t,n;return e&&"object"==typeof e&&("before"in e||"after"in e)?{before:null!==(t=e.before)&&void 0!==t?t:null,after:null!==(n=e.after)&&void 0!==n?n:null}:null}(e);return t&&Lb(t.before)&&Lb(t.after)?[...new Set([...Object.keys(t.before),...Object.keys(t.after)])].filter(e=>!Nb.includes(e)&&JSON.stringify(t.before[e])!==JSON.stringify(t.after[e])).map(e=>{var n,a;return{label:Eb(e),before:Cb(e,null!==(n=t.before[e])&&void 0!==n?n:""),value:Cb(e,null!==(a=t.after[e])&&void 0!==a?a:"")}}):Lb(e)?Object.keys(e).filter(t=>null!==e[t]&&""!==e[t]&&!function(e,t){if(Nb.includes(t))return!0;const n=t=>Object.prototype.hasOwnProperty.call(e,t)&&null!==e[t]&&""!==e[t];return!(!Mb[t]||!n(Mb[t]))||"will_delete_permanently"===t&&n("recoverable")}(e,t)).map(t=>({label:Eb(t),value:Cb(t,e[t])})):[]}(o.preview):[],h=e=>(0,a.createElement)(a.Fragment,null,(0,a.createElement)(Jd,{variant:"secondary",size:"sm",disabled:r===e.id,onClick:()=>p(e,"reject")},(0,$p.__)("Reject","saddle")),(0,a.createElement)(Jd,{size:"sm",disabled:r===e.id,onClick:()=>p(e,"approve")},(0,$p.__)("Approve","saddle")));return(0,a.createElement)("section",{className:"saddle-needs-ok"},(0,a.createElement)(am,{title:(0,$p.__)("Needs your OK","saddle")}),c&&(0,a.createElement)(Hu,{status:"error",onDismiss:()=>d(null)},c),(0,a.createElement)(Tu,null,t.map(e=>(0,a.createElement)(Au,{key:e.id,title:kb(e),description:(0,$p.sprintf)(/* translators: %s: how long ago, e.g. "3 min ago". */ /* translators: %s: how long ago, e.g. "3 min ago". */ +(0,$p.__)("%1$s wants to %2$s","saddle"),n,a)}function xb(e){return String(e).replace(/[_-]+/g," ").trim()}const Mb={plugin:"plugin_name",stylesheet:"theme_name"},Nb=["id","tool","address","spec","store"];function Eb(e){const t={type:(0,$p.__)("Type","saddle"),title:(0,$p.__)("Title","saddle"),status:(0,$p.__)("Status","saddle"),current_status:(0,$p.__)("Status now","saddle"),new_status:(0,$p.__)("New status","saddle"),recoverable:(0,$p.__)("Can be restored","saddle"),changes:(0,$p.__)("What changes","saddle"),mime_type:(0,$p.__)("File type","saddle"),url:(0,$p.__)("File","saddle"),menu:(0,$p.__)("Menu","saddle"),children:(0,$p.__)("Blocks inside","saddle"),children_move_up:(0,$p.__)("Sub-items that move up","saddle"),name:(0,$p.__)("Setting","saddle"),current_value:(0,$p.__)("Value now","saddle"),new_value:(0,$p.__)("New value","saddle"),module:(0,$p.__)("Module","saddle"),items:(0,$p.__)("Items","saddle"),note:(0,$p.__)("Note","saddle"),plugin_name:(0,$p.__)("Plugin","saddle"),theme_name:(0,$p.__)("Theme","saddle"),version:(0,$p.__)("Version","saddle")};return Object.prototype.hasOwnProperty.call(t,e)?t[e]:function(e){const t=xb(e);return t.charAt(0).toUpperCase()+t.slice(1)}(e)}const Ib=()=>({publish:(0,$p.__)("Published","saddle"),draft:(0,$p.__)("Draft","saddle"),"auto-draft":(0,$p.__)("Draft","saddle"),pending:(0,$p.__)("Pending review","saddle"),private:(0,$p.__)("Private","saddle"),future:(0,$p.__)("Scheduled","saddle"),trash:(0,$p.__)("In the trash","saddle")});function Cb(e,t){if("string"==typeof t){const n={status:Ib(),current_status:Ib(),new_status:Ib(),type:{post:(0,$p.__)("Post","saddle"),page:(0,$p.__)("Page","saddle"),attachment:(0,$p.__)("Media","saddle"),plugin:(0,$p.__)("Plugin","saddle"),theme:(0,$p.__)("Theme","saddle")}}[e]||null;if(n&&Object.prototype.hasOwnProperty.call(n,t))return n[t]}return"changes"===e&&Lb(t)?Object.keys(t).map(xb).join(", "):function(e){return"boolean"==typeof e?e?(0,$p.__)("Yes","saddle"):(0,$p.__)("No","saddle"):Array.isArray(e)&&e.every(e=>"object"!=typeof e||null===e)?e.join(", "):null!==e&&"object"==typeof e?JSON.stringify(e):String(e)}(t)}function Lb(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function Sb({onChange:e}){const[t,n]=(0,l.useState)([]),[r,s]=(0,l.useState)(0),[o,i]=(0,l.useState)(null),[c,d]=(0,l.useState)(null),u=(0,l.useCallback)(()=>{tm("approvals").then(e=>n([...e.approvals||[]].sort((e,t)=>t.created_at-e.created_at||t.id-e.id))).catch(()=>n([]))},[]);(0,l.useEffect)(u,[u]);const p=(t,a)=>{s(t.id),d(null),tm(`approvals/${t.id}`,{method:"POST",data:{decision:a}}).then(()=>{i(null),n(e=>e.filter(e=>e.id!==t.id)),e&&e()}).catch(e=>{d(e.message),u()}).finally(()=>s(0))};if(!t.length&&!c)return null;const m=o?function(e){const t=function(e){var t,n;return e&&"object"==typeof e&&("before"in e||"after"in e)?{before:null!==(t=e.before)&&void 0!==t?t:null,after:null!==(n=e.after)&&void 0!==n?n:null}:null}(e);return t&&Lb(t.before)&&Lb(t.after)?[...new Set([...Object.keys(t.before),...Object.keys(t.after)])].filter(e=>!Nb.includes(e)&&JSON.stringify(t.before[e])!==JSON.stringify(t.after[e])).map(e=>{var n,a;return{label:Eb(e),before:Cb(e,null!==(n=t.before[e])&&void 0!==n?n:""),value:Cb(e,null!==(a=t.after[e])&&void 0!==a?a:"")}}):Lb(e)?Object.keys(e).filter(t=>null!==e[t]&&""!==e[t]&&!function(e,t){if(Nb.includes(t))return!0;const n=t=>Object.prototype.hasOwnProperty.call(e,t)&&null!==e[t]&&""!==e[t];return!(!Mb[t]||!n(Mb[t]))||"will_delete_permanently"===t&&n("recoverable")}(e,t)).map(t=>({label:Eb(t),value:Cb(t,e[t])})):[]}(o.preview):[],h=e=>(0,a.createElement)(a.Fragment,null,(0,a.createElement)(Jd,{variant:"secondary",size:"sm",disabled:r===e.id,onClick:()=>p(e,"reject")},(0,$p.__)("Reject","saddle")),(0,a.createElement)(Jd,{size:"sm",disabled:r===e.id,onClick:()=>p(e,"approve")},(0,$p.__)("Approve","saddle")));return(0,a.createElement)("section",{className:"saddle-needs-ok"},(0,a.createElement)(am,{title:(0,$p.__)("Needs your OK","saddle")}),c&&(0,a.createElement)(Hu,{status:"error",onDismiss:()=>d(null)},c),(0,a.createElement)(Tu,null,t.map(e=>(0,a.createElement)(Au,{key:e.id,title:kb(e),description:e.waits?(0,$p.sprintf)(/* translators: %s: how long ago, e.g. "3 min ago". */ /* translators: %s: how long ago, e.g. "3 min ago". */ +(0,$p.__)("Asked %s · set to ask you first, so only you can approve it","saddle"),jb(e.created_at)):(0,$p.sprintf)(/* translators: %s: how long ago, e.g. "3 min ago". */ /* translators: %s: how long ago, e.g. "3 min ago". */ (0,$p.__)("Asked %s · waits until you decide","saddle"),jb(e.created_at)),actions:(0,a.createElement)(a.Fragment,null,(0,a.createElement)(Jd,{variant:"link",onClick:()=>i(e)},(0,$p.__)("Review","saddle")),h(e))}))),(0,a.createElement)(Np,{open:!!o,onOpenChange:e=>!e&&i(null),title:o?kb(o):"","aria-label":o?void 0:(0,$p.__)("Review a request","saddle"),size:"md"},o&&(0,a.createElement)("div",{className:"saddle-doc saddle-doc--bare"},m.length>0&&(0,a.createElement)(Qu,{className:"saddle-needs-ok__details",items:m.map(e=>({label:e.label,value:"before"in e?(0,$p.sprintf)(/* translators: 1: the value now, 2: the value after the change. */ /* translators: 1: the value now, 2: the value after the change. */ (0,$p.__)("%1$s → %2$s","saddle"),e.before||(0,$p.__)("(empty)","saddle"),e.value||(0,$p.__)("(empty)","saddle")):e.value}))}),(0,a.createElement)("div",{className:"saddle-needs-ok__actions"},h(o)))))}function Db({rows:e=4}){return(0,a.createElement)("div",{className:"saddle-activity__day","aria-busy":"true"},(0,a.createElement)(Ap,null,(0,$p.__)("Loading activity","saddle")),(0,a.createElement)(Mu,{className:"saddle-activity__daylabel",height:12,width:64}),(0,a.createElement)("ul",{className:"saddle-activity__list","aria-hidden":"true"},Array.from({length:e},(e,t)=>(0,a.createElement)("li",{key:t,className:"saddle-activity__row"},(0,a.createElement)(Mu,{round:!0,width:20,height:20}),(0,a.createElement)("span",{className:"saddle-activity__summary"},(0,a.createElement)(Mu,{height:12,width:70-10*t+"%"})),(0,a.createElement)(Mu,{height:12,width:48})))))}function Tb({plan:e,confirming:t,onConfirm:n,onCancel:r}){return e.ready?(0,a.createElement)("div",{className:"saddle-doc saddle-doc--bare"},e.steps.length>0&&(0,a.createElement)(a.Fragment,null,(0,a.createElement)("h3",{className:"saddle-doc__h"},(0,$p.__)("What comes back","saddle")),(0,a.createElement)("ul",{className:"saddle-doc__list"},e.steps.map((e,t)=>(0,a.createElement)("li",{key:t},e)))),(0,a.createElement)("div",{className:"saddle-needs-ok__actions"},(0,a.createElement)(Jd,{variant:"secondary",size:"sm",disabled:t,onClick:r},(0,$p.__)("Cancel","saddle")),(0,a.createElement)(Jd,{size:"sm",loading:t,disabled:t,onClick:n},(0,$p.__)("Undo change","saddle")))):(0,a.createElement)("div",{className:"saddle-doc saddle-doc--bare"},(0,a.createElement)("h3",{className:"saddle-doc__h"},(0,$p.__)("This change can’t be undone","saddle")),(0,a.createElement)("ul",{className:"saddle-doc__list"},e.reasons.map((e,t)=>(0,a.createElement)("li",{key:t},e))),(0,a.createElement)("div",{className:"saddle-needs-ok__actions"},(0,a.createElement)(Jd,{variant:"secondary",size:"sm",onClick:r},(0,$p.__)("Close","saddle"))))}function Ab({caps:e=[],title:t,hideEmpty:n=!1,onChange:r}){const[s,o]=(0,l.useState)([]),[i,c]=(0,l.useState)(0),[d,u]=(0,l.useState)(1),[p,m]=(0,l.useState)(""),[h,f]=(0,l.useState)(!0),[g,y]=(0,l.useState)(!1),[v,w]=(0,l.useState)(null),[_,j]=(0,l.useState)(null),[k,x]=(0,l.useState)(0),[M,N]=(0,l.useState)(null),[E,I]=(0,l.useState)(!1),C=(0,l.useCallback)((e,t,n)=>{n?y(!0):f(!0),tm(`audit-log?per_page=25&page=${e}${t?`&type=${t}`:""}`).then(a=>{const r=a.entries||[];o(e=>n?[...e,...r]:r),c(a.total||0),u(e),1!==e||t||j({any:r.length>0,rehearsed:r.some(e=>"rehearsed"===e.type)})}).catch(e=>w(e.message)).finally(()=>{f(!1),y(!1)})},[]);if((0,l.useEffect)(()=>{C(1,"",!1)},[C]),n&&(!_||!_.any))return null;const L=[{value:"",label:(0,$p.__)("All","saddle")},{value:"executed",label:(0,$p.__)("Changes","saddle")},{value:"denied",label:(0,$p.__)("Blocked","saddle")}];_&&_.rehearsed&&L.push({value:"rehearsed",label:(0,$p.__)("Rehearsed","saddle")});const S=(e=>{const t=[];return e.forEach(e=>{const n=(e=>{if(!e)return null;const t=e.replace(" ","T"),n=new Date(t.endsWith("Z")?t:`${t}Z`);return isNaN(n.getTime())?null:n})(e.date),a=n?(e=>{const t=new Date,n=new Date(e.getFullYear(),e.getMonth(),e.getDate()),a=new Date(t.getFullYear(),t.getMonth(),t.getDate()),r=Math.round((a-n)/864e5);return 0===r?(0,$p.__)("Today","saddle"):1===r?(0,$p.__)("Yesterday","saddle"):e.toLocaleDateString(void 0,{weekday:"short",month:"short",day:"numeric",...e.getFullYear()!==t.getFullYear()?{year:"numeric"}:{}})})(n):(0,$p.__)("Earlier","saddle"),r=t[t.length-1];r&&r.label===a?r.items.push({...e,d:n}):t.push({label:a,items:[{...e,d:n}]})}),t})(s);return(0,a.createElement)("div",{className:"saddle-activity"},(0,a.createElement)("div",{className:"saddle-activity__filters"},t&&(0,a.createElement)("h2",{className:"saddle-activity__heading"},t),_&&_.any&&(0,a.createElement)(cp,{"aria-label":(0,$p.__)("Filter activity","saddle"),items:L,value:p,onChange:e=>{e!==p&&(m(e),C(1,e,!1))}})),v&&(0,a.createElement)(Hu,{tone:"danger"},v),h&&(0,a.createElement)(Db,null),!h&&0===s.length&&!v&&(0,a.createElement)(xu,{title:"denied"===p?(0,$p.__)("Nothing has been blocked","saddle"):(0,$p.__)("No activity yet","saddle"),description:"denied"===p?(0,$p.__)("Attempts outside an app’s access appear here.","saddle"):(0,$p.__)("Changes your apps make appear here. Reading is not logged.","saddle")}),!h&&S.map(t=>(0,a.createElement)("section",{className:"saddle-activity__day",key:t.label},(0,a.createElement)("h3",{className:"saddle-activity__daylabel"},t.label),(0,a.createElement)("ul",{className:"saddle-activity__list"},t.items.map((n,r)=>{const s=((e,t)=>"denied"===e.type||"rehearsed"===e.type?((e,t)=>{const n=Lm(String(e.action||"").replace(/^denied-/,"")),a=(Array.isArray(t)?t:[]).find(e=>e.short===n);let r=a&&a.label?a.label:"";const s="denied"===e.type||"rehearsed"===e.type;if(!r&&!s&&e.summary)return e.summary;if(!r&&n){const e=n.replace(/[-_]+/g," ");r=e.charAt(0).toUpperCase()+e.slice(1)}if(!r)return e.summary||"";if("denied"===e.type){const t="tier"===e.target&&a&&ah[a.tier];return t?(0,$p.sprintf)(/* translators: 1: a tool's name, 2: the access level it needs. */ /* translators: 1: a tool's name, 2: the access level it needs. */ (0,$p.__)("Blocked · %1$s · needs %2$s","saddle"),r,t):(0,$p.sprintf)(/* translators: %s: a tool's name. */ /* translators: %s: a tool's name. */ @@ -114,7 +115,9 @@ (0,$p.__)("Must be %s or more.","saddle"),e.minimum):"max"===t?(0,$p.sprintf)(/* translators: %s: the largest allowed number. */ /* translators: %s: the largest allowed number. */ (0,$p.__)("Must be %s or less.","saddle"),e.maximum):(0,$p.__)("Enter a whole number.","saddle");function fw({scope:e,field:t,value:n,error:r,busy:s,onChange:o}){const l=ow(e,t.key),i=t.help?`${l}-help`:void 0,c=r?`${l}-error`:void 0,d={id:l,disabled:s,"aria-describedby":[i,c].filter(Boolean).join(" ")||void 0,"aria-invalid":!!r||void 0};let u;if("boolean"===t.type)u=(0,a.createElement)(iu,{...d,checked:!!n,onChange:o,"aria-label":t.label});else if(t.enum)u=(0,a.createElement)(lu,{...d,value:String(n),onChange:o,error:!!r,options:t.enum.map(e=>({value:String(e),label:String(e)}))});else if(sw(t))u=(0,a.createElement)(au,{...d,type:"number",inputMode:"numeric",min:t.minimum,max:t.maximum,step:"integer"===t.type?1:"any",value:n,error:!!r,onChange:e=>o(e.target.value)});else if("secret"===t.type){const e=t.value&&t.value.configured;u=(0,a.createElement)(a.Fragment,null,(0,a.createElement)("p",{className:"saddle-field__status"},e?(0,$p.sprintf)(/* translators: %s: the last characters of a saved key. */ /* translators: %s: the last characters of a saved key. */ (0,$p.__)("Set (%s)","saddle"),t.value.hint||""):(0,$p.__)("Not set","saddle")),(0,a.createElement)(au,{...d,type:"password",autoComplete:"off",value:n,error:!!r,placeholder:e?(0,$p.__)("Type a new value to replace it","saddle"):"",onChange:e=>o(e.target.value)}))}else u=(0,a.createElement)(au,{...d,type:"text",value:n,error:!!r,onChange:e=>o(e.target.value)});return(0,a.createElement)(Au,{className:"saddle-field saddle-field--"+("boolean"===t.type?"switch":"stacked"),title:(0,a.createElement)(qd,{htmlFor:l},t.label),description:t.help||r?(0,a.createElement)(a.Fragment,null,t.help&&(0,a.createElement)(eu,{id:i},t.help),r&&(0,a.createElement)(tu,{id:c},r)):void 0,actions:(0,a.createElement)("div",{className:"saddle-field__control"},u)})}function gw({scope:e,screen:t,keys:n,bare:r}){const[s,o]=(0,l.useState)(null),[i,c]=(0,l.useState)(""),[d,u]=(0,l.useState)({}),[p,m]=(0,l.useState)({}),[h,f]=(0,l.useState)(!1),[g,y]=(0,l.useState)({}),v=(0,l.useMemo)(()=>lw(s,t).filter(e=>!n||n.includes(e.key)),[s,t,n]),w=(0,l.useMemo)(()=>function(e){const t=[];return(e||[]).forEach(e=>{const n="string"==typeof e.section?e.section:"";let a=t.find(e=>e.section===n);a||(a={section:n,fields:[]},t.push(a)),a.fields.push(e)}),[...t.filter(e=>""===e.section),...t.filter(e=>""!==e.section)]}(v).map(e=>({...e,split:iw(e.fields)})),[v]),_=t=>t in g?g[t]:((e,t)=>{try{return"1"===window.localStorage.getItem(mw(e,t))}catch(e){return!1}})(e,t),j=(0,l.useCallback)(()=>{c(""),tm(`preferences/${encodeURIComponent(e)}`).then(e=>{const n=e.fields||[];o(n),u(cw(lw(n,t)))}).catch(e=>{o([]),c(e?.message||(0,$p.__)("Could not load these settings.","saddle"))})},[e,t]);(0,l.useEffect)(j,[j]),(0,l.useEffect)(()=>{if(!s)return;const t=()=>{const t=function(e,t,n){const a=String(e||"").replace(/^#/,"");return n.find(e=>ow(t,e.key)===a)||null}(window.location.hash,e,v);if(!t)return;const n=w.find(e=>e.fields.some(e=>e.key===t.key));n&&((e,t)=>e.disclosure&&e.advanced.some(e=>e.key===t.key))(n.split,t)&&y(e=>({...e,[n.section]:!0})),window.setTimeout(()=>{const n=document.getElementById(ow(e,t.key));n&&(n.scrollIntoView({block:"center"}),n.focus())},60)};return t(),window.addEventListener("hashchange",t),()=>window.removeEventListener("hashchange",t)},[s,e,v,w]);if(null===s)return(0,a.createElement)(Tu,{loading:!0,loadingRows:n?n.length:3});if(i)return(0,a.createElement)(Hu,{tone:"danger"},i," ",(0,a.createElement)(Jd,{variant:"secondary",size:"sm",onClick:j},(0,$p.__)("Try again","saddle")));if(0===v.length)return(0,a.createElement)(Hu,{tone:"info"},(0,$p.__)("There are no settings to change here.","saddle"));const k=t=>(0,a.createElement)(fw,{key:t.key,scope:e,field:t,value:d[t.key],error:p[t.key],busy:h,onChange:e=>{return n=t.key,a=e,u(e=>({...e,[n]:a})),void m(e=>{if(!e[n])return e;const t={...e};return delete t[n],t});var n,a}});return(0,a.createElement)("div",{className:"saddle-settings-form","data-scope":e},w.map(({section:t,split:n})=>(0,a.createElement)("section",{key:t||"general",className:"saddle-stack"},t&&!r&&(0,a.createElement)(am,{title:t}),(0,a.createElement)(Tu,null,n.basic.map(k)),n.disclosure&&(0,a.createElement)(zu,{className:"saddle-fields__more",open:_(t),onOpenChange:n=>((t,n)=>{y(e=>({...e,[t]:n})),((e,t,n)=>{try{window.localStorage.setItem(mw(e,t),n?"1":"0")}catch(e){}})(e,t,n)})(t,n),trigger:(0,$p.sprintf)(/* translators: %d: number of advanced settings. */ /* translators: %d: number of advanced settings. */ -(0,$p.__)("More settings (%d)","saddle"),n.advanced.length)},(0,a.createElement)(Tu,null,n.advanced.map(k))))),(0,a.createElement)(Lp,{open:pw(v,d),message:(0,$p.__)("You have unsaved changes.","saddle"),saveLabel:(0,$p.__)("Save","saddle"),discardLabel:(0,$p.__)("Discard","saddle"),saving:h,onSave:()=>{const n=function(e,t){const n={};return Object.keys(uw(e,t)).forEach(a=>{const r=e.find(e=>e.key===a);if(!r||!sw(r))return;const s=dw(r,t[a]);Number.isNaN(s)||"integer"===r.type&&!Number.isInteger(s)?n[a]="number":void 0!==r.minimum&&sr.maximum&&(n[a]="max")}),n}(v,d);Object.keys(n).length?m(Object.fromEntries(Object.entries(n).map(([e,t])=>[e,hw(v.find(t=>t.key===e),t)]))):(f(!0),m({}),tm(`preferences/${encodeURIComponent(e)}`,{method:"POST",data:{values:uw(v,d)}}).then(e=>{const n=e.fields||[];o(n),u(cw(lw(n,t))),b.success((0,$p.__)("Saved.","saddle"))}).catch(e=>{const t=e?.data?.field,n=e?.message||(0,$p.__)("Could not save that setting.","saddle");if(t&&v.some(e=>e.key===t)){m({[t]:n});const e=w.find(e=>e.split.advanced.some(e=>e.key===t));e&&y(t=>({...t,[e.section]:!0}))}else b.error(n)}).finally(()=>f(!1)))},onDiscard:()=>{u(cw(v)),m({})}}))}const yw=e=>new Set((e||[]).filter(e=>!1===e.enabled).map(e=>e.short));function vw(e,t){const n=new Set(e);return n.has(t)?n.delete(t):n.add(t),n}const bw=e=>({drafts_only:!(!e||!e.drafts_only),rehearsal:!(!e||!e.rehearsal),domain_enforced:!!(e&&e.domain&&e.domain.enforced),core_major_updates:!(!e||!e.core_major_updates)}),ww=({children:e,help:t})=>(0,a.createElement)("span",{className:"saddle-settings__label"},e,(0,a.createElement)(op,null,t));function _w({onRehearsalChanged:e}){const[t,n]=(0,l.useState)(null),r=(0,l.useRef)({});(0,l.useEffect)(()=>{tm("preferences").then(e=>n(bw(e))).catch(()=>n(bw({})))},[]);const s=({key:s,id:o,title:l,help:i,meta:c,label:d})=>(0,a.createElement)(Au,{key:s,title:i?(0,a.createElement)(ww,{help:i},l):l,description:c,actions:t?(0,a.createElement)(iu,{id:o,checked:!!t[s],onChange:()=>(a=>{const s=!t[a];r.current[a]=(r.current[a]||0)+1,n(e=>({...e,[a]:s})),tm("preferences",{method:"POST",data:{[a]:s}}).then(t=>{if(r.current[a]-=1,0===r.current[a]){const r=bw(t);n(e=>({...e,[a]:r[a]})),"rehearsal"===a&&e&&e(r.rehearsal)}b.success((0,$p.__)("Saved.","saddle"))}).catch(e=>{r.current[a]-=1,0===r.current[a]&&n(e=>({...e,[a]:!s})),b.error(e?.message||(0,$p.__)("Could not save that setting.","saddle"))})})(s),"aria-label":d||l}):(0,a.createElement)(Mu,{round:!0,width:32,height:18})});return(0,a.createElement)("section",{className:"saddle-section"},(0,a.createElement)(am,{title:(0,$p.__)("Safety","saddle")}),(0,a.createElement)(Tu,null,s({key:"drafts_only",id:"saddle-drafts-only-switch",title:(0,$p.__)("Publishing needs my OK","saddle"),help:(0,$p.__)("New posts and pages an app asks to publish or schedule are saved as drafts. Publishing or scheduling an existing item needs your confirmation. Edits to content that is already live still go through.","saddle"),meta:(0,$p.__)("Apps save drafts; you approve what goes live.","saddle")}),s({key:"rehearsal",id:"saddle-rehearsal-switch",title:(0,$p.__)("Practice mode","saddle"),help:(0,$p.__)("Apps can try anything their access allows and nothing is saved. Each tool that would change the site says what it would have done, and the attempt shows in Activity as rehearsed. Reading works as usual.","saddle"),meta:(0,$p.__)("Apps try changes but nothing is saved.","saddle")}),s({key:"domain_enforced",id:"saddle-domain-switch",title:(0,$p.__)("Stop writes if the site moves","saddle"),help:(0,$p.__)("If the site address changes, for example after copying it to another domain, apps are refused writes until you confirm their access again.","saddle"),meta:(0,$p.__)("After an address change, confirm access again.","saddle")}),s({key:"core_major_updates",id:"saddle-field-saddle-core_major_updates",title:(0,$p.__)("Major WordPress releases","saddle"),help:(0,$p.__)("Lets an app update WordPress to a new major version, such as 7.1 to 7.2, after you approve its preview. A major release has no automatic rollback and upgrades the database. Minor and security releases never need this. WordPress’s own “automatic updates for all new versions” in Dashboard → Updates is a separate setting.","saddle"),meta:(0,$p.__)("Apps can update WordPress to a new major version.","saddle")})))}function jw({caps:e,listed:t,onChanged:n}){const[r,s]=(0,l.useState)(""),[o,i]=(0,l.useState)(()=>yw(e)),c=(0,l.useRef)(0);(0,l.useEffect)(()=>i(yw(e)),[e]);const d=(0,l.useMemo)(()=>function(e,t,n="Other"){const a=new Map;return(e||[]).filter(e=>function(e,t){const n=String(t||"").trim().toLowerCase();return!n||[e.label,e.short,e.description].some(e=>String(e||"").toLowerCase().includes(n))}(e,t)).forEach(e=>{const t=e.category||n;a.has(t)||a.set(t,[]),a.get(t).push(e)}),[...a.entries()].map(([e,t])=>({category:e,tools:[...t].sort((e,t)=>String(e.label).localeCompare(String(t.label)))})).sort((e,t)=>e.category===n||t.category===n?e.category===n?1:-1:e.category.localeCompare(t.category))}(t,r,(0,$p.__)("Other","saddle")),[t,r]),u=(0,l.useCallback)(()=>{c.current-=1,0===c.current&&n&&n()},[n]);return(0,a.createElement)("div",{className:"saddle-tools"},(0,a.createElement)("input",{type:"search",className:"saddle-tools__search",value:r,onChange:e=>s(e.target.value),placeholder:(0,$p.__)("Search tools…","saddle"),"aria-label":(0,$p.__)("Search tools","saddle")}),0===d.length&&(0,a.createElement)("p",{className:"saddle-tools__empty"},(0,$p.__)("No tools match.","saddle")),d.map(({category:e,tools:t})=>(0,a.createElement)("div",{key:e,className:"saddle-tools__group"},(0,a.createElement)("h3",{className:"saddle-tools__area"},e),(0,a.createElement)(Tu,null,t.map(e=>(0,a.createElement)(Au,{key:e.short,title:(0,a.createElement)(rp,{content:e.description},(0,a.createElement)("span",null,e.label)),actions:(0,a.createElement)(iu,{checked:!o.has(e.short),onChange:()=>(e=>{const t=vw(o,e);i(t),c.current+=1,tm("abilities",{method:"POST",data:{disabled:[...t]}}).then(()=>b.success((0,$p.__)("Saved.","saddle"))).catch(t=>{i(t=>vw(t,e)),b.error(t?.message||(0,$p.__)("Could not save that change.","saddle"))}).finally(u)})(e.short),"aria-label":e.label})}))))))}function kw(){const e=bh();return(0,a.createElement)(kh,{...e})}const xw=["memory_max_entries","memory_core_budget"],Mw=["memory_recent_changes","memory_recent_limit"];function Nw({caps:e,loadCaps:t,onRehearsalChanged:n,children:r}){const[s]=(0,l.useState)(()=>"signin"===new URLSearchParams(window.location.search).get("section")),o=(0,l.useRef)(null),[i,c]=(0,l.useState)(!1),d=(0,l.useMemo)(()=>(e=>(Array.isArray(e)?e:[]).filter(e=>!!e&&!1!==e.available))(e),[e]);return(0,l.useEffect)(()=>{if(!s)return;const e=[0,400,1200].map(e=>setTimeout(()=>{o.current&&o.current.scrollIntoView({block:"start"})},e));return()=>e.forEach(clearTimeout)},[s]),(0,a.createElement)(a.Fragment,null,(0,a.createElement)(_w,{onRehearsalChanged:n}),(0,a.createElement)("section",{className:"saddle-section"},(0,a.createElement)(am,{title:(0,$p.__)("Advanced","saddle")}),(0,a.createElement)("div",{className:"saddle-adv"},(0,a.createElement)(zu,{trigger:(0,$p.sprintf)(/* translators: %d: number of tools. */ /* translators: %d: number of tools. */ -(0,$p.__)("Turn off single tools (%d)","saddle"),d.length)},(0,a.createElement)(jw,{caps:e,listed:d,onChanged:t})),(0,a.createElement)(zu,{ref:o,id:"saddle-adv-signin",defaultOpen:s,trigger:(0,$p.__)("Sign-in for apps","saddle")},(0,a.createElement)("div",{className:"saddle-adv__signin"},(0,a.createElement)(kw,null))),(0,a.createElement)(zu,{trigger:(0,$p.__)("Memory limits","saddle")},(0,a.createElement)(gw,{scope:"saddle",screen:"settings/general",keys:xw,bare:!0})),(0,a.createElement)(zu,{trigger:(0,$p.__)("Recent changes","saddle")},(0,a.createElement)(gw,{scope:"saddle",screen:"settings/general",keys:Mw,bare:!0})),(0,a.createElement)(zu,{trigger:(0,$p.__)("Connection check","saddle"),onOpenChange:e=>e&&c(!0)},i&&(0,a.createElement)("div",{className:"saddle-adv__check"},(0,a.createElement)(Tm,null),(0,a.createElement)(Ch,null))))),r)}function Ew({tasks:e,navigate:t}){const n=function(e){return(Array.isArray(e)?e:[]).filter(e=>e&&!e.done)}(e);return n.length?(0,a.createElement)(Tu,{className:"saddle-module-setup"},n.map(e=>(0,a.createElement)(Au,{key:e.id,title:e.title,description:e.line||void 0,actions:e.action?(0,a.createElement)(Jd,{variant:"secondary",size:"sm",href:e.action.url,onClick:e.action.route&&t?n=>{sb(n)&&(n.preventDefault(),t({...e.action.route}))}:void 0,target:e.action.external?"_blank":void 0,rel:e.action.external?"noopener noreferrer":void 0},e.action.label):void 0}))):null}const Iw={ApplyBar:Lp,Badge:_u,BarList:Ou,BulkBar:function({count:e,children:t,onClear:n,className:a,...r}){return e?(0,I.jsxs)("div",{className:Fd("pp-bulkbar",a),role:"toolbar","aria-label":"Bulk actions",...r,children:[(0,I.jsxs)("span",{className:"pp-bulkbar__count",children:[e," selected"]}),(0,I.jsx)("div",{className:"pp-bulkbar__actions",children:t}),n&&(0,I.jsx)("button",{type:"button",className:"pp-bulkbar__clear",onClick:n,children:"Clear"})]}):null},Button:Jd,CalloutCard:Bu,Card:hu,CardContent:gu,CardFooter:yu,CardGrid:Gu,CardHeader:fu,Checkbox:du,ChecklistItem:up,Chip:_p,CodeBlock:lp,Collapsible:zu,DataTable:function({columns:e=[],rows:t=[],rowId:n=e=>e.id,sort:r,defaultSort:s,onSort:o,autoSort:l=!1,selectable:i=!1,selected:c=[],onSelect:d,onRowClick:u,rowActions:p,loading:m=!1,loadingRows:h=5,stickyHeader:f=!1,pageSize:g=0,empty:y=null,emptyTitle:v="No results",emptyDescription:b,emptyIcon:w,className:_,...j}){const[k,x]=(0,a.useState)(s||null),[M,N]=(0,a.useState)(1),E=void 0!==r?r:k,C=(0,a.useMemo)(()=>{if(!l||!E)return t;const n=e.find(e=>e.key===E.key),a=(null==n?void 0:n.sortAccessor)||(e=>e[E.key]),r="asc"===E.dir?1:-1;return[...t].sort((e,t)=>{const n=a(e),s=a(t);return null==n?1:null==s?-1:ns?1*r:0})},[l,E,t,e]),L=g>0?Math.ceil(C.length/g):1,S=Math.min(M,L),D=g>0?C.slice((S-1)*g,S*g):C,T=new Set(c),A=D.map(n),O=D.length>0&&A.every(e=>T.has(e)),P=A.some(e=>T.has(e));return m?(0,I.jsx)("div",{className:Fd("pp-table-wrap",_),children:(0,I.jsx)(Eu,{rows:h})}):t.length?(0,I.jsxs)(I.Fragment,{children:[(0,I.jsxs)(Lu,{className:_,stickyHeader:f,...j,children:[(0,I.jsx)("thead",{children:(0,I.jsxs)("tr",{children:[i&&(0,I.jsx)(Su,{className:"pp-table__th--check",children:(0,I.jsx)(du,{checked:O,indeterminate:!O&&P,onChange:()=>d&&d(O?c.filter(e=>!A.includes(e)):[...new Set([...c,...A])]),"aria-label":"Select all rows"})}),e.map(e=>(0,I.jsx)(Su,{align:e.align,"aria-sort":(null==E?void 0:E.key)===e.key?"asc"===E.dir?"ascending":"descending":void 0,children:e.sortable?(0,I.jsxs)("button",{type:"button",className:"pp-table__sort","aria-label":"string"==typeof e.header?`Sort by ${e.header}`:void 0,onClick:()=>(e=>{const t=(null==E?void 0:E.key)===e&&"asc"===E.dir?"desc":"asc";void 0===r&&x({key:e,dir:t}),o&&o(e)})(e.key),children:[e.header,(0,I.jsx)("span",{className:"pp-table__sort-icon","aria-hidden":"true",children:(null==E?void 0:E.key)===e.key?"asc"===E.dir?(0,I.jsx)(Ms,{size:13}):(0,I.jsx)(zr,{size:13}):(0,I.jsx)(Ns,{size:13,className:"pp-table__sort-icon--idle"})})]}):e.header},e.key)),p&&(0,I.jsx)(Su,{className:"pp-table__th--actions","aria-label":"Actions"})]})}),(0,I.jsx)("tbody",{children:D.map(t=>{const a=n(t),r=Boolean(u);return(0,I.jsxs)("tr",{className:Fd(T.has(a)&&"pp-table__row--selected",r&&"pp-table__row--clickable"),onClick:r?()=>u(t):void 0,tabIndex:r?0:void 0,onKeyDown:r?e=>{"Enter"!==e.key&&" "!==e.key||e.target!==e.currentTarget||(e.preventDefault(),u(t))}:void 0,children:[i&&(0,I.jsx)(Du,{className:"pp-table__td--check",onClick:e=>e.stopPropagation(),children:(0,I.jsx)(du,{checked:T.has(a),onChange:()=>(e=>d&&d(T.has(e)?[...T].filter(t=>t!==e):[...T,e]))(a),"aria-label":"Select row"})}),e.map(e=>(0,I.jsx)(Du,{align:e.align,children:e.render?e.render(t):t[e.key]},e.key)),p&&(0,I.jsx)(Du,{className:"pp-table__td--actions",align:"right",onClick:e=>e.stopPropagation(),children:p(t)})]},a)})})]}),g>0&&L>1&&(0,I.jsxs)("div",{className:"pp-table__footer",children:[(0,I.jsxs)("span",{className:"pp-table__count",children:[C.length," items"]}),(0,I.jsx)(Cu,{page:S,pageCount:L,onChange:N})]})]}):null!=y?y:(0,I.jsx)("div",{className:Fd("pp-table-wrap","pp-table-wrap--empty",_),children:(0,I.jsx)(xu,{icon:w,title:v,description:b})})},Dialog:pp,Drawer:Np,DropdownItem:Ip,DropdownMenu:Ep,EmptyState:xu,ErrorText:tu,Field:nu,FilterTabs:cp,HelpTip:op,Hint:eu,IconButton:Kd,Input:au,KeyValueList:Qu,Label:qd,LiveIndicator:ku,Meter:Yu,NativeSelect:su,Notice:Hu,PageHeader:Rp,Pagination:Cu,Popover:sp,ProgressBar:Wu,RadioGroup:pu,Row:Au,RowList:Tu,SearchInput:ou,SegmentedControl:mu,Select:lu,Skeleton:Mu,SkeletonCard:function({delay:e=!1,className:t,...n}){return(0,I.jsxs)("div",{className:Fd("pp-card pp-card--padded",e&&"pp-skeleton--delay",t),"aria-busy":"true",...n,children:[(0,I.jsx)(Mu,{height:16,width:"40%"}),(0,I.jsx)("div",{style:{height:12}}),(0,I.jsx)(Nu,{lines:2})]})},SkeletonTable:Eu,SkeletonText:Nu,Snippet:ip,Spinner:Ru,StatCard:bu,StatGrid:wu,StatusDot:ju,Switch:iu,Tabs:Bp,Textarea:ru,toast:b,Tooltip:rp,useConfirm:fp,VisuallyHidden:Ap,icons:Cv},Cw={SectionHeader:am,SettingsForm:gw,WaitingLine:Eh},Lw=["settingsCards","tabs","screens","homeCards","connectionCards","mount","settings-form","waiting-line","ui-wide","view","drill-in","subtabs","page-layout"];"undefined"!=typeof window&&(window.saddleShell={version:2,has:e=>Lw.includes(e)});const Sw=(e,t)=>n=>{var a;return!(!n||t.some(e=>!n[e])||(null!==(a=n.requiresShell)&&void 0!==a?a:1)>2&&(console.warn(`[saddle] ${e} "${n.id}" needs shell v${n.requiresShell}, this is v2 — skipped. Update Saddle.`),1))},Dw=(e,t)=>{var n,a;return(null!==(n=e.order)&&void 0!==n?n:50)-(null!==(a=t.order)&&void 0!==a?a:50)};function Tw(){const e=(0,wb.applyFilters)("saddle.admin.settingsCards",[],{shellVersion:2,ui:Iw});return(Array.isArray(e)?e:[]).filter(Sw("settings card",["id","Component"])).sort(Dw)}function Aw(){const e=(0,wb.applyFilters)("saddle.admin.tabs",[],{shellVersion:2,ui:Iw,icons:[]});return(Array.isArray(e)?e:[]).filter(Sw("tab",["id","label","Component"])).sort(Dw)}function Ow(){const e=(0,wb.applyFilters)("saddle.admin.screens",[],{shellVersion:2,ui:Iw,kit:Cw});return(Array.isArray(e)?e:[]).filter(e=>e&&"string"==typeof e.module&&"string"==typeof e.tab&&(void 0===e.sub||"string"==typeof e.sub)&&e.Component)}function Pw({where:e,navigate:t}){return(0,l.useMemo)(()=>function(e){const t="home"===e?"saddle.admin.homeCards":"saddle.admin.connectionCards",n=(0,wb.applyFilters)(t,[],{shellVersion:2,ui:Iw,kit:Cw});return(Array.isArray(n)?n:[]).filter(Sw("card",["id","Component"])).sort(Dw)}(e),[e]).map(e=>(0,a.createElement)(e.Component,{key:e.id,ui:Iw,kit:Cw,navigate:t,shellVersion:2}))}function zw({module:e,tab:t}){const n=(0,l.useRef)(null);return(0,l.useEffect)(()=>{n.current&&(0,wb.doAction)("saddle.admin.mount",n.current,{module:e,tab:t})},[e,t]),(0,a.createElement)("div",{id:"saddle-module-slot",ref:n})}function Rw({area:e,tab:t,sub:n,view:r,args:s,navigate:o,header:i}){const c=(0,l.useMemo)(Ow,[]),d=(0,l.useMemo)(()=>i?{drillIn:i.drillIn}:void 0,[i]);(0,l.useEffect)(()=>()=>i&&i.clear(),[i]);const u=Xm(e,t),p=e.tabs[0]&&e.tabs[0].key===t&&(!u.length||u[0].key===n);if("mount"===e.content)return(0,a.createElement)(zw,{module:e.key,tab:t});const m=function(e,t,n,a){const r=(Array.isArray(e)?e:[]).filter(e=>e&&e.module===t&&e.tab===n);return(a?r.find(e=>e.sub===a):null)||r.find(e=>!e.sub)||null}(c,e.key,t,n);return m||"settings"!==t?m?(0,a.createElement)(a.Fragment,null,p&&!r&&(0,a.createElement)(Ew,{tasks:Jp.setup,navigate:o}),(0,a.createElement)(m.Component,{ui:Iw,kit:Cw,module:e.key,tab:t,sub:n||"",view:r||"",args:s||{},navigate:o,header:d,api:tm,shellVersion:2})):(0,a.createElement)(Hu,{tone:"warning"},(0,$p.sprintf)(/* translators: %s: module name, such as Saddle Analytics. */ /* translators: %s: module name, such as Saddle Analytics. */ -(0,$p.__)("%s did not add this screen. Updating it may add it.","saddle"),e.product||e.title)):(0,a.createElement)(gw,{scope:e.key})}function Zw({openConnect:e,closeConnect:t,connectOpen:n,connectApp:r,openWizard:s,refreshClients:o,removeClient:l,clients:i,tier:c,navigate:d}){const u=bh();return(0,a.createElement)(a.Fragment,null,(0,a.createElement)(zv,{clients:i,onClientsChanged:o,onClientRemoved:l,siteTier:c,onConnect:t=>e(t)}),(0,a.createElement)(Pw,{where:"connections",navigate:d}),(0,a.createElement)(Np,{open:n,onOpenChange:e=>!e&&t(),title:(0,$p.__)("Connect an app","saddle"),size:"md"},(0,a.createElement)(gh,{oauth:u.oauth,onKey:e=>{t(),s(e)},onConnected:o,initialApp:r})))}function Ww({extTabs:e,caps:t,loadCaps:n,onRehearsalChanged:r}){const s=(0,l.useMemo)(Tw,[]),o=Hm(Jp.areas,"home");return(0,a.createElement)(a.Fragment,null,(0,a.createElement)(Nw,{caps:t,loadCaps:n,onRehearsalChanged:r},s.map(e=>(0,a.createElement)("section",{key:e.id,className:"saddle-stack"},(e.title||e.label)&&(0,a.createElement)(am,{title:e.title||e.label}),(0,a.createElement)(e.Component,{ui:Iw,shellVersion:2}))),e.map(e=>(0,a.createElement)("section",{key:e.id,className:"saddle-stack"},(0,a.createElement)(am,{title:e.label}),(0,a.createElement)(e.Component,{ui:Iw,shellVersion:2})))),o&&(0,a.createElement)("div",null,(0,a.createElement)(Jd,{href:Km(o.url,"setup","1"),variant:"link",size:"sm"},(0,$p.__)("Run setup again","saddle"))))}function Yw({area:e,tab:t}){return"home"!==e.key||t&&"overview"!==t?(0,a.createElement)("div",{className:"saddle-skeleton"},(0,a.createElement)(Ap,null,(0,$p.__)("Loading","saddle")),(0,a.createElement)(Mu,{height:15,width:120,"aria-hidden":"true"}),(0,a.createElement)(Tu,{loading:!0,loadingRows:3,"aria-hidden":"true"})):(0,a.createElement)(Wb,null)}function Gw(e){const{area:t,tab:n,sub:r,view:s,args:o,navigate:i,header:c,tier:d,caps:u,clients:p,paused:m,wizardOpen:h,wizardApp:f,openWizard:g,closeWizard:y,refreshClients:v,removeClient:b,connectOpen:w,connectApp:_,openConnect:j,closeConnect:k,pausing:x,onTogglePause:M,loadCaps:N,onRehearsalChanged:E,onboarding:I,onHideSetup:C}=e,L=(0,l.useMemo)(Aw,[]);if(t.module)return(0,a.createElement)(Rw,{area:t,tab:n,sub:r,view:s,args:o,navigate:i,header:c});switch(`${t.key}/${n}`){case"home/overview":return(0,a.createElement)(a.Fragment,null,(0,a.createElement)(Gb,{tier:d,clients:p,paused:m,pausing:x,onTogglePause:M,caps:u,onNavigate:i,onConnect:()=>j(),onboarding:I,onHideSetup:C,homeUrl:t.url}),(0,a.createElement)(Pw,{where:"home",navigate:i}));case"connections/apps":return h?(0,a.createElement)(xh,{tier:d,clients:p,onExit:y,onClientsChanged:v,initialApp:f}):(0,a.createElement)(Zw,{openConnect:j,closeConnect:k,connectOpen:w,connectApp:_,openWizard:g,refreshClients:v,removeClient:b,clients:p,tier:d,navigate:i});case"settings/general":return(0,a.createElement)(Ww,{extTabs:L,caps:u,loadCaps:N,onRehearsalChanged:E});case"services/overview":return(0,a.createElement)(aw,null);case"context/overview":return(0,a.createElement)($b,null)}return null}const Bw=Jp.areas||[],Vw=Hm(Bw,Jp.area)||Hm(Bw,"home")||{key:"home",title:(0,$p.__)("Home","saddle"),url:window.location.href,tabs:[{key:"overview",label:(0,$p.__)("Home","saddle")}]},Uw=()=>new URLSearchParams(window.location.search),Fw=Array.isArray(Jp.notices)?Jp.notices:[],Hw=()=>"connections"===Vw.key?new URLSearchParams(window.location.search).get("add"):null,Qw=()=>"connections"===Vw.key?new URLSearchParams(window.location.search).get("key"):null,Xw=()=>null!==Hw(),$w=()=>null!==Qw(),Jw=e=>e&&"1"!==e?e:null;function Kw(){const e=Vw,t=(0,l.useMemo)(()=>"home"===e.key?function(e,t){const n=String(t||"").replace(/^#/,""),a=Object.prototype.hasOwnProperty.call(Fm,n)?Fm[n]:null;if(!a)return null;const r=Jm(e,a.area,a.tab);return r?a.anchor?`${r}#${a.anchor}`:r:null}(Bw,window.location.hash):function(e,t,n){return e&&"settings"===e.key?"services"!==new URLSearchParams(n||"").get("section")?null:Jm(t,"services")||null:null}(e,Bw,window.location.search),[e]);(0,l.useEffect)(()=>{t&&window.location.replace(t)},[t]);const[n,r]=(0,l.useState)(()=>{const t=qm(window.location.search),n=Qm(e,t.tab||Jp.tab),a=n!==Jp.tab||t.tab?"":Jp.sub;return{tab:n,sub:$m(e,n,t.sub||a||""),view:t.view,args:t.args}}),{tab:s,sub:o,view:i,args:c}=n,d=(0,l.useCallback)(t=>r({tab:t,sub:$m(e,t,""),view:"",args:{}}),[e]),[u,p]=(0,l.useState)(null),m=(0,l.useMemo)(()=>function(e,t,n,a=""){const r=e=>!!e&&e.tab===t&&e.view===n&&(e.sub||"")===a,s=()=>e(e=>r(e)?null:e);return{drillIn:o=>{const l=o&&o.title,i="string"==typeof l||"number"==typeof l?String(l).trim():"";i?e(e=>r(e)&&e.title===i?e:{title:i,tab:t,sub:a,view:n}):s()},clear:s}}(p,s,i,o),[s,i,o]),[h,f]=(0,l.useState)(null),[g,y]=(0,l.useState)([]),[v,w]=(0,l.useState)([]),[_,j]=(0,l.useState)(null),[k,x]=(0,l.useState)(!0),[M,N]=(0,l.useState)(()=>"home"===Vw.key&&Uw().has("setup")),[E,I]=(0,l.useState)(!1),C=(0,l.useRef)(!1),[L,S]=(0,l.useState)(!1),[D,T]=(0,l.useState)(!1),[A,O]=(0,l.useState)(!1),[P,z]=(0,l.useState)(!0),[R,Z]=(0,l.useState)(null),[W,Y]=(0,l.useState)(Fw),[G,B]=(0,l.useState)(!1),[V,U]=(0,l.useState)($w),[F,H]=(0,l.useState)(()=>Jw(Qw())),[Q,X]=(0,l.useState)(Xw),[$,J]=(0,l.useState)(()=>Jw(Hw())),K=(0,l.useCallback)(t=>{const n=Qm(e,t);d(n),U(!1);const a=Jm(Bw,e.key,n);a&&window.history&&window.history.pushState&&window.history.pushState({},"",a)},[e,d]),q=(0,l.useCallback)(t=>{const a=Qm(e,t.tab||n.tab);let s="";"sub"in t?s=t.sub||"":a===n.tab&&(s=n.sub);const o={tab:a,sub:$m(e,a,s),view:t.view||"",args:t.args||{}};r(o),U(!1);const l=eh(Bw,e.key,o);l&&window.history&&window.history.pushState&&window.history.pushState({},"",l)},[e,n.tab,n.sub]);(0,l.useEffect)(()=>{if(t||!window.history||!window.history.replaceState)return;const e=function(e,t){const n=new URLSearchParams(e||""),a=t&&t.tab||"",r=t&&t.sub||"";let s=!1;if(n.has("tab")&&n.get("tab")!==a&&(n.delete("tab"),s=!0),n.has("sub")&&n.get("sub")!==r){const e=String(n.get("sub")).replace(/[^a-z0-9_-]/gi,"").toLowerCase();r&&e===r?n.set("sub",r):n.delete("sub"),s=!0}if(!s)return null;const o=n.toString();return o?`?${o}`:""}(window.location.search,n);null!==e&&window.history.replaceState(window.history.state,"",window.location.pathname+e+window.location.hash)},[]),(0,l.useEffect)(()=>{const t=()=>{const t=qm(window.location.search),n=Qm(e,t.tab);r({tab:n,sub:$m(e,n,t.sub),view:t.view,args:t.args}),U($w()),X(Xw()),J(Jw(Hw()))};return window.addEventListener("popstate",t),()=>window.removeEventListener("popstate",t)},[e]);const ee=(0,l.useCallback)(t=>{const n=function(e){if(e&&"object"==typeof e){const t=["sub","view","args"].some(t=>t in e);if(!e.area&&!e.tab&&!t)return null;const n={area:e.area||"",tab:e.tab||""};return"sub"in e&&(n.sub="string"==typeof e.sub?e.sub:""),t&&(n.view="string"==typeof e.view?e.view:"",n.args=e.args&&"object"==typeof e.args?e.args:{}),n}const t=Object.prototype.hasOwnProperty.call(Fm,e)?Fm[e]:null;return t?{area:t.area,tab:t.tab}:null}(t);if(!n)return;if(!n.area||n.area===e.key)return void("view"in n?q(n):K(n.tab));const a=eh(Bw,n.area,n);a&&window.location.assign(a)},[e,K,q]),te=(0,l.useCallback)(e=>tm("onboarding",{method:"POST",data:e}).then(e=>{const t=["new","active"].includes(e.first_run.state);C.current&&t||j(e)}).catch(()=>{}),[]),ne=(0,l.useCallback)(()=>tm("capabilities").then(e=>{y(e.capabilities||[]),f(e.current_tier)}),[]),ae=(0,l.useCallback)(()=>tm("clients").then(e=>w(e.clients||[])),[]),re=(0,l.useCallback)(()=>ae().catch(e=>b.error((0,$p.sprintf)(/* translators: %s: error message. */ /* translators: %s: error message. */ -(0,$p.__)("Couldn’t refresh the connections list: %s","saddle"),e.message))),[ae]),se=(0,l.useCallback)(e=>{w(t=>t.filter(t=>t.uuid!==e))},[]);(0,l.useEffect)(()=>{if(!t){if(window.history&&window.history.replaceState){const e=new URL(window.location.href),t=["password","user_login","site_url","connected","rejected"];t.some(t=>e.searchParams.has(t))&&(t.forEach(t=>e.searchParams.delete(t)),window.history.replaceState({},"",e.pathname+e.search+e.hash))}Promise.all([ne(),ae(),tm("onboarding").then(j).catch(()=>{}),tm("preferences").then(e=>{x(!!e.onboarded),S(!!e.paused),O(!!e.domain_warning)})]).catch(e=>{e&&(e.data&&401===e.data.status||"invalid_json"===e.code)?B(!0):Z(e.message)}).finally(()=>z(!1))}},[t,ne,ae]),(0,l.useEffect)(()=>{const e=window.location.hash.replace("#","");if(P||!e)return;const t=[0,400,1200].map(t=>setTimeout(()=>{const t=document.getElementById(e);t&&t.scrollIntoView()},t));return()=>t.forEach(clearTimeout)},[P]);const oe=Jm(Bw,"connections","apps")||window.location.href,le=()=>{const e=!L;T(!0),tm("preferences",{method:"POST",data:{paused:e}}).then(e=>S(!!e.paused)).catch(e=>Z(e.message)).finally(()=>T(!1))},ie=(0,l.useCallback)(e=>{Y(t=>t.filter(t=>t.id!==e.id)),tm(`notices/${encodeURIComponent(e.id)}/dismiss`,{method:"POST"}).catch(t=>{Y(t=>[...t,e]),b.error(t.message)})},[]),{slot:ce,rest:de}=function(e){const t=(Array.isArray(e)?e:[]).filter(e=>e&&e.id&&e.message);if(!t.length)return{slot:null,rest:[]};let n=0;return t.forEach((e,a)=>{ib(e)t!==n)}}([...R?[{id:"app-error",severity:"error",message:R}]:[],...A&&!V?[{id:"domain-drift",severity:"warning",message:(0,$p.__)("This site’s address changed after AI write access was turned on, which can mean a copy or migration carried over live credentials. If you didn’t expect that, review your AI apps and disconnect any you don’t recognise.","saddle"),action:{label:(0,$p.__)("Mark as expected","saddle"),onClick:()=>{tm("preferences",{method:"POST",data:{tier:h}}).then(e=>O(!!e.domain_warning)).catch(e=>Z(e.message))}}}]:[],...W]);let ue=null;const pe=_?function(e,t){if(t)return!0;const n=e&&e.state;return"new"===n||"active"===n}(_.first_run,M):!k||M,me=(()=>{const e=_?_.first_run:{state:"new",step:"",app:""},t=Uw();return M&&"try"===t.get("step")&&t.get("app")?{...e,step:"try",app:t.get("app")}:e})();return ue=t?null:P?(0,a.createElement)(bb,{area:e,tab:s,sub:o,view:i,onTab:K,onSub:e=>q({sub:e}),loading:!0},(0,a.createElement)(Yw,{area:e,tab:s})):G?(0,a.createElement)(rb,{onRetry:()=>window.location.reload()}):"home"===e.key&&pe?(0,a.createElement)(bb,{area:e,tab:"setup",showTabs:!1,crumb:(0,$p.__)("Welcome","saddle"),status:{paused:L,pausing:D,onToggle:le},moreNotices:[ce,...de].filter(Boolean),onDismissNotice:ie},(0,a.createElement)("div",{className:"saddle-app--setup"},(0,a.createElement)(Jv,{tier:h,clients:v,firstRun:me,send:te,onTierSaved:f,onClientsChanged:re,onFinish:()=>{if(C.current=!0,x(!0),N(!1),j(e=>e&&["new","active"].includes(e.first_run.state)?{...e,first_run:{...e.first_run,state:"skipped"}}:e),window.history&&window.history.replaceState){let e=window.location.href;["setup","step","app"].forEach(t=>{e=Km(e,t,null)}),window.history.replaceState({},"",e)}K("overview")}}))):(0,a.createElement)(bb,{area:e,tab:s,sub:o,view:i,drill:u,onTab:K,onSub:e=>q({sub:e}),onBack:()=>q({sub:o}),status:{paused:L,pausing:D,onToggle:le},notices:!V,notice:ce,moreNotices:de,onDismissNotice:ie},(0,a.createElement)("div",{className:"saddle-tabpane",key:`${e.key}/${s}${i?`/${i}`:""}${V?"/add":""}`},(0,a.createElement)(Gw,{area:e,tab:s,sub:o,view:i,args:c,navigate:ee,header:m,tier:h,caps:g,clients:v,paused:L,pausing:D,wizardOpen:V,wizardApp:F,openWizard:(t=null)=>{const n=Km(oe,"key",t||"1");"connections"===e.key?(d("apps"),X(!1),H(t),U(!0),window.history&&window.history.pushState&&window.history.pushState({},"",n)):window.location.assign(n)},closeWizard:()=>{U(!1),window.history&&window.history.replaceState&&window.history.replaceState({},"",oe),re()},connectOpen:Q,connectApp:$,openConnect:(t=null)=>{const n=Km(oe,"add",t||"1");"connections"===e.key?(d("apps"),J(t),X(!0),window.history&&window.history.pushState&&window.history.pushState({},"",n)):window.location.assign(n)},closeConnect:()=>{X(!1),J(null),window.history&&window.history.replaceState&&window.history.replaceState({},"",oe),re()},refreshClients:re,removeClient:se,onTogglePause:le,loadCaps:ne,onboarding:_,onHideSetup:()=>te({event:"setup.hide",module:"home"})})),!V&&"home"===e.key&&"overview"===s&&!E&&function(e){if(!e||!e.first_run)return!1;const{state:t,tier_choice:n}=e.first_run;return"done"===t&&""!==(n||"")&&!(e.user&&e.user.tour_done)}(_)&&(0,a.createElement)(eb,{onFinish:()=>{I(!0),te({event:"tour.done"})}})),(0,a.createElement)(ap,null,(0,a.createElement)(hp,null,ue,(0,a.createElement)(Ku,null)))}const qw=window.saddleData||{};var e_;o().use((({root:e,homeUrl:t})=>(n,a)=>{const r=tb();if(""===r||!((e,t,n)=>"string"==typeof e.url?!(!t||0!==e.url.indexOf(t))||!!n&&0===e.url.indexOf(n)&&-1!==e.url.indexOf("rest_route="):"string"==typeof e.path)(n,e,t))return a(n);const s={...n};return"string"==typeof s.path&&(s.path=nb(s.path,r)),"string"==typeof s.url&&(s.url=nb(s.url,r)),a(s)})({root:qw.root,homeUrl:qw.homeUrl})),e_=qw.nonce,!o().nonceMiddleware&&e_&&(o().nonceMiddleware=o().createNonceMiddleware(e_),o().use(o().nonceMiddleware)),o().nonceMiddleware,qw.root&&o().use(o().createRootURLMiddleware(qw.root));const t_=()=>{const e=document.getElementById("saddle-root");e&&(0,l.createRoot)(e).render((0,a.createElement)(Kw,null))};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",t_):t_()},20(e,t,n){var a=n(609),r=Symbol.for("react.element"),s=Symbol.for("react.fragment"),o=Object.prototype.hasOwnProperty,l=a.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,i={key:!0,ref:!0,__self:!0,__source:!0};function c(e,t,n){var a,s={},c=null,d=null;for(a in void 0!==n&&(c=""+n),void 0!==t.key&&(c=""+t.key),void 0!==t.ref&&(d=t.ref),t)o.call(t,a)&&!i.hasOwnProperty(a)&&(s[a]=t[a]);if(e&&e.defaultProps)for(a in t=e.defaultProps)void 0===s[a]&&(s[a]=t[a]);return{$$typeof:r,type:e,key:c,ref:d,props:s,_owner:l.current}}t.Fragment=s,t.jsx=c,t.jsxs=c},848(e,t,n){e.exports=n(20)},609(e){e.exports=window.React}};const t={};function n(a){const r=t[a];if(void 0!==r)return r.exports;const s=t[a]={exports:{}};return e[a](s,s.exports,n),s.exports}n.m=e,(()=>{const e=[];n.O=(t,a,r,s)=>{if(a){s=s||0;for(var o=e.length;o>0&&e[o-1][2]>s;o--)e[o]=e[o-1];return void(e[o]=[a,r,s])}let l=1/0;for(o=0;o=s)&&Object.keys(n.O).every(e=>n.O[e](a[i]))?a.splice(i--,1):(c=!1,s{const t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},(()=>{const e=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;let t;n.t=function(a,r){if(1&r&&(a=this(a)),8&r)return a;if("object"==typeof a&&a){if(4&r&&a.__esModule)return a;if(16&r&&"function"==typeof a.then)return a}const s=Object.create(null);n.r(s);const o={};t=t||[null,e({}),e([]),e(e)];for(var l=2&r&&a;("object"==typeof l||"function"==typeof l)&&!~t.indexOf(l);l=e(l))Object.getOwnPropertyNames(l).forEach(e=>o[e]=()=>a[e]);return o.default=()=>a,n.d(s,o),s}})(),n.d=(e,t)=>{if(Array.isArray(t))for(var a=0;aObject.hasOwn(e,t),n.r=e=>{Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={57:0,350:0};n.O.j=t=>0===e[t];const t=(t,a)=>{let[r,s,o]=a;var l,i,c=0;if(r.some(t=>0!==e[t])){for(l in s)n.o(s,l)&&(n.m[l]=s[l]);if(o)var d=o(n)}for(t&&t(a);cn(744));a=n.O(a)})(); \ No newline at end of file +(0,$p.__)("More settings (%d)","saddle"),n.advanced.length)},(0,a.createElement)(Tu,null,n.advanced.map(k))))),(0,a.createElement)(Lp,{open:pw(v,d),message:(0,$p.__)("You have unsaved changes.","saddle"),saveLabel:(0,$p.__)("Save","saddle"),discardLabel:(0,$p.__)("Discard","saddle"),saving:h,onSave:()=>{const n=function(e,t){const n={};return Object.keys(uw(e,t)).forEach(a=>{const r=e.find(e=>e.key===a);if(!r||!sw(r))return;const s=dw(r,t[a]);Number.isNaN(s)||"integer"===r.type&&!Number.isInteger(s)?n[a]="number":void 0!==r.minimum&&sr.maximum&&(n[a]="max")}),n}(v,d);Object.keys(n).length?m(Object.fromEntries(Object.entries(n).map(([e,t])=>[e,hw(v.find(t=>t.key===e),t)]))):(f(!0),m({}),tm(`preferences/${encodeURIComponent(e)}`,{method:"POST",data:{values:uw(v,d)}}).then(e=>{const n=e.fields||[];o(n),u(cw(lw(n,t))),b.success((0,$p.__)("Saved.","saddle"))}).catch(e=>{const t=e?.data?.field,n=e?.message||(0,$p.__)("Could not save that setting.","saddle");if(t&&v.some(e=>e.key===t)){m({[t]:n});const e=w.find(e=>e.split.advanced.some(e=>e.key===t));e&&y(t=>({...t,[e.section]:!0}))}else b.error(n)}).finally(()=>f(!1)))},onDiscard:()=>{u(cw(v)),m({})}}))}const yw=["allow","ask","off"],vw=e=>e&&e.askable?yw:["allow","off"],bw=e=>new Map((e||[]).map(e=>{return[e.short,(t=e,t&&yw.includes(t.mode)?t.mode:t&&!1===t.enabled?"off":"allow")];var t}));function ww(e,t,n){const a=new Map(e);return a.set(t,n),a}const _w=e=>({drafts_only:!(!e||!e.drafts_only),rehearsal:!(!e||!e.rehearsal),domain_enforced:!!(e&&e.domain&&e.domain.enforced),core_major_updates:!(!e||!e.core_major_updates)}),jw=({children:e,help:t})=>(0,a.createElement)("span",{className:"saddle-settings__label"},e,(0,a.createElement)(op,null,t));function kw({onRehearsalChanged:e}){const[t,n]=(0,l.useState)(null),r=(0,l.useRef)({});(0,l.useEffect)(()=>{tm("preferences").then(e=>n(_w(e))).catch(()=>n(_w({})))},[]);const s=({key:s,id:o,title:l,help:i,meta:c,label:d})=>(0,a.createElement)(Au,{key:s,title:i?(0,a.createElement)(jw,{help:i},l):l,description:c,actions:t?(0,a.createElement)(iu,{id:o,checked:!!t[s],onChange:()=>(a=>{const s=!t[a];r.current[a]=(r.current[a]||0)+1,n(e=>({...e,[a]:s})),tm("preferences",{method:"POST",data:{[a]:s}}).then(t=>{if(r.current[a]-=1,0===r.current[a]){const r=_w(t);n(e=>({...e,[a]:r[a]})),"rehearsal"===a&&e&&e(r.rehearsal)}b.success((0,$p.__)("Saved.","saddle"))}).catch(e=>{r.current[a]-=1,0===r.current[a]&&n(e=>({...e,[a]:!s})),b.error(e?.message||(0,$p.__)("Could not save that setting.","saddle"))})})(s),"aria-label":d||l}):(0,a.createElement)(Mu,{round:!0,width:32,height:18})});return(0,a.createElement)("section",{className:"saddle-section"},(0,a.createElement)(am,{title:(0,$p.__)("Safety","saddle")}),(0,a.createElement)(Tu,null,s({key:"drafts_only",id:"saddle-drafts-only-switch",title:(0,$p.__)("Publishing needs my OK","saddle"),help:(0,$p.__)("New posts and pages an app asks to publish or schedule are saved as drafts. Publishing or scheduling an existing item needs your confirmation. Edits to content that is already live still go through.","saddle"),meta:(0,$p.__)("Apps save drafts; you approve what goes live.","saddle")}),s({key:"rehearsal",id:"saddle-rehearsal-switch",title:(0,$p.__)("Practice mode","saddle"),help:(0,$p.__)("Apps can try anything their access allows and nothing is saved. Each tool that would change the site says what it would have done, and the attempt shows in Activity as rehearsed. Reading works as usual.","saddle"),meta:(0,$p.__)("Apps try changes but nothing is saved.","saddle")}),s({key:"domain_enforced",id:"saddle-domain-switch",title:(0,$p.__)("Stop writes if the site moves","saddle"),help:(0,$p.__)("If the site address changes, for example after copying it to another domain, apps are refused writes until you confirm their access again.","saddle"),meta:(0,$p.__)("After an address change, confirm access again.","saddle")}),s({key:"core_major_updates",id:"saddle-field-saddle-core_major_updates",title:(0,$p.__)("Major WordPress releases","saddle"),help:(0,$p.__)("Lets an app update WordPress to a new major version, such as 7.1 to 7.2, after you approve its preview. A major release has no automatic rollback and upgrades the database. Minor and security releases never need this. WordPress’s own “automatic updates for all new versions” in Dashboard → Updates is a separate setting.","saddle"),meta:(0,$p.__)("Apps can update WordPress to a new major version.","saddle")})))}const xw={allow:(0,$p.__)("Allow","saddle"),ask:(0,$p.__)("Ask first","saddle"),off:(0,$p.__)("Off","saddle")};function Mw({caps:e,listed:t,onChanged:n,onApplied:r}){const[s,o]=(0,l.useState)(""),[i,c]=(0,l.useState)(()=>bw(e)),[d,u]=(0,l.useState)(!1),p=(0,l.useRef)(0);(0,l.useEffect)(()=>c(bw(e)),[e]);const m=(0,l.useMemo)(()=>function(e,t,n="Other"){const a=new Map;return(e||[]).filter(e=>function(e,t){const n=String(t||"").trim().toLowerCase();return!n||[e.label,e.short,e.description].some(e=>String(e||"").toLowerCase().includes(n))}(e,t)).forEach(e=>{const t=e.category||n;a.has(t)||a.set(t,[]),a.get(t).push(e)}),[...a.entries()].map(([e,t])=>({category:e,tools:[...t].sort((e,t)=>String(e.label).localeCompare(String(t.label)))})).sort((e,t)=>e.category===n||t.category===n?e.category===n?1:-1:e.category.localeCompare(t.category))}(t,s,(0,$p.__)("Other","saddle")),[t,s]),h=(0,l.useCallback)(()=>{p.current-=1,0===p.current&&n&&n()},[n]);return(0,a.createElement)("div",{className:"saddle-tools"},(0,a.createElement)("p",{className:"saddle-tools__intro"},(0,$p.__)("Ask first: the app shows you the change and it waits under Needs your OK on the Dashboard. It runs only after you approve it there.","saddle")),(0,a.createElement)(Tu,null,(0,a.createElement)(Au,{title:(0,$p.__)("Recommended set","saddle"),description:(0,$p.__)("Ask first for plugins, themes, updates, settings and anything that deletes. Publishing needs your OK. Content edits stay allowed. Nothing you turned off is turned on.","saddle"),actions:(0,a.createElement)(Jd,{variant:"secondary",size:"sm",onClick:()=>{u(!0),tm("tool-modes/recommended",{method:"POST"}).then(e=>{const t=e&&e.moved||[];b.success(t.length?(0,$p.sprintf)(/* translators: %d: number of tools. */ /* translators: %d: number of tools. */ +(0,$p._n)("%d tool now asks you first. Publishing needs your OK.","%d tools now ask you first. Publishing needs your OK.",t.length,"saddle"),t.length):(0,$p.__)("Already set. Publishing needs your OK.","saddle")),r&&r(),n&&n()}).catch(e=>b.error(e?.message||(0,$p.__)("Could not save that change.","saddle"))).finally(()=>u(!1))},disabled:d},(0,$p.__)("Use it","saddle"))})),(0,a.createElement)("input",{type:"search",className:"saddle-tools__search",value:s,onChange:e=>o(e.target.value),placeholder:(0,$p.__)("Search tools…","saddle"),"aria-label":(0,$p.__)("Search tools","saddle")}),0===m.length&&(0,a.createElement)("p",{className:"saddle-tools__empty"},(0,$p.__)("No tools match.","saddle")),m.map(({category:e,tools:t})=>(0,a.createElement)("div",{key:e,className:"saddle-tools__group"},(0,a.createElement)("h3",{className:"saddle-tools__area"},e),(0,a.createElement)(Tu,null,t.map(e=>(0,a.createElement)(Au,{key:e.short,title:(0,a.createElement)(rp,{content:e.description},(0,a.createElement)("span",null,e.label)),actions:(0,a.createElement)(a.Fragment,null,e.new&&"ask"===i.get(e.short)&&(0,a.createElement)(rp,{content:(0,$p.__)("Added since you last chose. It asks you first until you pick.","saddle")},(0,a.createElement)("span",null,(0,a.createElement)(_u,null,(0,$p.__)("New","saddle")))),(0,a.createElement)(lu,{className:"saddle-tools__mode",size:"sm",options:vw(e).map(e=>({value:e,label:xw[e]})),value:i.get(e.short),onChange:t=>((e,t)=>{const n=i.get(e);n!==t&&(c(n=>ww(n,e,t)),p.current+=1,tm("tool-modes",{method:"POST",data:{tool:e,mode:t}}).then(()=>b.success((0,$p.__)("Saved.","saddle"))).catch(t=>{c(t=>ww(t,e,n)),b.error(t?.message||(0,$p.__)("Could not save that change.","saddle"))}).finally(h))})(e.short,t),"aria-label":(0,$p.sprintf)(/* translators: %s: tool name. */ /* translators: %s: tool name. */ +(0,$p.__)("What %s may do","saddle"),e.label)}))}))))))}function Nw(){const e=bh();return(0,a.createElement)(kh,{...e})}const Ew=["memory_max_entries","memory_core_budget"],Iw=["memory_recent_changes","memory_recent_limit"];function Cw({caps:e,loadCaps:t,onRehearsalChanged:n,children:r}){const[s]=(0,l.useState)(()=>"signin"===new URLSearchParams(window.location.search).get("section")),o=(0,l.useRef)(null),[i,c]=(0,l.useState)(!1),[d,u]=(0,l.useState)(0),p=(0,l.useMemo)(()=>(e=>(Array.isArray(e)?e:[]).filter(e=>!!e&&!1!==e.available))(e),[e]);return(0,l.useEffect)(()=>{if(!s)return;const e=[0,400,1200].map(e=>setTimeout(()=>{o.current&&o.current.scrollIntoView({block:"start"})},e));return()=>e.forEach(clearTimeout)},[s]),(0,a.createElement)(a.Fragment,null,(0,a.createElement)(kw,{key:d,onRehearsalChanged:n}),(0,a.createElement)("section",{className:"saddle-section"},(0,a.createElement)(am,{title:(0,$p.__)("Advanced","saddle")}),(0,a.createElement)("div",{className:"saddle-adv"},(0,a.createElement)(zu,{trigger:(0,$p.sprintf)(/* translators: %d: number of tools. */ /* translators: %d: number of tools. */ +(0,$p.__)("What each tool may do (%d)","saddle"),p.length)},(0,a.createElement)(Mw,{caps:e,listed:p,onChanged:t,onApplied:()=>u(e=>e+1)})),(0,a.createElement)(zu,{ref:o,id:"saddle-adv-signin",defaultOpen:s,trigger:(0,$p.__)("Sign-in for apps","saddle")},(0,a.createElement)("div",{className:"saddle-adv__signin"},(0,a.createElement)(Nw,null))),(0,a.createElement)(zu,{trigger:(0,$p.__)("Memory limits","saddle")},(0,a.createElement)(gw,{scope:"saddle",screen:"settings/general",keys:Ew,bare:!0})),(0,a.createElement)(zu,{trigger:(0,$p.__)("Recent changes","saddle")},(0,a.createElement)(gw,{scope:"saddle",screen:"settings/general",keys:Iw,bare:!0})),(0,a.createElement)(zu,{trigger:(0,$p.__)("Connection check","saddle"),onOpenChange:e=>e&&c(!0)},i&&(0,a.createElement)("div",{className:"saddle-adv__check"},(0,a.createElement)(Tm,null),(0,a.createElement)(Ch,null))))),r)}function Lw({tasks:e,navigate:t}){const n=function(e){return(Array.isArray(e)?e:[]).filter(e=>e&&!e.done)}(e);return n.length?(0,a.createElement)(Tu,{className:"saddle-module-setup"},n.map(e=>(0,a.createElement)(Au,{key:e.id,title:e.title,description:e.line||void 0,actions:e.action?(0,a.createElement)(Jd,{variant:"secondary",size:"sm",href:e.action.url,onClick:e.action.route&&t?n=>{sb(n)&&(n.preventDefault(),t({...e.action.route}))}:void 0,target:e.action.external?"_blank":void 0,rel:e.action.external?"noopener noreferrer":void 0},e.action.label):void 0}))):null}const Sw={ApplyBar:Lp,Badge:_u,BarList:Ou,BulkBar:function({count:e,children:t,onClear:n,className:a,...r}){return e?(0,I.jsxs)("div",{className:Fd("pp-bulkbar",a),role:"toolbar","aria-label":"Bulk actions",...r,children:[(0,I.jsxs)("span",{className:"pp-bulkbar__count",children:[e," selected"]}),(0,I.jsx)("div",{className:"pp-bulkbar__actions",children:t}),n&&(0,I.jsx)("button",{type:"button",className:"pp-bulkbar__clear",onClick:n,children:"Clear"})]}):null},Button:Jd,CalloutCard:Bu,Card:hu,CardContent:gu,CardFooter:yu,CardGrid:Gu,CardHeader:fu,Checkbox:du,ChecklistItem:up,Chip:_p,CodeBlock:lp,Collapsible:zu,DataTable:function({columns:e=[],rows:t=[],rowId:n=e=>e.id,sort:r,defaultSort:s,onSort:o,autoSort:l=!1,selectable:i=!1,selected:c=[],onSelect:d,onRowClick:u,rowActions:p,loading:m=!1,loadingRows:h=5,stickyHeader:f=!1,pageSize:g=0,empty:y=null,emptyTitle:v="No results",emptyDescription:b,emptyIcon:w,className:_,...j}){const[k,x]=(0,a.useState)(s||null),[M,N]=(0,a.useState)(1),E=void 0!==r?r:k,C=(0,a.useMemo)(()=>{if(!l||!E)return t;const n=e.find(e=>e.key===E.key),a=(null==n?void 0:n.sortAccessor)||(e=>e[E.key]),r="asc"===E.dir?1:-1;return[...t].sort((e,t)=>{const n=a(e),s=a(t);return null==n?1:null==s?-1:ns?1*r:0})},[l,E,t,e]),L=g>0?Math.ceil(C.length/g):1,S=Math.min(M,L),D=g>0?C.slice((S-1)*g,S*g):C,T=new Set(c),A=D.map(n),O=D.length>0&&A.every(e=>T.has(e)),P=A.some(e=>T.has(e));return m?(0,I.jsx)("div",{className:Fd("pp-table-wrap",_),children:(0,I.jsx)(Eu,{rows:h})}):t.length?(0,I.jsxs)(I.Fragment,{children:[(0,I.jsxs)(Lu,{className:_,stickyHeader:f,...j,children:[(0,I.jsx)("thead",{children:(0,I.jsxs)("tr",{children:[i&&(0,I.jsx)(Su,{className:"pp-table__th--check",children:(0,I.jsx)(du,{checked:O,indeterminate:!O&&P,onChange:()=>d&&d(O?c.filter(e=>!A.includes(e)):[...new Set([...c,...A])]),"aria-label":"Select all rows"})}),e.map(e=>(0,I.jsx)(Su,{align:e.align,"aria-sort":(null==E?void 0:E.key)===e.key?"asc"===E.dir?"ascending":"descending":void 0,children:e.sortable?(0,I.jsxs)("button",{type:"button",className:"pp-table__sort","aria-label":"string"==typeof e.header?`Sort by ${e.header}`:void 0,onClick:()=>(e=>{const t=(null==E?void 0:E.key)===e&&"asc"===E.dir?"desc":"asc";void 0===r&&x({key:e,dir:t}),o&&o(e)})(e.key),children:[e.header,(0,I.jsx)("span",{className:"pp-table__sort-icon","aria-hidden":"true",children:(null==E?void 0:E.key)===e.key?"asc"===E.dir?(0,I.jsx)(Ms,{size:13}):(0,I.jsx)(zr,{size:13}):(0,I.jsx)(Ns,{size:13,className:"pp-table__sort-icon--idle"})})]}):e.header},e.key)),p&&(0,I.jsx)(Su,{className:"pp-table__th--actions","aria-label":"Actions"})]})}),(0,I.jsx)("tbody",{children:D.map(t=>{const a=n(t),r=Boolean(u);return(0,I.jsxs)("tr",{className:Fd(T.has(a)&&"pp-table__row--selected",r&&"pp-table__row--clickable"),onClick:r?()=>u(t):void 0,tabIndex:r?0:void 0,onKeyDown:r?e=>{"Enter"!==e.key&&" "!==e.key||e.target!==e.currentTarget||(e.preventDefault(),u(t))}:void 0,children:[i&&(0,I.jsx)(Du,{className:"pp-table__td--check",onClick:e=>e.stopPropagation(),children:(0,I.jsx)(du,{checked:T.has(a),onChange:()=>(e=>d&&d(T.has(e)?[...T].filter(t=>t!==e):[...T,e]))(a),"aria-label":"Select row"})}),e.map(e=>(0,I.jsx)(Du,{align:e.align,children:e.render?e.render(t):t[e.key]},e.key)),p&&(0,I.jsx)(Du,{className:"pp-table__td--actions",align:"right",onClick:e=>e.stopPropagation(),children:p(t)})]},a)})})]}),g>0&&L>1&&(0,I.jsxs)("div",{className:"pp-table__footer",children:[(0,I.jsxs)("span",{className:"pp-table__count",children:[C.length," items"]}),(0,I.jsx)(Cu,{page:S,pageCount:L,onChange:N})]})]}):null!=y?y:(0,I.jsx)("div",{className:Fd("pp-table-wrap","pp-table-wrap--empty",_),children:(0,I.jsx)(xu,{icon:w,title:v,description:b})})},Dialog:pp,Drawer:Np,DropdownItem:Ip,DropdownMenu:Ep,EmptyState:xu,ErrorText:tu,Field:nu,FilterTabs:cp,HelpTip:op,Hint:eu,IconButton:Kd,Input:au,KeyValueList:Qu,Label:qd,LiveIndicator:ku,Meter:Yu,NativeSelect:su,Notice:Hu,PageHeader:Rp,Pagination:Cu,Popover:sp,ProgressBar:Wu,RadioGroup:pu,Row:Au,RowList:Tu,SearchInput:ou,SegmentedControl:mu,Select:lu,Skeleton:Mu,SkeletonCard:function({delay:e=!1,className:t,...n}){return(0,I.jsxs)("div",{className:Fd("pp-card pp-card--padded",e&&"pp-skeleton--delay",t),"aria-busy":"true",...n,children:[(0,I.jsx)(Mu,{height:16,width:"40%"}),(0,I.jsx)("div",{style:{height:12}}),(0,I.jsx)(Nu,{lines:2})]})},SkeletonTable:Eu,SkeletonText:Nu,Snippet:ip,Spinner:Ru,StatCard:bu,StatGrid:wu,StatusDot:ju,Switch:iu,Tabs:Bp,Textarea:ru,toast:b,Tooltip:rp,useConfirm:fp,VisuallyHidden:Ap,icons:Cv},Dw={SectionHeader:am,SettingsForm:gw,WaitingLine:Eh},Tw=["settingsCards","tabs","screens","homeCards","connectionCards","mount","settings-form","waiting-line","ui-wide","view","drill-in","subtabs","page-layout"];"undefined"!=typeof window&&(window.saddleShell={version:2,has:e=>Tw.includes(e)});const Aw=(e,t)=>n=>{var a;return!(!n||t.some(e=>!n[e])||(null!==(a=n.requiresShell)&&void 0!==a?a:1)>2&&(console.warn(`[saddle] ${e} "${n.id}" needs shell v${n.requiresShell}, this is v2 — skipped. Update Saddle.`),1))},Ow=(e,t)=>{var n,a;return(null!==(n=e.order)&&void 0!==n?n:50)-(null!==(a=t.order)&&void 0!==a?a:50)};function Pw(){const e=(0,wb.applyFilters)("saddle.admin.settingsCards",[],{shellVersion:2,ui:Sw});return(Array.isArray(e)?e:[]).filter(Aw("settings card",["id","Component"])).sort(Ow)}function zw(){const e=(0,wb.applyFilters)("saddle.admin.tabs",[],{shellVersion:2,ui:Sw,icons:[]});return(Array.isArray(e)?e:[]).filter(Aw("tab",["id","label","Component"])).sort(Ow)}function Rw(){const e=(0,wb.applyFilters)("saddle.admin.screens",[],{shellVersion:2,ui:Sw,kit:Dw});return(Array.isArray(e)?e:[]).filter(e=>e&&"string"==typeof e.module&&"string"==typeof e.tab&&(void 0===e.sub||"string"==typeof e.sub)&&e.Component)}function Zw({where:e,navigate:t}){return(0,l.useMemo)(()=>function(e){const t="home"===e?"saddle.admin.homeCards":"saddle.admin.connectionCards",n=(0,wb.applyFilters)(t,[],{shellVersion:2,ui:Sw,kit:Dw});return(Array.isArray(n)?n:[]).filter(Aw("card",["id","Component"])).sort(Ow)}(e),[e]).map(e=>(0,a.createElement)(e.Component,{key:e.id,ui:Sw,kit:Dw,navigate:t,shellVersion:2}))}function Ww({module:e,tab:t}){const n=(0,l.useRef)(null);return(0,l.useEffect)(()=>{n.current&&(0,wb.doAction)("saddle.admin.mount",n.current,{module:e,tab:t})},[e,t]),(0,a.createElement)("div",{id:"saddle-module-slot",ref:n})}function Yw({area:e,tab:t,sub:n,view:r,args:s,navigate:o,header:i}){const c=(0,l.useMemo)(Rw,[]),d=(0,l.useMemo)(()=>i?{drillIn:i.drillIn}:void 0,[i]);(0,l.useEffect)(()=>()=>i&&i.clear(),[i]);const u=Xm(e,t),p=e.tabs[0]&&e.tabs[0].key===t&&(!u.length||u[0].key===n);if("mount"===e.content)return(0,a.createElement)(Ww,{module:e.key,tab:t});const m=function(e,t,n,a){const r=(Array.isArray(e)?e:[]).filter(e=>e&&e.module===t&&e.tab===n);return(a?r.find(e=>e.sub===a):null)||r.find(e=>!e.sub)||null}(c,e.key,t,n);return m||"settings"!==t?m?(0,a.createElement)(a.Fragment,null,p&&!r&&(0,a.createElement)(Lw,{tasks:Jp.setup,navigate:o}),(0,a.createElement)(m.Component,{ui:Sw,kit:Dw,module:e.key,tab:t,sub:n||"",view:r||"",args:s||{},navigate:o,header:d,api:tm,shellVersion:2})):(0,a.createElement)(Hu,{tone:"warning"},(0,$p.sprintf)(/* translators: %s: module name, such as Saddle Analytics. */ /* translators: %s: module name, such as Saddle Analytics. */ +(0,$p.__)("%s did not add this screen. Updating it may add it.","saddle"),e.product||e.title)):(0,a.createElement)(gw,{scope:e.key})}function Gw({openConnect:e,closeConnect:t,connectOpen:n,connectApp:r,openWizard:s,refreshClients:o,removeClient:l,clients:i,tier:c,navigate:d}){const u=bh();return(0,a.createElement)(a.Fragment,null,(0,a.createElement)(zv,{clients:i,onClientsChanged:o,onClientRemoved:l,siteTier:c,onConnect:t=>e(t)}),(0,a.createElement)(Zw,{where:"connections",navigate:d}),(0,a.createElement)(Np,{open:n,onOpenChange:e=>!e&&t(),title:(0,$p.__)("Connect an app","saddle"),size:"md"},(0,a.createElement)(gh,{oauth:u.oauth,onKey:e=>{t(),s(e)},onConnected:o,initialApp:r})))}function Bw({extTabs:e,caps:t,loadCaps:n,onRehearsalChanged:r}){const s=(0,l.useMemo)(Pw,[]),o=Hm(Jp.areas,"home");return(0,a.createElement)(a.Fragment,null,(0,a.createElement)(Cw,{caps:t,loadCaps:n,onRehearsalChanged:r},s.map(e=>(0,a.createElement)("section",{key:e.id,className:"saddle-stack"},(e.title||e.label)&&(0,a.createElement)(am,{title:e.title||e.label}),(0,a.createElement)(e.Component,{ui:Sw,shellVersion:2}))),e.map(e=>(0,a.createElement)("section",{key:e.id,className:"saddle-stack"},(0,a.createElement)(am,{title:e.label}),(0,a.createElement)(e.Component,{ui:Sw,shellVersion:2})))),o&&(0,a.createElement)("div",null,(0,a.createElement)(Jd,{href:Km(o.url,"setup","1"),variant:"link",size:"sm"},(0,$p.__)("Run setup again","saddle"))))}function Vw({area:e,tab:t}){return"home"!==e.key||t&&"overview"!==t?(0,a.createElement)("div",{className:"saddle-skeleton"},(0,a.createElement)(Ap,null,(0,$p.__)("Loading","saddle")),(0,a.createElement)(Mu,{height:15,width:120,"aria-hidden":"true"}),(0,a.createElement)(Tu,{loading:!0,loadingRows:3,"aria-hidden":"true"})):(0,a.createElement)(Wb,null)}function Uw(e){const{area:t,tab:n,sub:r,view:s,args:o,navigate:i,header:c,tier:d,caps:u,clients:p,paused:m,wizardOpen:h,wizardApp:f,openWizard:g,closeWizard:y,refreshClients:v,removeClient:b,connectOpen:w,connectApp:_,openConnect:j,closeConnect:k,pausing:x,onTogglePause:M,loadCaps:N,onRehearsalChanged:E,onboarding:I,onHideSetup:C}=e,L=(0,l.useMemo)(zw,[]);if(t.module)return(0,a.createElement)(Yw,{area:t,tab:n,sub:r,view:s,args:o,navigate:i,header:c});switch(`${t.key}/${n}`){case"home/overview":return(0,a.createElement)(a.Fragment,null,(0,a.createElement)(Gb,{tier:d,clients:p,paused:m,pausing:x,onTogglePause:M,caps:u,onNavigate:i,onConnect:()=>j(),onboarding:I,onHideSetup:C,homeUrl:t.url}),(0,a.createElement)(Zw,{where:"home",navigate:i}));case"connections/apps":return h?(0,a.createElement)(xh,{tier:d,clients:p,onExit:y,onClientsChanged:v,initialApp:f}):(0,a.createElement)(Gw,{openConnect:j,closeConnect:k,connectOpen:w,connectApp:_,openWizard:g,refreshClients:v,removeClient:b,clients:p,tier:d,navigate:i});case"settings/general":return(0,a.createElement)(Bw,{extTabs:L,caps:u,loadCaps:N,onRehearsalChanged:E});case"services/overview":return(0,a.createElement)(aw,null);case"context/overview":return(0,a.createElement)($b,null)}return null}const Fw=Jp.areas||[],Hw=Hm(Fw,Jp.area)||Hm(Fw,"home")||{key:"home",title:(0,$p.__)("Home","saddle"),url:window.location.href,tabs:[{key:"overview",label:(0,$p.__)("Home","saddle")}]},Qw=()=>new URLSearchParams(window.location.search),Xw=Array.isArray(Jp.notices)?Jp.notices:[],$w=()=>"connections"===Hw.key?new URLSearchParams(window.location.search).get("add"):null,Jw=()=>"connections"===Hw.key?new URLSearchParams(window.location.search).get("key"):null,Kw=()=>null!==$w(),qw=()=>null!==Jw(),e_=e=>e&&"1"!==e?e:null;function t_(){const e=Hw,t=(0,l.useMemo)(()=>"home"===e.key?function(e,t){const n=String(t||"").replace(/^#/,""),a=Object.prototype.hasOwnProperty.call(Fm,n)?Fm[n]:null;if(!a)return null;const r=Jm(e,a.area,a.tab);return r?a.anchor?`${r}#${a.anchor}`:r:null}(Fw,window.location.hash):function(e,t,n){return e&&"settings"===e.key?"services"!==new URLSearchParams(n||"").get("section")?null:Jm(t,"services")||null:null}(e,Fw,window.location.search),[e]);(0,l.useEffect)(()=>{t&&window.location.replace(t)},[t]);const[n,r]=(0,l.useState)(()=>{const t=qm(window.location.search),n=Qm(e,t.tab||Jp.tab),a=n!==Jp.tab||t.tab?"":Jp.sub;return{tab:n,sub:$m(e,n,t.sub||a||""),view:t.view,args:t.args}}),{tab:s,sub:o,view:i,args:c}=n,d=(0,l.useCallback)(t=>r({tab:t,sub:$m(e,t,""),view:"",args:{}}),[e]),[u,p]=(0,l.useState)(null),m=(0,l.useMemo)(()=>function(e,t,n,a=""){const r=e=>!!e&&e.tab===t&&e.view===n&&(e.sub||"")===a,s=()=>e(e=>r(e)?null:e);return{drillIn:o=>{const l=o&&o.title,i="string"==typeof l||"number"==typeof l?String(l).trim():"";i?e(e=>r(e)&&e.title===i?e:{title:i,tab:t,sub:a,view:n}):s()},clear:s}}(p,s,i,o),[s,i,o]),[h,f]=(0,l.useState)(null),[g,y]=(0,l.useState)([]),[v,w]=(0,l.useState)([]),[_,j]=(0,l.useState)(null),[k,x]=(0,l.useState)(!0),[M,N]=(0,l.useState)(()=>"home"===Hw.key&&Qw().has("setup")),[E,I]=(0,l.useState)(!1),C=(0,l.useRef)(!1),[L,S]=(0,l.useState)(!1),[D,T]=(0,l.useState)(!1),[A,O]=(0,l.useState)(!1),[P,z]=(0,l.useState)(!0),[R,Z]=(0,l.useState)(null),[W,Y]=(0,l.useState)(Xw),[G,B]=(0,l.useState)(!1),[V,U]=(0,l.useState)(qw),[F,H]=(0,l.useState)(()=>e_(Jw())),[Q,X]=(0,l.useState)(Kw),[$,J]=(0,l.useState)(()=>e_($w())),K=(0,l.useCallback)(t=>{const n=Qm(e,t);d(n),U(!1);const a=Jm(Fw,e.key,n);a&&window.history&&window.history.pushState&&window.history.pushState({},"",a)},[e,d]),q=(0,l.useCallback)(t=>{const a=Qm(e,t.tab||n.tab);let s="";"sub"in t?s=t.sub||"":a===n.tab&&(s=n.sub);const o={tab:a,sub:$m(e,a,s),view:t.view||"",args:t.args||{}};r(o),U(!1);const l=eh(Fw,e.key,o);l&&window.history&&window.history.pushState&&window.history.pushState({},"",l)},[e,n.tab,n.sub]);(0,l.useEffect)(()=>{if(t||!window.history||!window.history.replaceState)return;const e=function(e,t){const n=new URLSearchParams(e||""),a=t&&t.tab||"",r=t&&t.sub||"";let s=!1;if(n.has("tab")&&n.get("tab")!==a&&(n.delete("tab"),s=!0),n.has("sub")&&n.get("sub")!==r){const e=String(n.get("sub")).replace(/[^a-z0-9_-]/gi,"").toLowerCase();r&&e===r?n.set("sub",r):n.delete("sub"),s=!0}if(!s)return null;const o=n.toString();return o?`?${o}`:""}(window.location.search,n);null!==e&&window.history.replaceState(window.history.state,"",window.location.pathname+e+window.location.hash)},[]),(0,l.useEffect)(()=>{const t=()=>{const t=qm(window.location.search),n=Qm(e,t.tab);r({tab:n,sub:$m(e,n,t.sub),view:t.view,args:t.args}),U(qw()),X(Kw()),J(e_($w()))};return window.addEventListener("popstate",t),()=>window.removeEventListener("popstate",t)},[e]);const ee=(0,l.useCallback)(t=>{const n=function(e){if(e&&"object"==typeof e){const t=["sub","view","args"].some(t=>t in e);if(!e.area&&!e.tab&&!t)return null;const n={area:e.area||"",tab:e.tab||""};return"sub"in e&&(n.sub="string"==typeof e.sub?e.sub:""),t&&(n.view="string"==typeof e.view?e.view:"",n.args=e.args&&"object"==typeof e.args?e.args:{}),n}const t=Object.prototype.hasOwnProperty.call(Fm,e)?Fm[e]:null;return t?{area:t.area,tab:t.tab}:null}(t);if(!n)return;if(!n.area||n.area===e.key)return void("view"in n?q(n):K(n.tab));const a=eh(Fw,n.area,n);a&&window.location.assign(a)},[e,K,q]),te=(0,l.useCallback)(e=>tm("onboarding",{method:"POST",data:e}).then(e=>{const t=["new","active"].includes(e.first_run.state);C.current&&t||j(e)}).catch(()=>{}),[]),ne=(0,l.useCallback)(()=>tm("capabilities").then(e=>{y(e.capabilities||[]),f(e.current_tier)}),[]),ae=(0,l.useCallback)(()=>tm("clients").then(e=>w(e.clients||[])),[]),re=(0,l.useCallback)(()=>ae().catch(e=>b.error((0,$p.sprintf)(/* translators: %s: error message. */ /* translators: %s: error message. */ +(0,$p.__)("Couldn’t refresh the connections list: %s","saddle"),e.message))),[ae]),se=(0,l.useCallback)(e=>{w(t=>t.filter(t=>t.uuid!==e))},[]);(0,l.useEffect)(()=>{if(!t){if(window.history&&window.history.replaceState){const e=new URL(window.location.href),t=["password","user_login","site_url","connected","rejected"];t.some(t=>e.searchParams.has(t))&&(t.forEach(t=>e.searchParams.delete(t)),window.history.replaceState({},"",e.pathname+e.search+e.hash))}Promise.all([ne(),ae(),tm("onboarding").then(j).catch(()=>{}),tm("preferences").then(e=>{x(!!e.onboarded),S(!!e.paused),O(!!e.domain_warning)})]).catch(e=>{e&&(e.data&&401===e.data.status||"invalid_json"===e.code)?B(!0):Z(e.message)}).finally(()=>z(!1))}},[t,ne,ae]),(0,l.useEffect)(()=>{const e=window.location.hash.replace("#","");if(P||!e)return;const t=[0,400,1200].map(t=>setTimeout(()=>{const t=document.getElementById(e);t&&t.scrollIntoView()},t));return()=>t.forEach(clearTimeout)},[P]);const oe=Jm(Fw,"connections","apps")||window.location.href,le=()=>{const e=!L;T(!0),tm("preferences",{method:"POST",data:{paused:e}}).then(e=>S(!!e.paused)).catch(e=>Z(e.message)).finally(()=>T(!1))},ie=(0,l.useCallback)(e=>{Y(t=>t.filter(t=>t.id!==e.id)),tm(`notices/${encodeURIComponent(e.id)}/dismiss`,{method:"POST"}).catch(t=>{Y(t=>[...t,e]),b.error(t.message)})},[]),{slot:ce,rest:de}=function(e){const t=(Array.isArray(e)?e:[]).filter(e=>e&&e.id&&e.message);if(!t.length)return{slot:null,rest:[]};let n=0;return t.forEach((e,a)=>{ib(e)t!==n)}}([...R?[{id:"app-error",severity:"error",message:R}]:[],...A&&!V?[{id:"domain-drift",severity:"warning",message:(0,$p.__)("This site’s address changed after AI write access was turned on, which can mean a copy or migration carried over live credentials. If you didn’t expect that, review your AI apps and disconnect any you don’t recognise.","saddle"),action:{label:(0,$p.__)("Mark as expected","saddle"),onClick:()=>{tm("preferences",{method:"POST",data:{tier:h}}).then(e=>O(!!e.domain_warning)).catch(e=>Z(e.message))}}}]:[],...W]);let ue=null;const pe=_?function(e,t){if(t)return!0;const n=e&&e.state;return"new"===n||"active"===n}(_.first_run,M):!k||M,me=(()=>{const e=_?_.first_run:{state:"new",step:"",app:""},t=Qw();return M&&"try"===t.get("step")&&t.get("app")?{...e,step:"try",app:t.get("app")}:e})();return ue=t?null:P?(0,a.createElement)(bb,{area:e,tab:s,sub:o,view:i,onTab:K,onSub:e=>q({sub:e}),loading:!0},(0,a.createElement)(Vw,{area:e,tab:s})):G?(0,a.createElement)(rb,{onRetry:()=>window.location.reload()}):"home"===e.key&&pe?(0,a.createElement)(bb,{area:e,tab:"setup",showTabs:!1,crumb:(0,$p.__)("Welcome","saddle"),status:{paused:L,pausing:D,onToggle:le},moreNotices:[ce,...de].filter(Boolean),onDismissNotice:ie},(0,a.createElement)("div",{className:"saddle-app--setup"},(0,a.createElement)(Jv,{tier:h,clients:v,firstRun:me,send:te,onTierSaved:f,onClientsChanged:re,onFinish:()=>{if(C.current=!0,x(!0),N(!1),j(e=>e&&["new","active"].includes(e.first_run.state)?{...e,first_run:{...e.first_run,state:"skipped"}}:e),window.history&&window.history.replaceState){let e=window.location.href;["setup","step","app"].forEach(t=>{e=Km(e,t,null)}),window.history.replaceState({},"",e)}K("overview")}}))):(0,a.createElement)(bb,{area:e,tab:s,sub:o,view:i,drill:u,onTab:K,onSub:e=>q({sub:e}),onBack:()=>q({sub:o}),status:{paused:L,pausing:D,onToggle:le},notices:!V,notice:ce,moreNotices:de,onDismissNotice:ie},(0,a.createElement)("div",{className:"saddle-tabpane",key:`${e.key}/${s}${i?`/${i}`:""}${V?"/add":""}`},(0,a.createElement)(Uw,{area:e,tab:s,sub:o,view:i,args:c,navigate:ee,header:m,tier:h,caps:g,clients:v,paused:L,pausing:D,wizardOpen:V,wizardApp:F,openWizard:(t=null)=>{const n=Km(oe,"key",t||"1");"connections"===e.key?(d("apps"),X(!1),H(t),U(!0),window.history&&window.history.pushState&&window.history.pushState({},"",n)):window.location.assign(n)},closeWizard:()=>{U(!1),window.history&&window.history.replaceState&&window.history.replaceState({},"",oe),re()},connectOpen:Q,connectApp:$,openConnect:(t=null)=>{const n=Km(oe,"add",t||"1");"connections"===e.key?(d("apps"),J(t),X(!0),window.history&&window.history.pushState&&window.history.pushState({},"",n)):window.location.assign(n)},closeConnect:()=>{X(!1),J(null),window.history&&window.history.replaceState&&window.history.replaceState({},"",oe),re()},refreshClients:re,removeClient:se,onTogglePause:le,loadCaps:ne,onboarding:_,onHideSetup:()=>te({event:"setup.hide",module:"home"})})),!V&&"home"===e.key&&"overview"===s&&!E&&function(e){if(!e||!e.first_run)return!1;const{state:t,tier_choice:n}=e.first_run;return"done"===t&&""!==(n||"")&&!(e.user&&e.user.tour_done)}(_)&&(0,a.createElement)(eb,{onFinish:()=>{I(!0),te({event:"tour.done"})}})),(0,a.createElement)(ap,null,(0,a.createElement)(hp,null,ue,(0,a.createElement)(Ku,null)))}const n_=window.saddleData||{};var a_;o().use((({root:e,homeUrl:t})=>(n,a)=>{const r=tb();if(""===r||!((e,t,n)=>"string"==typeof e.url?!(!t||0!==e.url.indexOf(t))||!!n&&0===e.url.indexOf(n)&&-1!==e.url.indexOf("rest_route="):"string"==typeof e.path)(n,e,t))return a(n);const s={...n};return"string"==typeof s.path&&(s.path=nb(s.path,r)),"string"==typeof s.url&&(s.url=nb(s.url,r)),a(s)})({root:n_.root,homeUrl:n_.homeUrl})),a_=n_.nonce,!o().nonceMiddleware&&a_&&(o().nonceMiddleware=o().createNonceMiddleware(a_),o().use(o().nonceMiddleware)),o().nonceMiddleware,n_.root&&o().use(o().createRootURLMiddleware(n_.root));const r_=()=>{const e=document.getElementById("saddle-root");e&&(0,l.createRoot)(e).render((0,a.createElement)(t_,null))};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",r_):r_()},20(e,t,n){var a=n(609),r=Symbol.for("react.element"),s=Symbol.for("react.fragment"),o=Object.prototype.hasOwnProperty,l=a.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,i={key:!0,ref:!0,__self:!0,__source:!0};function c(e,t,n){var a,s={},c=null,d=null;for(a in void 0!==n&&(c=""+n),void 0!==t.key&&(c=""+t.key),void 0!==t.ref&&(d=t.ref),t)o.call(t,a)&&!i.hasOwnProperty(a)&&(s[a]=t[a]);if(e&&e.defaultProps)for(a in t=e.defaultProps)void 0===s[a]&&(s[a]=t[a]);return{$$typeof:r,type:e,key:c,ref:d,props:s,_owner:l.current}}t.Fragment=s,t.jsx=c,t.jsxs=c},848(e,t,n){e.exports=n(20)},609(e){e.exports=window.React}};const t={};function n(a){const r=t[a];if(void 0!==r)return r.exports;const s=t[a]={exports:{}};return e[a](s,s.exports,n),s.exports}n.m=e,(()=>{const e=[];n.O=(t,a,r,s)=>{if(a){s=s||0;for(var o=e.length;o>0&&e[o-1][2]>s;o--)e[o]=e[o-1];return void(e[o]=[a,r,s])}let l=1/0;for(o=0;o=s)&&Object.keys(n.O).every(e=>n.O[e](a[i]))?a.splice(i--,1):(c=!1,s{const t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},(()=>{const e=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;let t;n.t=function(a,r){if(1&r&&(a=this(a)),8&r)return a;if("object"==typeof a&&a){if(4&r&&a.__esModule)return a;if(16&r&&"function"==typeof a.then)return a}const s=Object.create(null);n.r(s);const o={};t=t||[null,e({}),e([]),e(e)];for(var l=2&r&&a;("object"==typeof l||"function"==typeof l)&&!~t.indexOf(l);l=e(l))Object.getOwnPropertyNames(l).forEach(e=>o[e]=()=>a[e]);return o.default=()=>a,n.d(s,o),s}})(),n.d=(e,t)=>{if(Array.isArray(t))for(var a=0;aObject.hasOwn(e,t),n.r=e=>{Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{const e={57:0,350:0};n.O.j=t=>0===e[t];const t=(t,a)=>{let[r,s,o]=a;var l,i,c=0;if(r.some(t=>0!==e[t])){for(l in s)n.o(s,l)&&(n.m[l]=s[l]);if(o)var d=o(n)}for(t&&t(a);cn(744));a=n.O(a)})(); \ No newline at end of file diff --git a/admin/build/style-index-rtl.css b/admin/build/style-index-rtl.css index 8c321007..bf34ee37 100644 --- a/admin/build/style-index-rtl.css +++ b/admin/build/style-index-rtl.css @@ -1 +1 @@ -.pp-app,.pp-scope,.saddle-app,body.saddle-admin-page{--saddle-brand:#d000c4;--saddle-brand-hover:#b000a6;--saddle-brand-press:#900088;--saddle-brand-text:#a3009a;--saddle-brand-tint:#fde0fb;--saddle-brand-soft:#f9c2f4;--pp-n-50:#f6f7f7;--pp-n-100:#f0f0f1;--pp-n-200:#f6f7f7;--pp-n-300:#dcdcde;--pp-n-400:#c3c4c7;--pp-n-500:#a7aaad;--pp-n-600:#646970;--pp-n-700:#646970;--pp-n-800:#3c434a;--pp-n-900:#2c3338;--pp-n-950:#1d2327;--pp-canvas:#f0f0f1;--pp-surface:#fff;--pp-surface-hover:#f6f7f7;--pp-accent:var( --wp-admin-theme-color,#2271b1 );--pp-accent-hover:var( --wp-admin-theme-color-darker-10,#135e96 );--pp-accent-text:var( --wp-admin-theme-color,#2271b1 );--pp-accent-tint:color-mix(in srgb,var( --pp-accent ) 7%,#fff);--pp-accent-muted:color-mix(in srgb,var( --pp-accent ) 12%,transparent);--pp-on-accent:#fff;--pp-focus:var( --wp-admin-theme-color,#2271b1 );--pp-action:var( --saddle-brand );--pp-action-hover:var( --saddle-brand-hover );--pp-on-action:#fff;--pp-success:#007017;--pp-success-bg:#edfaef;--pp-info:#2271b1;--pp-info-bg:#f0f6fc;--pp-warning:#8a4f00;--pp-warning-bg:#fef8ee;--pp-danger:#b32d2e;--pp-danger-hover:#8a2424;--pp-danger-bg:#fcf0f1;--pp-r-sm:4px;--pp-r:4px;--pp-r-lg:8px;--pp-shadow-xs:0 1px 2px rgba(0,0,0,.04);--pp-shadow-sm:0 1px 2px rgba(0,0,0,.04);--pp-shadow-btn:none;--pp-shadow-btn-hover:none;--pp-text-2xs:12px;--pp-text-xs:13px;--pp-text-sm:13px;--pp-text-md:15px;--pp-text-lg:15px;--pp-text-xl:15px;--pp-text-2xl:15px;--pp-text-3xl:20px;--pp-text-4xl:20px}.saddle-app,body.saddle-admin-page{--s-canvas:var( --pp-canvas );--s-bg:var( --pp-surface );--s-fg:var( --pp-text );--s-fg-muted:var( --pp-text-secondary );--s-fg-subtle:var( --pp-text-muted );--s-border:var( --pp-border );--s-border-strong:var( --pp-border-strong );--s-surface-2:var( --pp-surface-alt );--s-surface-3:var( --pp-surface-hover );--s-action:var( --pp-action );--s-on-action:var( --pp-on-action );--s-accent:var( --pp-accent );--s-accent-text:var( --pp-accent-text );--s-brand:var( --saddle-brand );--s-safe:var( --pp-success );--s-safe-bg:var( --pp-success-bg );--s-safe-text:var( --pp-success );--s-active:var( --pp-warning );--s-active-bg:var( --pp-warning-bg );--s-active-text:var( --pp-warning );--s-danger:var( --pp-danger );--s-danger-text:var( --pp-danger );--s-rail-safe:var( --s-safe-text );--s-rail-change:var( --pp-accent );--s-rail-remove:var( --s-active-text );--s-focus:var( --pp-focus );--s-field-edge:#8c8f94;--s-tab-indicator:#50575e;--s-nav-hover:var( --pp-surface-hover );--s-overlay:#1d2327;--s-overlay-hover:#101517;--s-paused-mark:#b26200;--s-paused-edge:#f5e1c0;--s-typing:var( --pp-n-500 );--s-code-bg:#1d2327;--s-shadow-sm:var( --pp-shadow-sm );--s-shadow-md:var( --pp-shadow-md );--s-text-sm:var( --pp-text-2xs );--s-text:var( --pp-text-sm );--s-text-title:var( --pp-text-md );--s-radius:var( --pp-r );--s-radius-lg:var( --pp-r-lg );--s-radius-pill:var( --pp-r-pill );--s-ease:cubic-bezier(0.4,0,0.2,1);--s-dur:160ms}.pp-app,.pp-scope,.saddle-app,body.saddle-admin-page{--saddle-chart-ink:var( --saddle-brand );--saddle-chart-ink-soft:color-mix(in srgb,var( --saddle-brand ) 14%,transparent);--saddle-chart-compare:#707070;--saddle-chart-grid:var( --pp-border );--saddle-chart-text:var( --pp-text-secondary )}.pp-tone--success{--pp-tone-text:var( --pp-success )}.pp-tone--info{--pp-tone-text:var( --pp-info )}.pp-tone--warning{--pp-tone-text:var( --pp-warning )}.pp-tone--danger{--pp-tone-text:var( --pp-danger )}.pp-applybar,.pp-bulkbar,.pp-coachmark,.pp-toast,.pp-tooltip{--pp-action:var( --s-overlay );--pp-action-hover:var( --s-overlay-hover )}.pp-btn--danger,.pp-btn--primary{--pp-accent-text:var( --pp-on-action );--pp-accent-hover:var( --pp-on-action )}.pp-btn--secondary{--pp-accent-text:var( --pp-text );--pp-accent-hover:var( --pp-text )}.pp-btn--ghost{--pp-accent-text:var( --pp-text-secondary );--pp-accent-hover:var( --pp-text )}.pp-code--dark{--pp-code-bg:var( --s-code-bg )}.pp-code .pp-code__pre code{background:none;font-size:inherit;margin:0;padding:0}.saddle-app .pp-card__title,body.saddle-admin-page .pp-card__title{font-size:var(--s-text);font-weight:600}.saddle-app .pp-rowlist__title,body.saddle-admin-page .pp-rowlist__title{font-weight:600}.saddle-app .pp-control,body.saddle-admin-page .pp-control{--pp-border:var( --s-field-edge );--pp-border-strong:var( --s-fg-muted )}.saddle-app input.pp-control:not(:focus),body.saddle-admin-page input.pp-control:not(:focus){border-color:var(--pp-border)}.saddle-app input.pp-control:hover:not(:focus):not(:disabled),body.saddle-admin-page input.pp-control:hover:not(:focus):not(:disabled){border-color:var(--pp-border-strong)}.saddle-app .pp-switch:not([aria-checked=true]),body.saddle-admin-page .pp-switch:not([aria-checked=true]){background:var(--s-bg);box-shadow:inset 0 0 0 1px var(--s-field-edge)}.saddle-app .pp-switch:not([aria-checked=true]):after,body.saddle-admin-page .pp-switch:not([aria-checked=true]):after{background:var(--s-field-edge);box-shadow:none}.saddle-app .pp-switch:not([aria-checked=true]):hover:not(:disabled),body.saddle-admin-page .pp-switch:not([aria-checked=true]):hover:not(:disabled){background:var(--s-bg);box-shadow:inset 0 0 0 1px var(--s-fg-muted)}.saddle-app .pp-switch:not([aria-checked=true]):hover:not(:disabled):after,body.saddle-admin-page .pp-switch:not([aria-checked=true]):hover:not(:disabled):after{background:var(--s-fg-muted)}.saddle-app .pp-rowlist__icon,body.saddle-admin-page .pp-rowlist__icon{background:transparent}.saddle-app .pp-rowlist,body.saddle-admin-page .pp-rowlist{box-shadow:var(--pp-shadow-xs)}.saddle-app .pp-card .pp-rowlist,.saddle-app .saddle-adv .pp-card,.saddle-app .saddle-adv .pp-rowlist,body.saddle-admin-page .pp-card .pp-rowlist,body.saddle-admin-page .saddle-adv .pp-card,body.saddle-admin-page .saddle-adv .pp-rowlist{box-shadow:none}body.saddle-admin-page #wpcontent{background:var(--s-canvas);padding-inline-start:0}body.saddle-admin-page #wpbody-content{background:var(--s-canvas);box-sizing:border-box;min-height:calc(100vh - var(--wp-admin--admin-bar--height, 32px));padding-bottom:0}body.saddle-admin-page .wrap{margin:0}body.saddle-admin-page #wpfooter{display:none}.saddle-app{box-sizing:border-box;color:var(--s-fg);-webkit-font-smoothing:antialiased}.saddle-app--loading{display:flex;justify-content:center;padding:120px 0}.saddle-app--setup{padding-block-end:var(--pp-space-8)}.saddle-app [role=radio]:focus-visible,.saddle-app a:focus-visible,.saddle-app button:focus-visible{box-shadow:none;outline:2px solid var(--s-focus);outline-offset:2px}.saddle-app button{touch-action:manipulation}.saddle-app h1,.saddle-app h2,.saddle-app h3,.saddle-app h4{color:inherit}.saddle-app a:not([class]){color:var(--s-accent-text)}.saddle-app a:not([class]):hover{color:var(--pp-accent-hover)}.saddle-foreign{max-height:60vh;max-width:min(520px,100vw - 32px);overflow-y:auto}.saddle-foreign__button{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-pill);color:var(--s-fg-muted);cursor:pointer;display:inline-flex;font-size:var(--s-text);gap:6px;height:28px;line-height:1;padding:0 10px}.saddle-foreign__button svg{height:20px;width:20px}.saddle-foreign__button:hover{border-color:var(--s-border-strong);color:var(--s-fg)}.saddle-foreign__head{color:var(--s-fg-subtle);font-size:var(--s-text);margin-bottom:8px}.saddle-foreign__saddle{display:grid;gap:8px;margin-bottom:12px}.saddle-foreign__list{color-scheme:light}.saddle-foreign__list .notice,.saddle-foreign__list div.error,.saddle-foreign__list div.updated{background:var(--s-bg);color:var(--s-fg);margin:0 0 8px;position:relative}.saddle-foreign__list>:last-child{margin-bottom:0}.saddle-app--frame{display:flex;flex-direction:column;min-height:calc(100vh - var(--wp-admin--admin-bar--height, 32px))}.saddle-header{background:var(--s-bg);border-block-end:1px solid var(--s-border);padding:16px 24px}.saddle-header--tabs{padding-block-end:0}.saddle-header__row{align-items:center;display:flex;gap:16px;justify-content:space-between;min-height:32px}.saddle-app .saddle-header__title{align-items:center;color:var(--s-fg-muted);display:flex;flex-wrap:wrap;font-size:var(--s-text-title);font-weight:400;gap:10px;line-height:1.4;margin:0;min-width:0;padding:0}.saddle-app .saddle-header__title [aria-current]{color:var(--s-fg);font-weight:600}.saddle-header__crumb{align-items:center;display:inline-flex;gap:10px;min-width:0}.saddle-header__crumb>[aria-current]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-header__link{--pp-accent-text:var( --s-fg-muted );--pp-accent-hover:var( --s-fg );text-decoration:none}.saddle-header__link:focus,.saddle-header__link:hover{text-decoration:underline}.saddle-header__home{--pp-accent-text:var( --s-fg-muted );--pp-accent-hover:var( --s-fg );align-items:center;display:inline-flex;gap:8px;text-decoration:none}.saddle-header__home:is(a):focus span,.saddle-header__home:is(a):hover span{text-decoration:underline}.saddle-header__home svg{color:var(--s-brand);height:22px;width:22px}.saddle-header__sep{color:var(--s-fg-subtle)}.saddle-header__actions{align-items:center;display:flex;flex:0 0 auto;gap:10px}.saddle-header__tabs{--pp-tabs-gap:0}.saddle-header__tabs .pp-tabs{border-block-end:0;gap:16px;margin:0}.saddle-header__tabs .pp-tabs__tab{color:var(--s-fg);font-size:var(--s-text);font-weight:400;gap:6px;letter-spacing:normal;min-block-size:48px;padding-block:0;white-space:nowrap}.saddle-header__tabs .pp-tabs__tab .saddle-nav-icon{flex:none}.saddle-header__tabs .pp-tabs__indicator{background:var(--s-tab-indicator);border-radius:0;bottom:0;height:1.5px}.saddle-header__tabs{margin-block-start:8px;margin-inline:-4px;overflow-x:auto;overflow-y:visible;padding-inline:4px}.saddle-module{align-items:stretch;display:flex;flex:1 0 auto;min-width:0}.saddle-sections{background:var(--s-bg);border-inline-end:1px solid var(--s-border);flex:0 0 220px;width:220px}.saddle-sections__list{display:flex;flex-direction:column;gap:2px;inset-block-start:32px;list-style:none;margin:0;padding:16px 12px;position:sticky}.saddle-sections__list>li{margin:0}.saddle-sections__list>.saddle-sections__rule{margin:8px 0}.saddle-sections__item{--pp-accent-text:var( --s-fg );--pp-accent-hover:var( --s-fg );align-items:center;border-radius:6px;color:var(--s-fg);display:flex;font-size:var(--s-text);gap:8px;line-height:1.4;min-block-size:32px;padding-inline:10px;text-decoration:none;transition:background var(--s-dur) var(--s-ease)}.saddle-sections__item .saddle-nav-icon{flex:none;height:16px;width:16px}.saddle-sections__item:hover{background:var(--s-nav-hover)}.saddle-sections__item.is-active{background:var(--s-canvas);font-weight:600}.saddle-sections__rule{background:var(--s-border);height:1px;margin:8px 0}.saddle-module__body{display:flex;flex:1 1 auto;flex-direction:column;min-width:0}.saddle-subtabs{--pp-tabs-gap:0}.saddle-subtabs .pp-tabs{border-block-end:0;gap:16px;margin:0}.saddle-subtabs .pp-tabs__tab{color:var(--s-fg);font-size:var(--s-text);font-weight:400;gap:6px;letter-spacing:normal;min-block-size:48px;padding-block:0;white-space:nowrap}.saddle-subtabs .pp-tabs__tab .saddle-nav-icon{flex:none}.saddle-subtabs .pp-tabs__indicator{background:var(--s-tab-indicator);border-radius:0;bottom:0;height:1.5px}.saddle-subtabs{background:var(--s-bg);border-block-end:1px solid var(--s-border);overflow-x:auto}.saddle-module .saddle-frame,.saddle-subtabs{padding-inline:40px}.saddle-module .saddle-frame .pp-content{margin-inline:0}.saddle-frame{flex:1 0 auto;padding:var(--pp-space-5) var(--pp-space-5) var(--pp-space-8)}.saddle-frame__notice{margin-block-end:var(--pp-space-4)}.saddle-footer{align-items:center;border-block-start:1px solid var(--s-border);color:var(--s-fg-subtle);display:flex;flex-wrap:wrap;font-size:var(--s-text);gap:8px 20px;padding:16px 24px}.saddle-footer__brand{align-items:center;display:inline-flex;gap:6px;margin-inline-end:auto}.saddle-footer__brand svg{color:var(--s-fg-subtle);height:14px;width:14px}.saddle-footer a{--pp-accent-text:var( --s-fg-muted );--pp-accent-hover:var( --s-fg );text-decoration:none}.saddle-footer a:focus,.saddle-footer a:hover{text-decoration:underline}.saddle-ai{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-pill);color:var(--s-fg);cursor:pointer;display:inline-flex;flex:0 0 auto;font:inherit;font-size:var(--s-text);font-weight:500;gap:8px;height:32px;padding:0 12px 0 10px;transition:border-color var(--s-dur) var(--s-ease)}.saddle-ai:hover{border-color:var(--s-field-edge)}.saddle-ai svg{color:var(--s-fg-muted)}.saddle-ai__dot{background:var(--s-safe);border-radius:50%;box-shadow:0 0 0 3px var(--s-safe-bg);height:8px;width:8px}.saddle-ai.is-paused .saddle-ai__dot{background:var(--s-paused-mark);box-shadow:0 0 0 3px var(--s-active-bg)}.saddle-ai__panel{display:grid;gap:6px;padding:14px 16px 16px;width:280px}.saddle-ai__panel p{font-size:var(--s-text);margin:0}.saddle-ai__panel .pp-btn{justify-self:start;margin-top:6px}.saddle-ai__head{font-weight:600}.saddle-ai__text{color:var(--s-fg-muted)}.saddle-paused{align-items:center;background:var(--s-active-bg);border-block-end:1px solid var(--s-paused-edge);color:var(--s-active-text);display:flex;flex-wrap:wrap;font-size:var(--s-text);gap:12px;justify-content:space-between;padding:10px 24px}.saddle-paused strong{font-weight:600}@media(max-width:782px){.saddle-header{padding-inline:16px}.saddle-module{flex-direction:column}.saddle-sections{border-block-end:1px solid var(--s-border);border-inline-end:0;flex:none;inset-block-start:46px;position:sticky;width:auto;z-index:2}.saddle-sections__list{align-items:center;flex-direction:row;gap:4px;overflow-x:auto;padding:8px 16px;position:static}.saddle-sections__item{white-space:nowrap}.saddle-sections__list>.saddle-sections__rule,.saddle-sections__rule{flex:none;height:20px;margin:0 6px;width:1px}.saddle-subtabs{padding-inline:16px}.saddle-module .saddle-frame{padding-inline:var(--pp-space-4)}.saddle-header__row{flex-wrap:wrap}.saddle-frame{padding:var(--pp-space-4) var(--pp-space-4) var(--pp-space-6)}.saddle-footer{padding-inline:16px}}@media(max-width:600px){.saddle-sections{inset-block-start:0}}.saddle-section-head{align-items:center;display:flex;gap:var(--pp-space-4);justify-content:space-between}.saddle-section-head__title{font-size:var(--s-text-title);font-weight:600;line-height:1.4;margin:0}.saddle-section-head__name{align-items:center;display:flex;flex-wrap:wrap;gap:6px}.saddle-section-head__desc{color:var(--s-fg-subtle);font-size:var(--s-text);margin:2px 0 0}.saddle-section-head__actions{display:flex;flex:0 0 auto;gap:8px}.saddle-section{align-content:start;display:grid;gap:var(--pp-space-3);grid-template-columns:minmax(0,1fr);min-width:0}.saddle-settings__label,.saddle-signin__label{align-items:center;display:inline-flex;flex-wrap:wrap;gap:6px}.saddle-connect{display:grid;gap:var(--pp-space-3);min-width:0}.saddle-connect__groups{display:grid;gap:var(--pp-space-4);min-width:0}.saddle-connect__group{display:grid;gap:8px}.saddle-connect__group-title{color:var(--s-fg-muted);font-size:var(--s-text);font-weight:600;margin:0}.saddle-connect__other{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-connect__apps{display:grid;gap:8px;grid-template-columns:repeat(auto-fill,minmax(132px,1fr))}.saddle-connect__app{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius);color:var(--s-fg);cursor:pointer;display:flex;font:inherit;font-size:var(--s-text);gap:8px;min-width:0;padding:8px 10px;text-align:start}.saddle-connect__app img{flex:0 0 auto;height:18px;width:18px}.saddle-connect__app:hover{background:var(--s-surface-2)}.saddle-connect__app[aria-pressed=true]{background:var(--s-surface-3);border-color:var(--s-border-strong);font-weight:600}.saddle-connect__app-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-connect__app-skeleton{border-radius:var(--s-radius);display:block;width:100%}.saddle-connect__steps{display:grid;gap:var(--pp-space-4);min-width:0}.saddle-connect__hint{color:var(--s-fg-muted);font-size:var(--s-text);margin:0}.saddle-connect__steps-head{align-items:center;display:flex;gap:12px;justify-content:space-between}.saddle-connect__steps-title{align-items:center;display:flex;font-size:var(--s-text);font-weight:600;gap:10px;margin:0}.saddle-connect__steps-title img{height:20px;width:20px}.saddle-connect__list{counter-reset:saddle-step;display:grid;gap:var(--pp-space-4);list-style:none;margin:0;padding:0}.saddle-connect__list>li{counter-increment:saddle-step;display:grid;gap:10px;margin:0;padding-inline-start:34px;position:relative}.saddle-connect__list>li:before{align-items:center;border:1px solid var(--s-border);border-radius:50%;color:var(--s-fg-muted);content:counter(saddle-step);display:inline-flex;font-size:var(--s-text-sm);height:22px;inset-inline-start:0;justify-content:center;position:absolute;top:0;width:22px}.saddle-connect__list p{margin:0}.saddle-connect__links{display:flex;flex-wrap:wrap;gap:8px}.saddle-connect__config{min-width:0}.saddle-connect__config code,.saddle-connect__config pre{overflow-wrap:break-word;white-space:pre-wrap}.saddle-connect__config code{background:none;margin:0;padding:0}.saddle-connect__values code{background:var(--s-surface-2);border-radius:var(--s-radius);font-size:var(--s-text-sm);overflow-wrap:anywhere;padding:1px 6px}.saddle-connect__values .pp-btn{margin-inline-start:8px}.saddle-connect__key{display:grid;justify-items:start}.saddle-connect__steps>.pp-btn--link{justify-self:start}.saddle-connect__waiting{align-items:center;color:var(--s-fg-muted);display:flex;font-size:var(--s-text);gap:8px;margin:0}.saddle-connect__spinner{animation:saddle-spin .9s linear infinite;border:2px solid var(--s-border-strong);border-radius:50%;border-top-color:var(--s-accent);height:10px;width:10px}@keyframes saddle-spin{to{transform:rotate(-1turn)}}.saddle-connect-page{max-width:640px}.saddle-context{align-content:start;display:grid;gap:var(--pp-space-5)}.saddle-context-field .pp-rowlist__body{min-width:0}.saddle-context-field__label{align-items:center;display:inline-flex;gap:6px}.saddle-context-field__text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-context-field__empty{color:var(--s-fg-subtle)}.saddle-context-field__edit{background:var(--s-surface-2);border-block-start:1px solid var(--s-border);display:grid;gap:var(--pp-space-3);padding:var(--pp-space-3) var(--pp-space-4)}.saddle-context-field__actions{align-items:center;display:flex;gap:8px}.saddle-stack{align-content:start;display:grid;gap:var(--pp-space-4)}.saddle-app .pp-content{align-content:start;display:grid;gap:var(--pp-space-5)}.saddle-tabpane{align-content:start;animation:saddle-step-in .24s var(--s-ease) both;display:grid;gap:var(--pp-space-6)}.saddle-chat{margin:0 auto;max-width:680px;padding-block-end:var(--pp-space-8)}.saddle-chat__bar{display:flex;justify-content:flex-end;margin-block-end:8px}.saddle-chat__bar .pp-btn{color:var(--s-fg-muted)}.saddle-chat__thread{display:grid;gap:4px}.saddle-chat__say{align-items:start;animation:saddle-step-in .28s var(--s-ease) both;display:grid;gap:12px;grid-template-columns:28px minmax(0,1fr);margin-block-start:14px}.saddle-chat__say.is-cont{margin-block-start:6px}.saddle-chat__say.is-cont .saddle-chat__avatar{visibility:hidden}.saddle-chat__avatar svg{color:var(--s-brand);display:block;height:28px;width:28px}.saddle-chat__body{display:grid;font-size:var(--s-text-title);gap:12px;grid-template-columns:minmax(0,1fr);line-height:1.5;min-width:0;padding-block-start:3px}.saddle-chat__body>p{margin:0;text-wrap:pretty}.saddle-chat__muted{color:var(--s-fg-muted)}.saddle-chat__typing{display:inline-flex;gap:4px;padding-block-start:10px}.saddle-chat__typing i{animation:saddle-typing 1s ease-in-out infinite;background:var(--s-typing);border-radius:50%;height:6px;width:6px}.saddle-chat__typing i:nth-child(2){animation-delay:.15s}.saddle-chat__typing i:nth-child(3){animation-delay:.3s}.saddle-chat__you{animation:saddle-step-in .22s var(--s-ease) both;display:flex;justify-content:flex-end;margin-block-start:14px}.saddle-chat__bubble{align-items:center;background:var(--s-bg);border-radius:18px 18px 18px 4px;box-shadow:inset 0 0 0 1px var(--s-border);display:inline-flex;font-size:var(--s-text-title);gap:8px;max-width:80%;padding:8px 14px}.saddle-chat__done{align-items:center;color:var(--s-safe-text);display:flex;font-size:var(--s-text);gap:8px;margin:0}.saddle-chat__done:before{background:var(--s-safe) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 16 16%27%3E%3Cpath fill=%27none%27 stroke=%27%23fff%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27 stroke-width=%271.8%27 d=%27m4.2 8.2 2.5 2.5 5.1-5.3%27/%3E%3C/svg%3E") center/16px no-repeat;border-radius:50%;content:"";flex:0 0 auto;height:16px;width:16px}.saddle-chat__calls{color:var(--s-fg-muted);font-size:var(--s-text);margin:0;padding-inline-start:24px}.saddle-chat__replies{animation:saddle-step-in .28s var(--s-ease) both;display:flex;flex-wrap:wrap;gap:8px;margin-block-start:12px;padding-inline-start:40px}.saddle-chat__chip{align-items:center;background:var(--s-bg);border:1px solid var(--s-border-strong);border-radius:var(--s-radius-pill);color:var(--s-fg);cursor:pointer;display:inline-flex;font:inherit;font-size:var(--s-text);font-weight:500;gap:8px;padding:7px 10px 7px 14px;transition:border-color var(--s-dur) var(--s-ease)}.saddle-chat__chip:hover{border-color:var(--s-field-edge)}.saddle-chat__chip--more{color:var(--s-fg-muted);padding-inline-start:14px}.saddle-chat__options{animation:saddle-step-in .28s var(--s-ease) both;display:grid;gap:8px;margin-block-start:12px;max-width:460px;padding-inline-start:40px}.saddle-chat__option{background:var(--s-bg);border:1px solid var(--s-border-strong);border-radius:10px;color:var(--s-fg);cursor:pointer;display:grid;font:inherit;font-size:var(--s-text);gap:1px;justify-items:start;padding:10px 14px;text-align:start;transition:border-color var(--s-dur) var(--s-ease)}.saddle-chat__option:hover:not(:disabled){border-color:var(--s-field-edge)}.saddle-chat__option:disabled{cursor:default;opacity:.6}.saddle-chat__option[aria-busy=true]{border-color:var(--s-brand);opacity:1}.saddle-chat__option strong{font-weight:600}.saddle-chat__option span{color:var(--s-fg-muted)}.saddle-chat__option small{background:var(--saddle-brand-tint);border-radius:var(--s-radius-pill);color:var(--saddle-brand-text);font-size:var(--s-text-sm);margin-block-start:4px;padding:1px 8px}.saddle-chat .pp-snippet__code{font-family:inherit;font-size:var(--s-text);line-height:1.5;overflow-wrap:anywhere;white-space:normal}.saddle-chat .saddle-wizard--embedded{font-size:var(--s-text)}@media(max-width:782px){.saddle-chat__options,.saddle-chat__replies{padding-inline-start:0}}@keyframes saddle-typing{0%,80%,to{opacity:.3}40%{opacity:1}}.saddle-waiting{display:flex;flex-direction:column;gap:10px;margin:4px 0 8px}.saddle-waiting__line{animation:saddle-step-in .28s var(--s-ease) both}.saddle-selfcheck__rows{display:flex;flex-direction:column;gap:12px;margin:4px 0 12px}.saddle-tour__anchor{pointer-events:none;position:fixed;z-index:0}.saddle-home{align-items:start;display:grid;gap:var(--pp-space-6);grid-template-columns:minmax(0,1fr) 280px}.saddle-home__main,.saddle-home__side{display:grid;gap:var(--pp-space-6);grid-template-columns:minmax(0,1fr);min-width:0}.saddle-home--single{grid-template-columns:minmax(0,1fr)}@media(max-width:1100px){.saddle-home{grid-template-columns:minmax(0,1fr)}}.saddle-skeleton{display:grid;gap:var(--pp-space-3)}.saddle-home__connect{align-items:center;display:flex;flex-direction:column;gap:var(--pp-space-4);padding-block:var(--pp-space-8);text-align:center}.saddle-home__connect-text{font-size:var(--s-text);margin:0}.saddle-home__week{padding:var(--pp-space-4) var(--pp-space-5)}.saddle-home__week .pp-card__header{margin-block-end:var(--pp-space-3)}.saddle-home__week .pp-stat{padding-block:0}@media(min-width:561px)and (max-width:900px){.saddle-home__week .pp-stat-grid--fixed{grid-template-columns:repeat(var(--pp-stat-grid-cols,3),minmax(0,1fr))}}.saddle-nudge{align-items:center;display:flex;gap:12px}.saddle-nudge__mark svg{color:var(--s-brand);display:block;height:22px;width:22px}.saddle-nudge__text{flex:1 1 auto;font-size:var(--s-text);margin:0;min-width:0}.saddle-card__empty{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.55;margin:0 0 12px}.saddle-setup-guide{display:grid;gap:20px}.saddle-setup-guide__app{align-items:center;display:flex;font-weight:600;gap:10px}.saddle-setup-guide__app img{height:24px;width:24px}.saddle-setup-guide__step{display:grid;gap:10px;justify-items:start}.saddle-setup-guide__step h3{color:var(--s-fg);font-size:var(--s-text);font-weight:600;margin:0}.saddle-setup-guide__step p{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.55;margin:0}.saddle-setup-guide__step .pp-codeblock,.saddle-setup-guide__step .pp-snippet{width:100%}.saddle-link-danger,.saddle-link-danger:hover:not(:disabled){color:var(--s-danger-text)}.saddle-doc{background:var(--s-surface-2);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);max-height:340px;overflow:auto;padding:16px 18px}.saddle-doc__h{color:var(--s-fg);font-size:var(--s-text);font-weight:600;letter-spacing:.01em;margin:18px 0 8px}.saddle-doc__h:first-child{margin-top:0}.saddle-doc__h--sub,.saddle-doc__p{color:var(--s-fg-muted);font-size:var(--s-text)}.saddle-doc__p{line-height:1.6;margin:0 0 8px}.saddle-doc__list{display:flex;flex-direction:column;gap:5px;margin:0 0 10px;padding-right:18px}.saddle-doc__list li{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.55}.saddle-doc--bare{background:none;border:none;max-height:none;overflow:visible;padding:0}.saddle-memory__compose{align-items:flex-start;display:flex;flex-direction:column;gap:12px;max-width:560px;width:100%}.saddle-memory__compose .pp-field{width:100%}.saddle-memory__compose-actions{align-items:center;display:flex;gap:8px}.saddle-memory .pp-rowlist__body{min-width:0}.saddle-memory__text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-memory__open{border-block-start:1px solid var(--s-border);display:grid;gap:var(--pp-space-2);padding:var(--pp-space-2) var(--pp-space-4) var(--pp-space-3)}.saddle-memory__open-actions{align-items:center;display:flex;gap:12px;justify-content:space-between}.saddle-memory__pin{align-items:center;color:var(--s-fg-muted);display:inline-flex;font-size:var(--s-text);gap:8px}.saddle-memory__setting-label{align-items:center;display:inline-flex;font-weight:600;gap:6px}.saddle-skill-row .pp-rowlist__body{min-width:0}.saddle-skill-row__desc{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-guide__rawtoggle{margin-top:10px}.saddle-guide__skill-actions{align-items:center;display:flex;gap:var(--pp-space-3);justify-content:space-between;margin-top:var(--pp-space-3)}.saddle-guide__skill-actions .saddle-guide__delete{margin-top:0}.saddle-guide__delete{margin-top:var(--pp-space-3)}.saddle-guide__edit{display:grid;gap:var(--pp-space-3)}.saddle-guide__edit textarea{font-family:var(--pp-font-mono,monospace);font-size:var(--s-text);line-height:1.55}.saddle-guide__edit-actions{display:flex;gap:8px}.saddle-guide__system{color:var(--s-fg-muted);margin-top:0;max-height:320px;overflow:auto;white-space:pre-wrap}.saddle-apps__access{min-width:148px;width:auto}.saddle-role{display:grid;gap:2px}.saddle-role__name{font-weight:500}.saddle-role__hint{color:var(--s-fg-muted);font-size:var(--s-text)}.pp-select__trigger .saddle-role__hint{display:none}.saddle-role__list{--pp-accent:var(--saddle-brand);--pp-accent-tint:var(--saddle-brand-tint);min-width:264px}.saddle-role__list .pp-select__item{padding-block:8px}.saddle-role__list .pp-select__item:focus-visible{outline:none}.saddle-apps__address{align-items:center;display:flex;flex-wrap:wrap;font-size:var(--s-text);gap:8px;margin:0}.saddle-apps__address-label{color:var(--s-fg-muted)}.saddle-apps__url{background:var(--s-surface-2);border-radius:var(--s-radius);color:var(--s-fg);font-size:var(--s-text-sm);margin:0;min-width:0;overflow-wrap:anywhere;padding:2px 6px}.saddle-quick__group{display:grid;gap:var(--pp-space-2)}.saddle-quick__group-title{color:var(--s-fg-muted);font-size:var(--s-text);font-weight:600;margin:0}.saddle-quick__cards{display:grid;gap:var(--pp-space-2);grid-template-columns:repeat(auto-fill,minmax(160px,1fr))}.saddle-quick__card{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:6px;box-sizing:border-box;color:var(--s-fg);cursor:pointer;display:flex;font:inherit;font-size:var(--s-text);gap:10px;min-height:44px;min-width:0;padding:0 12px;text-align:start;transition:border-color var(--s-dur) var(--s-ease)}.saddle-quick__card:hover{border-color:var(--s-field-edge)}.saddle-quick__card img{flex:0 0 auto}.saddle-quick__name{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-quick__more{display:grid;gap:var(--pp-space-2)}.saddle-quick__other{font-size:var(--s-text);margin:0}.saddle-apps__empty{color:var(--s-fg-subtle);margin:0}.saddle-apps__advanced{background:var(--s-surface-2);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);display:grid;gap:var(--pp-space-4);margin-top:10px;padding:var(--pp-space-5) var(--pp-space-5)}.saddle-apps__field{display:grid;gap:6px}.saddle-apps__fieldlabel{color:var(--s-fg-muted);font-size:var(--s-text);font-weight:600}.saddle-apps__test{align-items:center;display:flex;gap:12px}.saddle-apps__transport{align-items:center;color:var(--s-fg-subtle);display:flex;font-size:var(--s-text);gap:6px;margin:0}.saddle-wizard{min-width:0}.saddle-wizard__step{animation:saddle-step-in .28s var(--s-ease) both;margin:0 auto;max-width:640px}@keyframes saddle-step-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}.saddle-wizard__lead{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.6;margin:0 0 20px;max-width:60ch;text-wrap:pretty}.saddle-wizard__config{margin:0 0 18px}.saddle-wizard__copy{margin-top:10px}.saddle-wizard__hint{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0 0 16px}.saddle-wizard__actions{align-items:center;display:flex;flex-wrap:wrap;gap:10px;justify-content:flex-end;margin-top:26px}.saddle-wizard__actions--start{justify-content:flex-start;margin-top:0}.saddle-wizard__oauth-gate{margin:0 0 16px}.saddle-wizard__install{align-items:center;display:flex;flex-wrap:wrap;gap:8px 12px;margin:0 0 16px}.saddle-wizard__install .saddle-wizard__hint{margin:0}.saddle-health{font-size:var(--s-text);margin-top:14px}.saddle-health--checking{align-items:center;color:var(--s-fg-subtle);display:flex;gap:6px}.saddle-health--ok{color:var(--s-safe-text);font-weight:600}.saddle-health--muted{color:var(--s-fg-subtle)}.saddle-health__body{color:var(--s-fg-muted);line-height:1.55;margin:0 0 12px}.saddle-health__error{color:var(--s-danger-text)}.saddle-health__actions{align-items:center;display:flex;gap:12px;margin-bottom:12px}@media(prefers-reduced-motion:reduce){.saddle-app *,.saddle-app :after,.saddle-app :before{animation-duration:.01ms!important;transition-duration:.01ms!important}}.saddle-activity__filters{align-items:center;display:flex;flex-wrap:wrap;gap:6px;margin-bottom:16px}.saddle-activity__heading{font-size:var(--s-text-title);font-weight:600;margin:0 0 0 auto}.saddle-activity__loading{display:flex;justify-content:center;padding:60px 0}.saddle-activity__day{margin-bottom:22px}.saddle-activity__daylabel{color:var(--s-fg-subtle);font-size:var(--s-text);font-weight:600;margin:0 0 8px}.saddle-activity__list{background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);box-shadow:var(--s-shadow-sm);margin:0;overflow:hidden}.saddle-activity__row{align-items:center;display:flex;gap:12px;margin:0;padding:10px 16px}.saddle-activity__row+.saddle-activity__row{border-top:1px solid var(--s-border)}.saddle-activity__logo{flex:0 0 auto;height:20px;width:20px}.saddle-activity__mark{background:var(--s-safe);border-radius:50%;flex:0 0 auto;height:8px;margin-inline:6px;width:8px}.saddle-activity__row.is-denied .saddle-activity__mark{background:var(--s-danger)}.saddle-activity__summary{flex:1 1 auto;font-size:var(--s-text);line-height:1.5;min-width:0;overflow:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap}.saddle-activity__time{color:var(--s-fg-subtle);flex:0 0 auto;font-size:var(--s-text);font-variant-numeric:tabular-nums}.saddle-activity__undo{flex:0 0 auto;opacity:0;transition:opacity var(--s-dur) var(--s-ease)}.saddle-activity__row:focus-within .saddle-activity__undo,.saddle-activity__row:hover .saddle-activity__undo,.saddle-activity__undo[aria-busy=true]{opacity:1}@media(hover:none){.saddle-activity__undo{opacity:1}}@media(prefers-reduced-motion:reduce){.saddle-activity__undo{transition:none}}.saddle-activity__undone{color:var(--s-fg-subtle);flex:0 0 auto;font-size:var(--s-text-sm)}.saddle-activity__more{display:flex;justify-content:center;margin-top:4px}.saddle-rows__body{margin:8px 12px 12px;max-height:320px;overflow:auto}.saddle-settings-form{align-content:start;display:grid;gap:var(--pp-space-5)}.saddle-settings-form__agent{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-fields__more,.saddle-fields__more>div{margin-top:var(--pp-space-3)}.saddle-field__control{display:grid;gap:var(--pp-space-1);justify-items:end}.saddle-field__control .pp-control:not(.pp-switch){inline-size:240px;max-inline-size:100%}.saddle-field__status{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-notice__actions{display:flex;flex-wrap:wrap;gap:12px;margin-top:8px}.saddle-svc__row.pp-rowlist__row--clickable:focus-visible{box-shadow:none;outline:2px solid var(--s-focus);outline-offset:-2px}.saddle-svc__line{align-items:center;display:flex;gap:var(--pp-space-3);min-width:0}.saddle-svc__name{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--pp-space-05);min-width:0}.saddle-svc__go{color:var(--s-fg-subtle);flex:0 0 auto}.saddle-svc__empty{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-svc__sends{color:var(--s-fg-muted);font-size:var(--s-text);margin:0}.saddle-svc__detail{display:grid;gap:var(--pp-space-5);min-width:0}.saddle-svc__key{display:grid;gap:var(--pp-space-2);min-width:0}.saddle-svc__key-row{align-items:center;display:flex;flex-wrap:wrap;gap:var(--pp-space-3)}.saddle-svc__key-row code{background:var(--s-surface-2);border-radius:var(--s-radius);font-size:var(--s-text-sm);margin:0;padding:2px 6px}.saddle-svc__key-row .saddle-svc__actions{margin-inline-start:auto}.saddle-svc__key-label{font-weight:600}.saddle-svc__get{font-size:var(--s-text);margin:0}.saddle-svc__actions,.saddle-svc__switch{align-items:center;display:flex;gap:var(--pp-space-2)}.saddle-svc__switch{font-size:var(--s-text)}.saddle-svc__tools .pp-collapsible__trigger:focus-visible{border-radius:var(--s-radius);outline:2px solid var(--s-focus);outline-offset:2px}.saddle-svc__tool-list{border:1px solid var(--s-border);border-radius:var(--s-radius-lg);display:grid;font-size:var(--s-text);list-style:none;margin:0;overflow:hidden;padding:0}.saddle-svc__tool-list li{display:grid;gap:var(--pp-space-3);grid-template-columns:minmax(0,1fr) auto;margin:0;padding:var(--pp-space-2) var(--pp-space-3)}.saddle-svc__tool-list li+li{border-block-start:1px solid var(--s-border)}.saddle-svc__role{color:var(--s-fg-muted);white-space:nowrap}.saddle-adv{background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);box-shadow:var(--s-shadow-sm);overflow:hidden}.saddle-adv>.pp-collapsible+.pp-collapsible{border-block-start:1px solid var(--s-border)}.saddle-adv>.pp-collapsible{scroll-margin-block-start:calc(var(--wp-admin--admin-bar--height, 32px) + var(--pp-space-4))}.saddle-adv .pp-collapsible__trigger{flex-direction:row-reverse;font-size:var(--s-text);font-weight:600;inline-size:100%;justify-content:space-between;padding:var(--pp-space-3) var(--pp-space-4);text-align:start}.saddle-adv .pp-collapsible__panel{padding:0 0 var(--pp-space-3)}.saddle-adv__check{padding-inline:var(--pp-space-4)}.saddle-adv .pp-rowlist{background:transparent;border:0;border-radius:0}.saddle-tools{display:grid;gap:var(--pp-space-3)}.saddle-tools__search{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius);color:var(--s-fg);font:inherit;font-size:var(--s-text);margin-inline:var(--pp-space-4);padding:6px 10px}.saddle-tools__search:focus-visible{outline:2px solid var(--s-focus);outline-offset:1px}.saddle-tools__area{font-weight:600;padding:var(--pp-space-1) var(--pp-space-4)}.saddle-tools__area,.saddle-tools__empty{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-tools__empty{padding-inline:var(--pp-space-4)}.saddle-mcp-diag__health{font-size:var(--s-text);margin:0 0 8px}.saddle-mcp-diag__degraded{color:var(--s-danger-text);font-size:var(--s-text);margin:0 0 12px}.saddle-mcp-diag__actions{align-items:center;display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px}.saddle-mcp-diag__empty,.saddle-mcp-diag__hint{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0 0 12px}.saddle-mcp-diag__table{border-collapse:collapse;font-size:var(--s-text);width:100%}.saddle-mcp-diag__table td,.saddle-mcp-diag__table th{border-bottom:1px solid var(--s-border);padding:6px 8px;text-align:right;vertical-align:middle}.saddle-mcp-diag__table th{font-weight:600}.saddle-mcp-diag__table td:first-child,.saddle-mcp-diag__table th{color:var(--s-fg-subtle);white-space:nowrap}.saddle-needs-ok pre{background:var(--s-surface-2);border:1px solid var(--s-border);border-radius:var(--s-radius);margin:0 0 12px;max-height:240px;overflow:auto;padding:10px 12px;white-space:pre-wrap}.saddle-needs-ok__actions{display:flex;gap:8px;margin-top:16px}@media(max-width:600px){.saddle-apps .pp-rowlist__row,.saddle-needs-ok .pp-rowlist__row{flex-wrap:wrap}.saddle-apps .pp-rowlist__actions,.saddle-needs-ok .pp-rowlist__actions{flex-basis:100%;justify-content:flex-start}}.saddle-module-setup{margin-block-end:16px} +.pp-app,.pp-scope,.saddle-app,body.saddle-admin-page{--saddle-brand:#d000c4;--saddle-brand-hover:#b000a6;--saddle-brand-press:#900088;--saddle-brand-text:#a3009a;--saddle-brand-tint:#fde0fb;--saddle-brand-soft:#f9c2f4;--pp-n-50:#f6f7f7;--pp-n-100:#f0f0f1;--pp-n-200:#f6f7f7;--pp-n-300:#dcdcde;--pp-n-400:#c3c4c7;--pp-n-500:#a7aaad;--pp-n-600:#646970;--pp-n-700:#646970;--pp-n-800:#3c434a;--pp-n-900:#2c3338;--pp-n-950:#1d2327;--pp-canvas:#f0f0f1;--pp-surface:#fff;--pp-surface-hover:#f6f7f7;--pp-accent:var( --wp-admin-theme-color,#2271b1 );--pp-accent-hover:var( --wp-admin-theme-color-darker-10,#135e96 );--pp-accent-text:var( --wp-admin-theme-color,#2271b1 );--pp-accent-tint:color-mix(in srgb,var( --pp-accent ) 7%,#fff);--pp-accent-muted:color-mix(in srgb,var( --pp-accent ) 12%,transparent);--pp-on-accent:#fff;--pp-focus:var( --wp-admin-theme-color,#2271b1 );--pp-action:var( --saddle-brand );--pp-action-hover:var( --saddle-brand-hover );--pp-on-action:#fff;--pp-success:#007017;--pp-success-bg:#edfaef;--pp-info:#2271b1;--pp-info-bg:#f0f6fc;--pp-warning:#8a4f00;--pp-warning-bg:#fef8ee;--pp-danger:#b32d2e;--pp-danger-hover:#8a2424;--pp-danger-bg:#fcf0f1;--pp-r-sm:4px;--pp-r:4px;--pp-r-lg:8px;--pp-shadow-xs:0 1px 2px rgba(0,0,0,.04);--pp-shadow-sm:0 1px 2px rgba(0,0,0,.04);--pp-shadow-btn:none;--pp-shadow-btn-hover:none;--pp-text-2xs:12px;--pp-text-xs:13px;--pp-text-sm:13px;--pp-text-md:15px;--pp-text-lg:15px;--pp-text-xl:15px;--pp-text-2xl:15px;--pp-text-3xl:20px;--pp-text-4xl:20px}.saddle-app,body.saddle-admin-page{--s-canvas:var( --pp-canvas );--s-bg:var( --pp-surface );--s-fg:var( --pp-text );--s-fg-muted:var( --pp-text-secondary );--s-fg-subtle:var( --pp-text-muted );--s-border:var( --pp-border );--s-border-strong:var( --pp-border-strong );--s-surface-2:var( --pp-surface-alt );--s-surface-3:var( --pp-surface-hover );--s-action:var( --pp-action );--s-on-action:var( --pp-on-action );--s-accent:var( --pp-accent );--s-accent-text:var( --pp-accent-text );--s-brand:var( --saddle-brand );--s-safe:var( --pp-success );--s-safe-bg:var( --pp-success-bg );--s-safe-text:var( --pp-success );--s-active:var( --pp-warning );--s-active-bg:var( --pp-warning-bg );--s-active-text:var( --pp-warning );--s-danger:var( --pp-danger );--s-danger-text:var( --pp-danger );--s-rail-safe:var( --s-safe-text );--s-rail-change:var( --pp-accent );--s-rail-remove:var( --s-active-text );--s-focus:var( --pp-focus );--s-field-edge:#8c8f94;--s-tab-indicator:#50575e;--s-nav-hover:var( --pp-surface-hover );--s-overlay:#1d2327;--s-overlay-hover:#101517;--s-paused-mark:#b26200;--s-paused-edge:#f5e1c0;--s-typing:var( --pp-n-500 );--s-code-bg:#1d2327;--s-shadow-sm:var( --pp-shadow-sm );--s-shadow-md:var( --pp-shadow-md );--s-text-sm:var( --pp-text-2xs );--s-text:var( --pp-text-sm );--s-text-title:var( --pp-text-md );--s-radius:var( --pp-r );--s-radius-lg:var( --pp-r-lg );--s-radius-pill:var( --pp-r-pill );--s-ease:cubic-bezier(0.4,0,0.2,1);--s-dur:160ms}.pp-app,.pp-scope,.saddle-app,body.saddle-admin-page{--saddle-chart-ink:var( --saddle-brand );--saddle-chart-ink-soft:color-mix(in srgb,var( --saddle-brand ) 14%,transparent);--saddle-chart-compare:#707070;--saddle-chart-grid:var( --pp-border );--saddle-chart-text:var( --pp-text-secondary )}.pp-tone--success{--pp-tone-text:var( --pp-success )}.pp-tone--info{--pp-tone-text:var( --pp-info )}.pp-tone--warning{--pp-tone-text:var( --pp-warning )}.pp-tone--danger{--pp-tone-text:var( --pp-danger )}.pp-applybar,.pp-bulkbar,.pp-coachmark,.pp-toast,.pp-tooltip{--pp-action:var( --s-overlay );--pp-action-hover:var( --s-overlay-hover )}.pp-btn--danger,.pp-btn--primary{--pp-accent-text:var( --pp-on-action );--pp-accent-hover:var( --pp-on-action )}.pp-btn--secondary{--pp-accent-text:var( --pp-text );--pp-accent-hover:var( --pp-text )}.pp-btn--ghost{--pp-accent-text:var( --pp-text-secondary );--pp-accent-hover:var( --pp-text )}.pp-code--dark{--pp-code-bg:var( --s-code-bg )}.pp-code .pp-code__pre code{background:none;font-size:inherit;margin:0;padding:0}.saddle-app .pp-card__title,body.saddle-admin-page .pp-card__title{font-size:var(--s-text);font-weight:600}.saddle-app .pp-rowlist__title,body.saddle-admin-page .pp-rowlist__title{font-weight:600}.saddle-app .pp-control,body.saddle-admin-page .pp-control{--pp-border:var( --s-field-edge );--pp-border-strong:var( --s-fg-muted )}.saddle-app input.pp-control:not(:focus),body.saddle-admin-page input.pp-control:not(:focus){border-color:var(--pp-border)}.saddle-app input.pp-control:hover:not(:focus):not(:disabled),body.saddle-admin-page input.pp-control:hover:not(:focus):not(:disabled){border-color:var(--pp-border-strong)}.saddle-app .pp-switch:not([aria-checked=true]),body.saddle-admin-page .pp-switch:not([aria-checked=true]){background:var(--s-bg);box-shadow:inset 0 0 0 1px var(--s-field-edge)}.saddle-app .pp-switch:not([aria-checked=true]):after,body.saddle-admin-page .pp-switch:not([aria-checked=true]):after{background:var(--s-field-edge);box-shadow:none}.saddle-app .pp-switch:not([aria-checked=true]):hover:not(:disabled),body.saddle-admin-page .pp-switch:not([aria-checked=true]):hover:not(:disabled){background:var(--s-bg);box-shadow:inset 0 0 0 1px var(--s-fg-muted)}.saddle-app .pp-switch:not([aria-checked=true]):hover:not(:disabled):after,body.saddle-admin-page .pp-switch:not([aria-checked=true]):hover:not(:disabled):after{background:var(--s-fg-muted)}.saddle-app .pp-rowlist__icon,body.saddle-admin-page .pp-rowlist__icon{background:transparent}.saddle-app .pp-rowlist,body.saddle-admin-page .pp-rowlist{box-shadow:var(--pp-shadow-xs)}.saddle-app .pp-card .pp-rowlist,.saddle-app .saddle-adv .pp-card,.saddle-app .saddle-adv .pp-rowlist,body.saddle-admin-page .pp-card .pp-rowlist,body.saddle-admin-page .saddle-adv .pp-card,body.saddle-admin-page .saddle-adv .pp-rowlist{box-shadow:none}body.saddle-admin-page #wpcontent{background:var(--s-canvas);padding-inline-start:0}body.saddle-admin-page #wpbody-content{background:var(--s-canvas);box-sizing:border-box;min-height:calc(100vh - var(--wp-admin--admin-bar--height, 32px));padding-bottom:0}body.saddle-admin-page .wrap{margin:0}body.saddle-admin-page #wpfooter{display:none}.saddle-app{box-sizing:border-box;color:var(--s-fg);-webkit-font-smoothing:antialiased}.saddle-app--loading{display:flex;justify-content:center;padding:120px 0}.saddle-app--setup{padding-block-end:var(--pp-space-8)}.saddle-app [role=radio]:focus-visible,.saddle-app a:focus-visible,.saddle-app button:focus-visible{box-shadow:none;outline:2px solid var(--s-focus);outline-offset:2px}.saddle-app button{touch-action:manipulation}.saddle-app h1,.saddle-app h2,.saddle-app h3,.saddle-app h4{color:inherit}.saddle-app a:not([class]){color:var(--s-accent-text)}.saddle-app a:not([class]):hover{color:var(--pp-accent-hover)}.saddle-foreign{max-height:60vh;max-width:min(520px,100vw - 32px);overflow-y:auto}.saddle-foreign__button{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-pill);color:var(--s-fg-muted);cursor:pointer;display:inline-flex;font-size:var(--s-text);gap:6px;height:28px;line-height:1;padding:0 10px}.saddle-foreign__button svg{height:20px;width:20px}.saddle-foreign__button:hover{border-color:var(--s-border-strong);color:var(--s-fg)}.saddle-foreign__head{color:var(--s-fg-subtle);font-size:var(--s-text);margin-bottom:8px}.saddle-foreign__saddle{display:grid;gap:8px;margin-bottom:12px}.saddle-foreign__list{color-scheme:light}.saddle-foreign__list .notice,.saddle-foreign__list div.error,.saddle-foreign__list div.updated{background:var(--s-bg);color:var(--s-fg);margin:0 0 8px;position:relative}.saddle-foreign__list>:last-child{margin-bottom:0}.saddle-app--frame{display:flex;flex-direction:column;min-height:calc(100vh - var(--wp-admin--admin-bar--height, 32px))}.saddle-header{background:var(--s-bg);border-block-end:1px solid var(--s-border);padding:16px 24px}.saddle-header--tabs{padding-block-end:0}.saddle-header__row{align-items:center;display:flex;gap:16px;justify-content:space-between;min-height:32px}.saddle-app .saddle-header__title{align-items:center;color:var(--s-fg-muted);display:flex;flex-wrap:wrap;font-size:var(--s-text-title);font-weight:400;gap:10px;line-height:1.4;margin:0;min-width:0;padding:0}.saddle-app .saddle-header__title [aria-current]{color:var(--s-fg);font-weight:600}.saddle-header__crumb{align-items:center;display:inline-flex;gap:10px;min-width:0}.saddle-header__crumb>[aria-current]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-header__link{--pp-accent-text:var( --s-fg-muted );--pp-accent-hover:var( --s-fg );text-decoration:none}.saddle-header__link:focus,.saddle-header__link:hover{text-decoration:underline}.saddle-header__home{--pp-accent-text:var( --s-fg-muted );--pp-accent-hover:var( --s-fg );align-items:center;display:inline-flex;gap:8px;text-decoration:none}.saddle-header__home:is(a):focus span,.saddle-header__home:is(a):hover span{text-decoration:underline}.saddle-header__home svg{color:var(--s-brand);height:22px;width:22px}.saddle-header__sep{color:var(--s-fg-subtle)}.saddle-header__actions{align-items:center;display:flex;flex:0 0 auto;gap:10px}.saddle-header__tabs{--pp-tabs-gap:0}.saddle-header__tabs .pp-tabs{border-block-end:0;gap:16px;margin:0}.saddle-header__tabs .pp-tabs__tab{color:var(--s-fg);font-size:var(--s-text);font-weight:400;gap:6px;letter-spacing:normal;min-block-size:48px;padding-block:0;white-space:nowrap}.saddle-header__tabs .pp-tabs__tab .saddle-nav-icon{flex:none}.saddle-header__tabs .pp-tabs__indicator{background:var(--s-tab-indicator);border-radius:0;bottom:0;height:1.5px}.saddle-header__tabs{margin-block-start:8px;margin-inline:-4px;overflow-x:auto;overflow-y:visible;padding-inline:4px}.saddle-module{align-items:stretch;display:flex;flex:1 0 auto;min-width:0}.saddle-sections{background:var(--s-bg);border-inline-end:1px solid var(--s-border);flex:0 0 220px;width:220px}.saddle-sections__list{display:flex;flex-direction:column;gap:2px;inset-block-start:32px;list-style:none;margin:0;padding:16px 12px;position:sticky}.saddle-sections__list>li{margin:0}.saddle-sections__list>.saddle-sections__rule{margin:8px 0}.saddle-sections__item{--pp-accent-text:var( --s-fg );--pp-accent-hover:var( --s-fg );align-items:center;border-radius:6px;color:var(--s-fg);display:flex;font-size:var(--s-text);gap:8px;line-height:1.4;min-block-size:32px;padding-inline:10px;text-decoration:none;transition:background var(--s-dur) var(--s-ease)}.saddle-sections__item .saddle-nav-icon{flex:none;height:16px;width:16px}.saddle-sections__item:hover{background:var(--s-nav-hover)}.saddle-sections__item.is-active{background:var(--s-canvas);font-weight:600}.saddle-sections__rule{background:var(--s-border);height:1px;margin:8px 0}.saddle-module__body{display:flex;flex:1 1 auto;flex-direction:column;min-width:0}.saddle-subtabs{--pp-tabs-gap:0}.saddle-subtabs .pp-tabs{border-block-end:0;gap:16px;margin:0}.saddle-subtabs .pp-tabs__tab{color:var(--s-fg);font-size:var(--s-text);font-weight:400;gap:6px;letter-spacing:normal;min-block-size:48px;padding-block:0;white-space:nowrap}.saddle-subtabs .pp-tabs__tab .saddle-nav-icon{flex:none}.saddle-subtabs .pp-tabs__indicator{background:var(--s-tab-indicator);border-radius:0;bottom:0;height:1.5px}.saddle-subtabs{background:var(--s-bg);border-block-end:1px solid var(--s-border);overflow-x:auto}.saddle-module .saddle-frame,.saddle-subtabs{padding-inline:40px}.saddle-module .saddle-frame .pp-content{margin-inline:0}.saddle-frame{flex:1 0 auto;padding:var(--pp-space-5) var(--pp-space-5) var(--pp-space-8)}.saddle-frame__notice{margin-block-end:var(--pp-space-4)}.saddle-footer{align-items:center;border-block-start:1px solid var(--s-border);color:var(--s-fg-subtle);display:flex;flex-wrap:wrap;font-size:var(--s-text);gap:8px 20px;padding:16px 24px}.saddle-footer__brand{align-items:center;display:inline-flex;gap:6px;margin-inline-end:auto}.saddle-footer__brand svg{color:var(--s-fg-subtle);height:14px;width:14px}.saddle-footer a{--pp-accent-text:var( --s-fg-muted );--pp-accent-hover:var( --s-fg );text-decoration:none}.saddle-footer a:focus,.saddle-footer a:hover{text-decoration:underline}.saddle-ai{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-pill);color:var(--s-fg);cursor:pointer;display:inline-flex;flex:0 0 auto;font:inherit;font-size:var(--s-text);font-weight:500;gap:8px;height:32px;padding:0 12px 0 10px;transition:border-color var(--s-dur) var(--s-ease)}.saddle-ai:hover{border-color:var(--s-field-edge)}.saddle-ai svg{color:var(--s-fg-muted)}.saddle-ai__dot{background:var(--s-safe);border-radius:50%;box-shadow:0 0 0 3px var(--s-safe-bg);height:8px;width:8px}.saddle-ai.is-paused .saddle-ai__dot{background:var(--s-paused-mark);box-shadow:0 0 0 3px var(--s-active-bg)}.saddle-ai__panel{display:grid;gap:6px;padding:14px 16px 16px;width:280px}.saddle-ai__panel p{font-size:var(--s-text);margin:0}.saddle-ai__panel .pp-btn{justify-self:start;margin-top:6px}.saddle-ai__head{font-weight:600}.saddle-ai__text{color:var(--s-fg-muted)}.saddle-paused{align-items:center;background:var(--s-active-bg);border-block-end:1px solid var(--s-paused-edge);color:var(--s-active-text);display:flex;flex-wrap:wrap;font-size:var(--s-text);gap:12px;justify-content:space-between;padding:10px 24px}.saddle-paused strong{font-weight:600}@media(max-width:782px){.saddle-header{padding-inline:16px}.saddle-module{flex-direction:column}.saddle-sections{border-block-end:1px solid var(--s-border);border-inline-end:0;flex:none;inset-block-start:46px;position:sticky;width:auto;z-index:2}.saddle-sections__list{align-items:center;flex-direction:row;gap:4px;overflow-x:auto;padding:8px 16px;position:static}.saddle-sections__item{white-space:nowrap}.saddle-sections__list>.saddle-sections__rule,.saddle-sections__rule{flex:none;height:20px;margin:0 6px;width:1px}.saddle-subtabs{padding-inline:16px}.saddle-module .saddle-frame{padding-inline:var(--pp-space-4)}.saddle-header__row{flex-wrap:wrap}.saddle-frame{padding:var(--pp-space-4) var(--pp-space-4) var(--pp-space-6)}.saddle-footer{padding-inline:16px}}@media(max-width:600px){.saddle-sections{inset-block-start:0}}.saddle-section-head{align-items:center;display:flex;gap:var(--pp-space-4);justify-content:space-between}.saddle-section-head__title{font-size:var(--s-text-title);font-weight:600;line-height:1.4;margin:0}.saddle-section-head__name{align-items:center;display:flex;flex-wrap:wrap;gap:6px}.saddle-section-head__desc{color:var(--s-fg-subtle);font-size:var(--s-text);margin:2px 0 0}.saddle-section-head__actions{display:flex;flex:0 0 auto;gap:8px}.saddle-section{align-content:start;display:grid;gap:var(--pp-space-3);grid-template-columns:minmax(0,1fr);min-width:0}.saddle-settings__label,.saddle-signin__label{align-items:center;display:inline-flex;flex-wrap:wrap;gap:6px}.saddle-connect{display:grid;gap:var(--pp-space-3);min-width:0}.saddle-connect__groups{display:grid;gap:var(--pp-space-4);min-width:0}.saddle-connect__group{display:grid;gap:8px}.saddle-connect__group-title{color:var(--s-fg-muted);font-size:var(--s-text);font-weight:600;margin:0}.saddle-connect__other{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-connect__apps{display:grid;gap:8px;grid-template-columns:repeat(auto-fill,minmax(132px,1fr))}.saddle-connect__app{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius);color:var(--s-fg);cursor:pointer;display:flex;font:inherit;font-size:var(--s-text);gap:8px;min-width:0;padding:8px 10px;text-align:start}.saddle-connect__app img{flex:0 0 auto;height:18px;width:18px}.saddle-connect__app:hover{background:var(--s-surface-2)}.saddle-connect__app[aria-pressed=true]{background:var(--s-surface-3);border-color:var(--s-border-strong);font-weight:600}.saddle-connect__app-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-connect__app-skeleton{border-radius:var(--s-radius);display:block;width:100%}.saddle-connect__steps{display:grid;gap:var(--pp-space-4);min-width:0}.saddle-connect__hint{color:var(--s-fg-muted);font-size:var(--s-text);margin:0}.saddle-connect__steps-head{align-items:center;display:flex;gap:12px;justify-content:space-between}.saddle-connect__steps-title{align-items:center;display:flex;font-size:var(--s-text);font-weight:600;gap:10px;margin:0}.saddle-connect__steps-title img{height:20px;width:20px}.saddle-connect__list{counter-reset:saddle-step;display:grid;gap:var(--pp-space-4);list-style:none;margin:0;padding:0}.saddle-connect__list>li{counter-increment:saddle-step;display:grid;gap:10px;margin:0;padding-inline-start:34px;position:relative}.saddle-connect__list>li:before{align-items:center;border:1px solid var(--s-border);border-radius:50%;color:var(--s-fg-muted);content:counter(saddle-step);display:inline-flex;font-size:var(--s-text-sm);height:22px;inset-inline-start:0;justify-content:center;position:absolute;top:0;width:22px}.saddle-connect__list p{margin:0}.saddle-connect__links{display:flex;flex-wrap:wrap;gap:8px}.saddle-connect__config{min-width:0}.saddle-connect__config code,.saddle-connect__config pre{overflow-wrap:break-word;white-space:pre-wrap}.saddle-connect__config code{background:none;margin:0;padding:0}.saddle-connect__values code{background:var(--s-surface-2);border-radius:var(--s-radius);font-size:var(--s-text-sm);overflow-wrap:anywhere;padding:1px 6px}.saddle-connect__values .pp-btn{margin-inline-start:8px}.saddle-connect__key{display:grid;justify-items:start}.saddle-connect__steps>.pp-btn--link{justify-self:start}.saddle-connect__waiting{align-items:center;color:var(--s-fg-muted);display:flex;font-size:var(--s-text);gap:8px;margin:0}.saddle-connect__spinner{animation:saddle-spin .9s linear infinite;border:2px solid var(--s-border-strong);border-radius:50%;border-top-color:var(--s-accent);height:10px;width:10px}@keyframes saddle-spin{to{transform:rotate(-1turn)}}.saddle-connect-page{max-width:640px}.saddle-context{align-content:start;display:grid;gap:var(--pp-space-5)}.saddle-context-field .pp-rowlist__body{min-width:0}.saddle-context-field__label{align-items:center;display:inline-flex;gap:6px}.saddle-context-field__text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-context-field__empty{color:var(--s-fg-subtle)}.saddle-context-field__edit{background:var(--s-surface-2);border-block-start:1px solid var(--s-border);display:grid;gap:var(--pp-space-3);padding:var(--pp-space-3) var(--pp-space-4)}.saddle-context-field__actions{align-items:center;display:flex;gap:8px}.saddle-stack{align-content:start;display:grid;gap:var(--pp-space-4)}.saddle-app .pp-content{align-content:start;display:grid;gap:var(--pp-space-5)}.saddle-tabpane{align-content:start;animation:saddle-step-in .24s var(--s-ease) both;display:grid;gap:var(--pp-space-6)}.saddle-chat{margin:0 auto;max-width:680px;padding-block-end:var(--pp-space-8)}.saddle-chat__bar{display:flex;justify-content:flex-end;margin-block-end:8px}.saddle-chat__bar .pp-btn{color:var(--s-fg-muted)}.saddle-chat__thread{display:grid;gap:4px}.saddle-chat__say{align-items:start;animation:saddle-step-in .28s var(--s-ease) both;display:grid;gap:12px;grid-template-columns:28px minmax(0,1fr);margin-block-start:14px}.saddle-chat__say.is-cont{margin-block-start:6px}.saddle-chat__say.is-cont .saddle-chat__avatar{visibility:hidden}.saddle-chat__avatar svg{color:var(--s-brand);display:block;height:28px;width:28px}.saddle-chat__body{display:grid;font-size:var(--s-text-title);gap:12px;grid-template-columns:minmax(0,1fr);line-height:1.5;min-width:0;padding-block-start:3px}.saddle-chat__body>p{margin:0;text-wrap:pretty}.saddle-chat__muted{color:var(--s-fg-muted)}.saddle-chat__typing{display:inline-flex;gap:4px;padding-block-start:10px}.saddle-chat__typing i{animation:saddle-typing 1s ease-in-out infinite;background:var(--s-typing);border-radius:50%;height:6px;width:6px}.saddle-chat__typing i:nth-child(2){animation-delay:.15s}.saddle-chat__typing i:nth-child(3){animation-delay:.3s}.saddle-chat__you{animation:saddle-step-in .22s var(--s-ease) both;display:flex;justify-content:flex-end;margin-block-start:14px}.saddle-chat__bubble{align-items:center;background:var(--s-bg);border-radius:18px 18px 18px 4px;box-shadow:inset 0 0 0 1px var(--s-border);display:inline-flex;font-size:var(--s-text-title);gap:8px;max-width:80%;padding:8px 14px}.saddle-chat__done{align-items:center;color:var(--s-safe-text);display:flex;font-size:var(--s-text);gap:8px;margin:0}.saddle-chat__done:before{background:var(--s-safe) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 16 16%27%3E%3Cpath fill=%27none%27 stroke=%27%23fff%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27 stroke-width=%271.8%27 d=%27m4.2 8.2 2.5 2.5 5.1-5.3%27/%3E%3C/svg%3E") center/16px no-repeat;border-radius:50%;content:"";flex:0 0 auto;height:16px;width:16px}.saddle-chat__calls{color:var(--s-fg-muted);font-size:var(--s-text);margin:0;padding-inline-start:24px}.saddle-chat__replies{animation:saddle-step-in .28s var(--s-ease) both;display:flex;flex-wrap:wrap;gap:8px;margin-block-start:12px;padding-inline-start:40px}.saddle-chat__chip{align-items:center;background:var(--s-bg);border:1px solid var(--s-border-strong);border-radius:var(--s-radius-pill);color:var(--s-fg);cursor:pointer;display:inline-flex;font:inherit;font-size:var(--s-text);font-weight:500;gap:8px;padding:7px 10px 7px 14px;transition:border-color var(--s-dur) var(--s-ease)}.saddle-chat__chip:hover{border-color:var(--s-field-edge)}.saddle-chat__chip--more{color:var(--s-fg-muted);padding-inline-start:14px}.saddle-chat__options{animation:saddle-step-in .28s var(--s-ease) both;display:grid;gap:8px;margin-block-start:12px;max-width:460px;padding-inline-start:40px}.saddle-chat__option{background:var(--s-bg);border:1px solid var(--s-border-strong);border-radius:10px;color:var(--s-fg);cursor:pointer;display:grid;font:inherit;font-size:var(--s-text);gap:1px;justify-items:start;padding:10px 14px;text-align:start;transition:border-color var(--s-dur) var(--s-ease)}.saddle-chat__option:hover:not(:disabled){border-color:var(--s-field-edge)}.saddle-chat__option:disabled{cursor:default;opacity:.6}.saddle-chat__option[aria-busy=true]{border-color:var(--s-brand);opacity:1}.saddle-chat__option strong{font-weight:600}.saddle-chat__option span{color:var(--s-fg-muted)}.saddle-chat__option small{background:var(--saddle-brand-tint);border-radius:var(--s-radius-pill);color:var(--saddle-brand-text);font-size:var(--s-text-sm);margin-block-start:4px;padding:1px 8px}.saddle-chat .pp-snippet__code{font-family:inherit;font-size:var(--s-text);line-height:1.5;overflow-wrap:anywhere;white-space:normal}.saddle-chat .saddle-wizard--embedded{font-size:var(--s-text)}@media(max-width:782px){.saddle-chat__options,.saddle-chat__replies{padding-inline-start:0}}@keyframes saddle-typing{0%,80%,to{opacity:.3}40%{opacity:1}}.saddle-waiting{display:flex;flex-direction:column;gap:10px;margin:4px 0 8px}.saddle-waiting__line{animation:saddle-step-in .28s var(--s-ease) both}.saddle-selfcheck__rows{display:flex;flex-direction:column;gap:12px;margin:4px 0 12px}.saddle-tour__anchor{pointer-events:none;position:fixed;z-index:0}.saddle-home{align-items:start;display:grid;gap:var(--pp-space-6);grid-template-columns:minmax(0,1fr) 280px}.saddle-home__main,.saddle-home__side{display:grid;gap:var(--pp-space-6);grid-template-columns:minmax(0,1fr);min-width:0}.saddle-home--single{grid-template-columns:minmax(0,1fr)}@media(max-width:1100px){.saddle-home{grid-template-columns:minmax(0,1fr)}}.saddle-skeleton{display:grid;gap:var(--pp-space-3)}.saddle-home__connect{align-items:center;display:flex;flex-direction:column;gap:var(--pp-space-4);padding-block:var(--pp-space-8);text-align:center}.saddle-home__connect-text{font-size:var(--s-text);margin:0}.saddle-home__week{padding:var(--pp-space-4) var(--pp-space-5)}.saddle-home__week .pp-card__header{margin-block-end:var(--pp-space-3)}.saddle-home__week .pp-stat{padding-block:0}@media(min-width:561px)and (max-width:900px){.saddle-home__week .pp-stat-grid--fixed{grid-template-columns:repeat(var(--pp-stat-grid-cols,3),minmax(0,1fr))}}.saddle-nudge{align-items:center;display:flex;gap:12px}.saddle-nudge__mark svg{color:var(--s-brand);display:block;height:22px;width:22px}.saddle-nudge__text{flex:1 1 auto;font-size:var(--s-text);margin:0;min-width:0}.saddle-card__empty{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.55;margin:0 0 12px}.saddle-setup-guide{display:grid;gap:20px}.saddle-setup-guide__app{align-items:center;display:flex;font-weight:600;gap:10px}.saddle-setup-guide__app img{height:24px;width:24px}.saddle-setup-guide__step{display:grid;gap:10px;justify-items:start}.saddle-setup-guide__step h3{color:var(--s-fg);font-size:var(--s-text);font-weight:600;margin:0}.saddle-setup-guide__step p{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.55;margin:0}.saddle-setup-guide__step .pp-codeblock,.saddle-setup-guide__step .pp-snippet{width:100%}.saddle-link-danger,.saddle-link-danger:hover:not(:disabled){color:var(--s-danger-text)}.saddle-doc{background:var(--s-surface-2);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);max-height:340px;overflow:auto;padding:16px 18px}.saddle-doc__h{color:var(--s-fg);font-size:var(--s-text);font-weight:600;letter-spacing:.01em;margin:18px 0 8px}.saddle-doc__h:first-child{margin-top:0}.saddle-doc__h--sub,.saddle-doc__p{color:var(--s-fg-muted);font-size:var(--s-text)}.saddle-doc__p{line-height:1.6;margin:0 0 8px}.saddle-doc__list{display:flex;flex-direction:column;gap:5px;margin:0 0 10px;padding-right:18px}.saddle-doc__list li{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.55}.saddle-doc--bare{background:none;border:none;max-height:none;overflow:visible;padding:0}.saddle-memory__compose{align-items:flex-start;display:flex;flex-direction:column;gap:12px;max-width:560px;width:100%}.saddle-memory__compose .pp-field{width:100%}.saddle-memory__compose-actions{align-items:center;display:flex;gap:8px}.saddle-memory .pp-rowlist__body{min-width:0}.saddle-memory__text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-memory__open{border-block-start:1px solid var(--s-border);display:grid;gap:var(--pp-space-2);padding:var(--pp-space-2) var(--pp-space-4) var(--pp-space-3)}.saddle-memory__open-actions{align-items:center;display:flex;gap:12px;justify-content:space-between}.saddle-memory__pin{align-items:center;color:var(--s-fg-muted);display:inline-flex;font-size:var(--s-text);gap:8px}.saddle-memory__setting-label{align-items:center;display:inline-flex;font-weight:600;gap:6px}.saddle-skill-row .pp-rowlist__body{min-width:0}.saddle-skill-row__desc{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-guide__rawtoggle{margin-top:10px}.saddle-guide__skill-actions{align-items:center;display:flex;gap:var(--pp-space-3);justify-content:space-between;margin-top:var(--pp-space-3)}.saddle-guide__skill-actions .saddle-guide__delete{margin-top:0}.saddle-guide__delete{margin-top:var(--pp-space-3)}.saddle-guide__edit{display:grid;gap:var(--pp-space-3)}.saddle-guide__edit textarea{font-family:var(--pp-font-mono,monospace);font-size:var(--s-text);line-height:1.55}.saddle-guide__edit-actions{display:flex;gap:8px}.saddle-guide__system{color:var(--s-fg-muted);margin-top:0;max-height:320px;overflow:auto;white-space:pre-wrap}.saddle-apps__access{min-width:148px;width:auto}.saddle-role{display:grid;gap:2px}.saddle-role__name{font-weight:500}.saddle-role__hint{color:var(--s-fg-muted);font-size:var(--s-text)}.pp-select__trigger .saddle-role__hint{display:none}.saddle-role__list{--pp-accent:var(--saddle-brand);--pp-accent-tint:var(--saddle-brand-tint);min-width:264px}.saddle-role__list .pp-select__item{padding-block:8px}.saddle-role__list .pp-select__item:focus-visible{outline:none}.saddle-apps__address{align-items:center;display:flex;flex-wrap:wrap;font-size:var(--s-text);gap:8px;margin:0}.saddle-apps__address-label{color:var(--s-fg-muted)}.saddle-apps__url{background:var(--s-surface-2);border-radius:var(--s-radius);color:var(--s-fg);font-size:var(--s-text-sm);margin:0;min-width:0;overflow-wrap:anywhere;padding:2px 6px}.saddle-quick__group{display:grid;gap:var(--pp-space-2)}.saddle-quick__group-title{color:var(--s-fg-muted);font-size:var(--s-text);font-weight:600;margin:0}.saddle-quick__cards{display:grid;gap:var(--pp-space-2);grid-template-columns:repeat(auto-fill,minmax(160px,1fr))}.saddle-quick__card{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:6px;box-sizing:border-box;color:var(--s-fg);cursor:pointer;display:flex;font:inherit;font-size:var(--s-text);gap:10px;min-height:44px;min-width:0;padding:0 12px;text-align:start;transition:border-color var(--s-dur) var(--s-ease)}.saddle-quick__card:hover{border-color:var(--s-field-edge)}.saddle-quick__card img{flex:0 0 auto}.saddle-quick__name{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-quick__more{display:grid;gap:var(--pp-space-2)}.saddle-quick__other{font-size:var(--s-text);margin:0}.saddle-apps__empty{color:var(--s-fg-subtle);margin:0}.saddle-apps__advanced{background:var(--s-surface-2);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);display:grid;gap:var(--pp-space-4);margin-top:10px;padding:var(--pp-space-5) var(--pp-space-5)}.saddle-apps__field{display:grid;gap:6px}.saddle-apps__fieldlabel{color:var(--s-fg-muted);font-size:var(--s-text);font-weight:600}.saddle-apps__test{align-items:center;display:flex;gap:12px}.saddle-apps__transport{align-items:center;color:var(--s-fg-subtle);display:flex;font-size:var(--s-text);gap:6px;margin:0}.saddle-wizard{min-width:0}.saddle-wizard__step{animation:saddle-step-in .28s var(--s-ease) both;margin:0 auto;max-width:640px}@keyframes saddle-step-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}.saddle-wizard__lead{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.6;margin:0 0 20px;max-width:60ch;text-wrap:pretty}.saddle-wizard__config{margin:0 0 18px}.saddle-wizard__copy{margin-top:10px}.saddle-wizard__hint{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0 0 16px}.saddle-wizard__actions{align-items:center;display:flex;flex-wrap:wrap;gap:10px;justify-content:flex-end;margin-top:26px}.saddle-wizard__actions--start{justify-content:flex-start;margin-top:0}.saddle-wizard__oauth-gate{margin:0 0 16px}.saddle-wizard__install{align-items:center;display:flex;flex-wrap:wrap;gap:8px 12px;margin:0 0 16px}.saddle-wizard__install .saddle-wizard__hint{margin:0}.saddle-health{font-size:var(--s-text);margin-top:14px}.saddle-health--checking{align-items:center;color:var(--s-fg-subtle);display:flex;gap:6px}.saddle-health--ok{color:var(--s-safe-text);font-weight:600}.saddle-health--muted{color:var(--s-fg-subtle)}.saddle-health__body{color:var(--s-fg-muted);line-height:1.55;margin:0 0 12px}.saddle-health__error{color:var(--s-danger-text)}.saddle-health__actions{align-items:center;display:flex;gap:12px;margin-bottom:12px}@media(prefers-reduced-motion:reduce){.saddle-app *,.saddle-app :after,.saddle-app :before{animation-duration:.01ms!important;transition-duration:.01ms!important}}.saddle-activity__filters{align-items:center;display:flex;flex-wrap:wrap;gap:6px;margin-bottom:16px}.saddle-activity__heading{font-size:var(--s-text-title);font-weight:600;margin:0 0 0 auto}.saddle-activity__loading{display:flex;justify-content:center;padding:60px 0}.saddle-activity__day{margin-bottom:22px}.saddle-activity__daylabel{color:var(--s-fg-subtle);font-size:var(--s-text);font-weight:600;margin:0 0 8px}.saddle-activity__list{background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);box-shadow:var(--s-shadow-sm);margin:0;overflow:hidden}.saddle-activity__row{align-items:center;display:flex;gap:12px;margin:0;padding:10px 16px}.saddle-activity__row+.saddle-activity__row{border-top:1px solid var(--s-border)}.saddle-activity__logo{flex:0 0 auto;height:20px;width:20px}.saddle-activity__mark{background:var(--s-safe);border-radius:50%;flex:0 0 auto;height:8px;margin-inline:6px;width:8px}.saddle-activity__row.is-denied .saddle-activity__mark{background:var(--s-danger)}.saddle-activity__summary{flex:1 1 auto;font-size:var(--s-text);line-height:1.5;min-width:0;overflow:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap}.saddle-activity__time{color:var(--s-fg-subtle);flex:0 0 auto;font-size:var(--s-text);font-variant-numeric:tabular-nums}.saddle-activity__undo{flex:0 0 auto;opacity:0;transition:opacity var(--s-dur) var(--s-ease)}.saddle-activity__row:focus-within .saddle-activity__undo,.saddle-activity__row:hover .saddle-activity__undo,.saddle-activity__undo[aria-busy=true]{opacity:1}@media(hover:none){.saddle-activity__undo{opacity:1}}@media(prefers-reduced-motion:reduce){.saddle-activity__undo{transition:none}}.saddle-activity__undone{color:var(--s-fg-subtle);flex:0 0 auto;font-size:var(--s-text-sm)}.saddle-activity__more{display:flex;justify-content:center;margin-top:4px}.saddle-rows__body{margin:8px 12px 12px;max-height:320px;overflow:auto}.saddle-settings-form{align-content:start;display:grid;gap:var(--pp-space-5)}.saddle-settings-form__agent{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-fields__more,.saddle-fields__more>div{margin-top:var(--pp-space-3)}.saddle-field__control{display:grid;gap:var(--pp-space-1);justify-items:end}.saddle-field__control .pp-control:not(.pp-switch){inline-size:240px;max-inline-size:100%}.saddle-field__status{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-notice__actions{display:flex;flex-wrap:wrap;gap:12px;margin-top:8px}.saddle-svc__row.pp-rowlist__row--clickable:focus-visible{box-shadow:none;outline:2px solid var(--s-focus);outline-offset:-2px}.saddle-svc__line{align-items:center;display:flex;gap:var(--pp-space-3);min-width:0}.saddle-svc__name{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--pp-space-05);min-width:0}.saddle-svc__go{color:var(--s-fg-subtle);flex:0 0 auto}.saddle-svc__empty{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-svc__sends{color:var(--s-fg-muted);font-size:var(--s-text);margin:0}.saddle-svc__detail{display:grid;gap:var(--pp-space-5);min-width:0}.saddle-svc__key{display:grid;gap:var(--pp-space-2);min-width:0}.saddle-svc__key-row{align-items:center;display:flex;flex-wrap:wrap;gap:var(--pp-space-3)}.saddle-svc__key-row code{background:var(--s-surface-2);border-radius:var(--s-radius);font-size:var(--s-text-sm);margin:0;padding:2px 6px}.saddle-svc__key-row .saddle-svc__actions{margin-inline-start:auto}.saddle-svc__key-label{font-weight:600}.saddle-svc__get{font-size:var(--s-text);margin:0}.saddle-svc__actions,.saddle-svc__switch{align-items:center;display:flex;gap:var(--pp-space-2)}.saddle-svc__switch{font-size:var(--s-text)}.saddle-svc__tools .pp-collapsible__trigger:focus-visible{border-radius:var(--s-radius);outline:2px solid var(--s-focus);outline-offset:2px}.saddle-svc__tool-list{border:1px solid var(--s-border);border-radius:var(--s-radius-lg);display:grid;font-size:var(--s-text);list-style:none;margin:0;overflow:hidden;padding:0}.saddle-svc__tool-list li{display:grid;gap:var(--pp-space-3);grid-template-columns:minmax(0,1fr) auto;margin:0;padding:var(--pp-space-2) var(--pp-space-3)}.saddle-svc__tool-list li+li{border-block-start:1px solid var(--s-border)}.saddle-svc__role{color:var(--s-fg-muted);white-space:nowrap}.saddle-adv{background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);box-shadow:var(--s-shadow-sm);overflow:hidden}.saddle-adv>.pp-collapsible+.pp-collapsible{border-block-start:1px solid var(--s-border)}.saddle-adv>.pp-collapsible{scroll-margin-block-start:calc(var(--wp-admin--admin-bar--height, 32px) + var(--pp-space-4))}.saddle-adv .pp-collapsible__trigger{flex-direction:row-reverse;font-size:var(--s-text);font-weight:600;inline-size:100%;justify-content:space-between;padding:var(--pp-space-3) var(--pp-space-4);text-align:start}.saddle-adv .pp-collapsible__panel{padding:0 0 var(--pp-space-3)}.saddle-adv__check{padding-inline:var(--pp-space-4)}.saddle-adv .pp-rowlist{background:transparent;border:0;border-radius:0}.saddle-tools{display:grid;gap:var(--pp-space-3)}.saddle-tools__search{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius);color:var(--s-fg);font:inherit;font-size:var(--s-text);margin-inline:var(--pp-space-4);padding:6px 10px}.saddle-tools__search:focus-visible{outline:2px solid var(--s-focus);outline-offset:1px}.saddle-tools__area{color:var(--s-fg-subtle);font-size:var(--s-text);font-weight:600;margin:0;padding:var(--pp-space-1) var(--pp-space-4)}.saddle-tools__empty,.saddle-tools__intro{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0;padding-inline:var(--pp-space-4)}.saddle-tools__mode{min-width:112px}.saddle-mcp-diag__health{font-size:var(--s-text);margin:0 0 8px}.saddle-mcp-diag__degraded{color:var(--s-danger-text);font-size:var(--s-text);margin:0 0 12px}.saddle-mcp-diag__actions{align-items:center;display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px}.saddle-mcp-diag__empty,.saddle-mcp-diag__hint{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0 0 12px}.saddle-mcp-diag__table{border-collapse:collapse;font-size:var(--s-text);width:100%}.saddle-mcp-diag__table td,.saddle-mcp-diag__table th{border-bottom:1px solid var(--s-border);padding:6px 8px;text-align:right;vertical-align:middle}.saddle-mcp-diag__table th{font-weight:600}.saddle-mcp-diag__table td:first-child,.saddle-mcp-diag__table th{color:var(--s-fg-subtle);white-space:nowrap}.saddle-needs-ok pre{background:var(--s-surface-2);border:1px solid var(--s-border);border-radius:var(--s-radius);margin:0 0 12px;max-height:240px;overflow:auto;padding:10px 12px;white-space:pre-wrap}.saddle-needs-ok__actions{display:flex;gap:8px;margin-top:16px}@media(max-width:600px){.saddle-apps .pp-rowlist__row,.saddle-needs-ok .pp-rowlist__row{flex-wrap:wrap}.saddle-apps .pp-rowlist__actions,.saddle-needs-ok .pp-rowlist__actions{flex-basis:100%;justify-content:flex-start}}.saddle-module-setup{margin-block-end:16px} diff --git a/admin/build/style-index.css b/admin/build/style-index.css index 981175fb..f1fe22b0 100644 --- a/admin/build/style-index.css +++ b/admin/build/style-index.css @@ -1 +1 @@ -.pp-app,.pp-scope,.saddle-app,body.saddle-admin-page{--saddle-brand:#d000c4;--saddle-brand-hover:#b000a6;--saddle-brand-press:#900088;--saddle-brand-text:#a3009a;--saddle-brand-tint:#fde0fb;--saddle-brand-soft:#f9c2f4;--pp-n-50:#f6f7f7;--pp-n-100:#f0f0f1;--pp-n-200:#f6f7f7;--pp-n-300:#dcdcde;--pp-n-400:#c3c4c7;--pp-n-500:#a7aaad;--pp-n-600:#646970;--pp-n-700:#646970;--pp-n-800:#3c434a;--pp-n-900:#2c3338;--pp-n-950:#1d2327;--pp-canvas:#f0f0f1;--pp-surface:#fff;--pp-surface-hover:#f6f7f7;--pp-accent:var( --wp-admin-theme-color,#2271b1 );--pp-accent-hover:var( --wp-admin-theme-color-darker-10,#135e96 );--pp-accent-text:var( --wp-admin-theme-color,#2271b1 );--pp-accent-tint:color-mix(in srgb,var( --pp-accent ) 7%,#fff);--pp-accent-muted:color-mix(in srgb,var( --pp-accent ) 12%,transparent);--pp-on-accent:#fff;--pp-focus:var( --wp-admin-theme-color,#2271b1 );--pp-action:var( --saddle-brand );--pp-action-hover:var( --saddle-brand-hover );--pp-on-action:#fff;--pp-success:#007017;--pp-success-bg:#edfaef;--pp-info:#2271b1;--pp-info-bg:#f0f6fc;--pp-warning:#8a4f00;--pp-warning-bg:#fef8ee;--pp-danger:#b32d2e;--pp-danger-hover:#8a2424;--pp-danger-bg:#fcf0f1;--pp-r-sm:4px;--pp-r:4px;--pp-r-lg:8px;--pp-shadow-xs:0 1px 2px rgba(0,0,0,.04);--pp-shadow-sm:0 1px 2px rgba(0,0,0,.04);--pp-shadow-btn:none;--pp-shadow-btn-hover:none;--pp-text-2xs:12px;--pp-text-xs:13px;--pp-text-sm:13px;--pp-text-md:15px;--pp-text-lg:15px;--pp-text-xl:15px;--pp-text-2xl:15px;--pp-text-3xl:20px;--pp-text-4xl:20px}.saddle-app,body.saddle-admin-page{--s-canvas:var( --pp-canvas );--s-bg:var( --pp-surface );--s-fg:var( --pp-text );--s-fg-muted:var( --pp-text-secondary );--s-fg-subtle:var( --pp-text-muted );--s-border:var( --pp-border );--s-border-strong:var( --pp-border-strong );--s-surface-2:var( --pp-surface-alt );--s-surface-3:var( --pp-surface-hover );--s-action:var( --pp-action );--s-on-action:var( --pp-on-action );--s-accent:var( --pp-accent );--s-accent-text:var( --pp-accent-text );--s-brand:var( --saddle-brand );--s-safe:var( --pp-success );--s-safe-bg:var( --pp-success-bg );--s-safe-text:var( --pp-success );--s-active:var( --pp-warning );--s-active-bg:var( --pp-warning-bg );--s-active-text:var( --pp-warning );--s-danger:var( --pp-danger );--s-danger-text:var( --pp-danger );--s-rail-safe:var( --s-safe-text );--s-rail-change:var( --pp-accent );--s-rail-remove:var( --s-active-text );--s-focus:var( --pp-focus );--s-field-edge:#8c8f94;--s-tab-indicator:#50575e;--s-nav-hover:var( --pp-surface-hover );--s-overlay:#1d2327;--s-overlay-hover:#101517;--s-paused-mark:#b26200;--s-paused-edge:#f5e1c0;--s-typing:var( --pp-n-500 );--s-code-bg:#1d2327;--s-shadow-sm:var( --pp-shadow-sm );--s-shadow-md:var( --pp-shadow-md );--s-text-sm:var( --pp-text-2xs );--s-text:var( --pp-text-sm );--s-text-title:var( --pp-text-md );--s-radius:var( --pp-r );--s-radius-lg:var( --pp-r-lg );--s-radius-pill:var( --pp-r-pill );--s-ease:cubic-bezier(0.4,0,0.2,1);--s-dur:160ms}.pp-app,.pp-scope,.saddle-app,body.saddle-admin-page{--saddle-chart-ink:var( --saddle-brand );--saddle-chart-ink-soft:color-mix(in srgb,var( --saddle-brand ) 14%,transparent);--saddle-chart-compare:#707070;--saddle-chart-grid:var( --pp-border );--saddle-chart-text:var( --pp-text-secondary )}.pp-tone--success{--pp-tone-text:var( --pp-success )}.pp-tone--info{--pp-tone-text:var( --pp-info )}.pp-tone--warning{--pp-tone-text:var( --pp-warning )}.pp-tone--danger{--pp-tone-text:var( --pp-danger )}.pp-applybar,.pp-bulkbar,.pp-coachmark,.pp-toast,.pp-tooltip{--pp-action:var( --s-overlay );--pp-action-hover:var( --s-overlay-hover )}.pp-btn--danger,.pp-btn--primary{--pp-accent-text:var( --pp-on-action );--pp-accent-hover:var( --pp-on-action )}.pp-btn--secondary{--pp-accent-text:var( --pp-text );--pp-accent-hover:var( --pp-text )}.pp-btn--ghost{--pp-accent-text:var( --pp-text-secondary );--pp-accent-hover:var( --pp-text )}.pp-code--dark{--pp-code-bg:var( --s-code-bg )}.pp-code .pp-code__pre code{background:none;font-size:inherit;margin:0;padding:0}.saddle-app .pp-card__title,body.saddle-admin-page .pp-card__title{font-size:var(--s-text);font-weight:600}.saddle-app .pp-rowlist__title,body.saddle-admin-page .pp-rowlist__title{font-weight:600}.saddle-app .pp-control,body.saddle-admin-page .pp-control{--pp-border:var( --s-field-edge );--pp-border-strong:var( --s-fg-muted )}.saddle-app input.pp-control:not(:focus),body.saddle-admin-page input.pp-control:not(:focus){border-color:var(--pp-border)}.saddle-app input.pp-control:hover:not(:focus):not(:disabled),body.saddle-admin-page input.pp-control:hover:not(:focus):not(:disabled){border-color:var(--pp-border-strong)}.saddle-app .pp-switch:not([aria-checked=true]),body.saddle-admin-page .pp-switch:not([aria-checked=true]){background:var(--s-bg);box-shadow:inset 0 0 0 1px var(--s-field-edge)}.saddle-app .pp-switch:not([aria-checked=true]):after,body.saddle-admin-page .pp-switch:not([aria-checked=true]):after{background:var(--s-field-edge);box-shadow:none}.saddle-app .pp-switch:not([aria-checked=true]):hover:not(:disabled),body.saddle-admin-page .pp-switch:not([aria-checked=true]):hover:not(:disabled){background:var(--s-bg);box-shadow:inset 0 0 0 1px var(--s-fg-muted)}.saddle-app .pp-switch:not([aria-checked=true]):hover:not(:disabled):after,body.saddle-admin-page .pp-switch:not([aria-checked=true]):hover:not(:disabled):after{background:var(--s-fg-muted)}.saddle-app .pp-rowlist__icon,body.saddle-admin-page .pp-rowlist__icon{background:transparent}.saddle-app .pp-rowlist,body.saddle-admin-page .pp-rowlist{box-shadow:var(--pp-shadow-xs)}.saddle-app .pp-card .pp-rowlist,.saddle-app .saddle-adv .pp-card,.saddle-app .saddle-adv .pp-rowlist,body.saddle-admin-page .pp-card .pp-rowlist,body.saddle-admin-page .saddle-adv .pp-card,body.saddle-admin-page .saddle-adv .pp-rowlist{box-shadow:none}body.saddle-admin-page #wpcontent{background:var(--s-canvas);padding-inline-start:0}body.saddle-admin-page #wpbody-content{background:var(--s-canvas);box-sizing:border-box;min-height:calc(100vh - var(--wp-admin--admin-bar--height, 32px));padding-bottom:0}body.saddle-admin-page .wrap{margin:0}body.saddle-admin-page #wpfooter{display:none}.saddle-app{box-sizing:border-box;color:var(--s-fg);-webkit-font-smoothing:antialiased}.saddle-app--loading{display:flex;justify-content:center;padding:120px 0}.saddle-app--setup{padding-block-end:var(--pp-space-8)}.saddle-app [role=radio]:focus-visible,.saddle-app a:focus-visible,.saddle-app button:focus-visible{box-shadow:none;outline:2px solid var(--s-focus);outline-offset:2px}.saddle-app button{touch-action:manipulation}.saddle-app h1,.saddle-app h2,.saddle-app h3,.saddle-app h4{color:inherit}.saddle-app a:not([class]){color:var(--s-accent-text)}.saddle-app a:not([class]):hover{color:var(--pp-accent-hover)}.saddle-foreign{max-height:60vh;max-width:min(520px,100vw - 32px);overflow-y:auto}.saddle-foreign__button{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-pill);color:var(--s-fg-muted);cursor:pointer;display:inline-flex;font-size:var(--s-text);gap:6px;height:28px;line-height:1;padding:0 10px}.saddle-foreign__button svg{height:20px;width:20px}.saddle-foreign__button:hover{border-color:var(--s-border-strong);color:var(--s-fg)}.saddle-foreign__head{color:var(--s-fg-subtle);font-size:var(--s-text);margin-bottom:8px}.saddle-foreign__saddle{display:grid;gap:8px;margin-bottom:12px}.saddle-foreign__list{color-scheme:light}.saddle-foreign__list .notice,.saddle-foreign__list div.error,.saddle-foreign__list div.updated{background:var(--s-bg);color:var(--s-fg);margin:0 0 8px;position:relative}.saddle-foreign__list>:last-child{margin-bottom:0}.saddle-app--frame{display:flex;flex-direction:column;min-height:calc(100vh - var(--wp-admin--admin-bar--height, 32px))}.saddle-header{background:var(--s-bg);border-block-end:1px solid var(--s-border);padding:16px 24px}.saddle-header--tabs{padding-block-end:0}.saddle-header__row{align-items:center;display:flex;gap:16px;justify-content:space-between;min-height:32px}.saddle-app .saddle-header__title{align-items:center;color:var(--s-fg-muted);display:flex;flex-wrap:wrap;font-size:var(--s-text-title);font-weight:400;gap:10px;line-height:1.4;margin:0;min-width:0;padding:0}.saddle-app .saddle-header__title [aria-current]{color:var(--s-fg);font-weight:600}.saddle-header__crumb{align-items:center;display:inline-flex;gap:10px;min-width:0}.saddle-header__crumb>[aria-current]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-header__link{--pp-accent-text:var( --s-fg-muted );--pp-accent-hover:var( --s-fg );text-decoration:none}.saddle-header__link:focus,.saddle-header__link:hover{text-decoration:underline}.saddle-header__home{--pp-accent-text:var( --s-fg-muted );--pp-accent-hover:var( --s-fg );align-items:center;display:inline-flex;gap:8px;text-decoration:none}.saddle-header__home:is(a):focus span,.saddle-header__home:is(a):hover span{text-decoration:underline}.saddle-header__home svg{color:var(--s-brand);height:22px;width:22px}.saddle-header__sep{color:var(--s-fg-subtle)}.saddle-header__actions{align-items:center;display:flex;flex:0 0 auto;gap:10px}.saddle-header__tabs{--pp-tabs-gap:0}.saddle-header__tabs .pp-tabs{border-block-end:0;gap:16px;margin:0}.saddle-header__tabs .pp-tabs__tab{color:var(--s-fg);font-size:var(--s-text);font-weight:400;gap:6px;letter-spacing:normal;min-block-size:48px;padding-block:0;white-space:nowrap}.saddle-header__tabs .pp-tabs__tab .saddle-nav-icon{flex:none}.saddle-header__tabs .pp-tabs__indicator{background:var(--s-tab-indicator);border-radius:0;bottom:0;height:1.5px}.saddle-header__tabs{margin-block-start:8px;margin-inline:-4px;overflow-x:auto;overflow-y:visible;padding-inline:4px}.saddle-module{align-items:stretch;display:flex;flex:1 0 auto;min-width:0}.saddle-sections{background:var(--s-bg);border-inline-end:1px solid var(--s-border);flex:0 0 220px;width:220px}.saddle-sections__list{display:flex;flex-direction:column;gap:2px;inset-block-start:32px;list-style:none;margin:0;padding:16px 12px;position:sticky}.saddle-sections__list>li{margin:0}.saddle-sections__list>.saddle-sections__rule{margin:8px 0}.saddle-sections__item{--pp-accent-text:var( --s-fg );--pp-accent-hover:var( --s-fg );align-items:center;border-radius:6px;color:var(--s-fg);display:flex;font-size:var(--s-text);gap:8px;line-height:1.4;min-block-size:32px;padding-inline:10px;text-decoration:none;transition:background var(--s-dur) var(--s-ease)}.saddle-sections__item .saddle-nav-icon{flex:none;height:16px;width:16px}.saddle-sections__item:hover{background:var(--s-nav-hover)}.saddle-sections__item.is-active{background:var(--s-canvas);font-weight:600}.saddle-sections__rule{background:var(--s-border);height:1px;margin:8px 0}.saddle-module__body{display:flex;flex:1 1 auto;flex-direction:column;min-width:0}.saddle-subtabs{--pp-tabs-gap:0}.saddle-subtabs .pp-tabs{border-block-end:0;gap:16px;margin:0}.saddle-subtabs .pp-tabs__tab{color:var(--s-fg);font-size:var(--s-text);font-weight:400;gap:6px;letter-spacing:normal;min-block-size:48px;padding-block:0;white-space:nowrap}.saddle-subtabs .pp-tabs__tab .saddle-nav-icon{flex:none}.saddle-subtabs .pp-tabs__indicator{background:var(--s-tab-indicator);border-radius:0;bottom:0;height:1.5px}.saddle-subtabs{background:var(--s-bg);border-block-end:1px solid var(--s-border);overflow-x:auto}.saddle-module .saddle-frame,.saddle-subtabs{padding-inline:40px}.saddle-module .saddle-frame .pp-content{margin-inline:0}.saddle-frame{flex:1 0 auto;padding:var(--pp-space-5) var(--pp-space-5) var(--pp-space-8)}.saddle-frame__notice{margin-block-end:var(--pp-space-4)}.saddle-footer{align-items:center;border-block-start:1px solid var(--s-border);color:var(--s-fg-subtle);display:flex;flex-wrap:wrap;font-size:var(--s-text);gap:8px 20px;padding:16px 24px}.saddle-footer__brand{align-items:center;display:inline-flex;gap:6px;margin-inline-end:auto}.saddle-footer__brand svg{color:var(--s-fg-subtle);height:14px;width:14px}.saddle-footer a{--pp-accent-text:var( --s-fg-muted );--pp-accent-hover:var( --s-fg );text-decoration:none}.saddle-footer a:focus,.saddle-footer a:hover{text-decoration:underline}.saddle-ai{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-pill);color:var(--s-fg);cursor:pointer;display:inline-flex;flex:0 0 auto;font:inherit;font-size:var(--s-text);font-weight:500;gap:8px;height:32px;padding:0 10px 0 12px;transition:border-color var(--s-dur) var(--s-ease)}.saddle-ai:hover{border-color:var(--s-field-edge)}.saddle-ai svg{color:var(--s-fg-muted)}.saddle-ai__dot{background:var(--s-safe);border-radius:50%;box-shadow:0 0 0 3px var(--s-safe-bg);height:8px;width:8px}.saddle-ai.is-paused .saddle-ai__dot{background:var(--s-paused-mark);box-shadow:0 0 0 3px var(--s-active-bg)}.saddle-ai__panel{display:grid;gap:6px;padding:14px 16px 16px;width:280px}.saddle-ai__panel p{font-size:var(--s-text);margin:0}.saddle-ai__panel .pp-btn{justify-self:start;margin-top:6px}.saddle-ai__head{font-weight:600}.saddle-ai__text{color:var(--s-fg-muted)}.saddle-paused{align-items:center;background:var(--s-active-bg);border-block-end:1px solid var(--s-paused-edge);color:var(--s-active-text);display:flex;flex-wrap:wrap;font-size:var(--s-text);gap:12px;justify-content:space-between;padding:10px 24px}.saddle-paused strong{font-weight:600}@media(max-width:782px){.saddle-header{padding-inline:16px}.saddle-module{flex-direction:column}.saddle-sections{border-block-end:1px solid var(--s-border);border-inline-end:0;flex:none;inset-block-start:46px;position:sticky;width:auto;z-index:2}.saddle-sections__list{align-items:center;flex-direction:row;gap:4px;overflow-x:auto;padding:8px 16px;position:static}.saddle-sections__item{white-space:nowrap}.saddle-sections__list>.saddle-sections__rule,.saddle-sections__rule{flex:none;height:20px;margin:0 6px;width:1px}.saddle-subtabs{padding-inline:16px}.saddle-module .saddle-frame{padding-inline:var(--pp-space-4)}.saddle-header__row{flex-wrap:wrap}.saddle-frame{padding:var(--pp-space-4) var(--pp-space-4) var(--pp-space-6)}.saddle-footer{padding-inline:16px}}@media(max-width:600px){.saddle-sections{inset-block-start:0}}.saddle-section-head{align-items:center;display:flex;gap:var(--pp-space-4);justify-content:space-between}.saddle-section-head__title{font-size:var(--s-text-title);font-weight:600;line-height:1.4;margin:0}.saddle-section-head__name{align-items:center;display:flex;flex-wrap:wrap;gap:6px}.saddle-section-head__desc{color:var(--s-fg-subtle);font-size:var(--s-text);margin:2px 0 0}.saddle-section-head__actions{display:flex;flex:0 0 auto;gap:8px}.saddle-section{align-content:start;display:grid;gap:var(--pp-space-3);grid-template-columns:minmax(0,1fr);min-width:0}.saddle-settings__label,.saddle-signin__label{align-items:center;display:inline-flex;flex-wrap:wrap;gap:6px}.saddle-connect{display:grid;gap:var(--pp-space-3);min-width:0}.saddle-connect__groups{display:grid;gap:var(--pp-space-4);min-width:0}.saddle-connect__group{display:grid;gap:8px}.saddle-connect__group-title{color:var(--s-fg-muted);font-size:var(--s-text);font-weight:600;margin:0}.saddle-connect__other{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-connect__apps{display:grid;gap:8px;grid-template-columns:repeat(auto-fill,minmax(132px,1fr))}.saddle-connect__app{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius);color:var(--s-fg);cursor:pointer;display:flex;font:inherit;font-size:var(--s-text);gap:8px;min-width:0;padding:8px 10px;text-align:start}.saddle-connect__app img{flex:0 0 auto;height:18px;width:18px}.saddle-connect__app:hover{background:var(--s-surface-2)}.saddle-connect__app[aria-pressed=true]{background:var(--s-surface-3);border-color:var(--s-border-strong);font-weight:600}.saddle-connect__app-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-connect__app-skeleton{border-radius:var(--s-radius);display:block;width:100%}.saddle-connect__steps{display:grid;gap:var(--pp-space-4);min-width:0}.saddle-connect__hint{color:var(--s-fg-muted);font-size:var(--s-text);margin:0}.saddle-connect__steps-head{align-items:center;display:flex;gap:12px;justify-content:space-between}.saddle-connect__steps-title{align-items:center;display:flex;font-size:var(--s-text);font-weight:600;gap:10px;margin:0}.saddle-connect__steps-title img{height:20px;width:20px}.saddle-connect__list{counter-reset:saddle-step;display:grid;gap:var(--pp-space-4);list-style:none;margin:0;padding:0}.saddle-connect__list>li{counter-increment:saddle-step;display:grid;gap:10px;margin:0;padding-inline-start:34px;position:relative}.saddle-connect__list>li:before{align-items:center;border:1px solid var(--s-border);border-radius:50%;color:var(--s-fg-muted);content:counter(saddle-step);display:inline-flex;font-size:var(--s-text-sm);height:22px;inset-inline-start:0;justify-content:center;position:absolute;top:0;width:22px}.saddle-connect__list p{margin:0}.saddle-connect__links{display:flex;flex-wrap:wrap;gap:8px}.saddle-connect__config{min-width:0}.saddle-connect__config code,.saddle-connect__config pre{overflow-wrap:break-word;white-space:pre-wrap}.saddle-connect__config code{background:none;margin:0;padding:0}.saddle-connect__values code{background:var(--s-surface-2);border-radius:var(--s-radius);font-size:var(--s-text-sm);overflow-wrap:anywhere;padding:1px 6px}.saddle-connect__values .pp-btn{margin-inline-start:8px}.saddle-connect__key{display:grid;justify-items:start}.saddle-connect__steps>.pp-btn--link{justify-self:start}.saddle-connect__waiting{align-items:center;color:var(--s-fg-muted);display:flex;font-size:var(--s-text);gap:8px;margin:0}.saddle-connect__spinner{animation:saddle-spin .9s linear infinite;border:2px solid var(--s-border-strong);border-radius:50%;border-top-color:var(--s-accent);height:10px;width:10px}@keyframes saddle-spin{to{transform:rotate(1turn)}}.saddle-connect-page{max-width:640px}.saddle-context{align-content:start;display:grid;gap:var(--pp-space-5)}.saddle-context-field .pp-rowlist__body{min-width:0}.saddle-context-field__label{align-items:center;display:inline-flex;gap:6px}.saddle-context-field__text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-context-field__empty{color:var(--s-fg-subtle)}.saddle-context-field__edit{background:var(--s-surface-2);border-block-start:1px solid var(--s-border);display:grid;gap:var(--pp-space-3);padding:var(--pp-space-3) var(--pp-space-4)}.saddle-context-field__actions{align-items:center;display:flex;gap:8px}.saddle-stack{align-content:start;display:grid;gap:var(--pp-space-4)}.saddle-app .pp-content{align-content:start;display:grid;gap:var(--pp-space-5)}.saddle-tabpane{align-content:start;animation:saddle-step-in .24s var(--s-ease) both;display:grid;gap:var(--pp-space-6)}.saddle-chat{margin:0 auto;max-width:680px;padding-block-end:var(--pp-space-8)}.saddle-chat__bar{display:flex;justify-content:flex-end;margin-block-end:8px}.saddle-chat__bar .pp-btn{color:var(--s-fg-muted)}.saddle-chat__thread{display:grid;gap:4px}.saddle-chat__say{align-items:start;animation:saddle-step-in .28s var(--s-ease) both;display:grid;gap:12px;grid-template-columns:28px minmax(0,1fr);margin-block-start:14px}.saddle-chat__say.is-cont{margin-block-start:6px}.saddle-chat__say.is-cont .saddle-chat__avatar{visibility:hidden}.saddle-chat__avatar svg{color:var(--s-brand);display:block;height:28px;width:28px}.saddle-chat__body{display:grid;font-size:var(--s-text-title);gap:12px;grid-template-columns:minmax(0,1fr);line-height:1.5;min-width:0;padding-block-start:3px}.saddle-chat__body>p{margin:0;text-wrap:pretty}.saddle-chat__muted{color:var(--s-fg-muted)}.saddle-chat__typing{display:inline-flex;gap:4px;padding-block-start:10px}.saddle-chat__typing i{animation:saddle-typing 1s ease-in-out infinite;background:var(--s-typing);border-radius:50%;height:6px;width:6px}.saddle-chat__typing i:nth-child(2){animation-delay:.15s}.saddle-chat__typing i:nth-child(3){animation-delay:.3s}.saddle-chat__you{animation:saddle-step-in .22s var(--s-ease) both;display:flex;justify-content:flex-end;margin-block-start:14px}.saddle-chat__bubble{align-items:center;background:var(--s-bg);border-radius:18px 18px 4px 18px;box-shadow:inset 0 0 0 1px var(--s-border);display:inline-flex;font-size:var(--s-text-title);gap:8px;max-width:80%;padding:8px 14px}.saddle-chat__done{align-items:center;color:var(--s-safe-text);display:flex;font-size:var(--s-text);gap:8px;margin:0}.saddle-chat__done:before{background:var(--s-safe) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 16 16%27%3E%3Cpath fill=%27none%27 stroke=%27%23fff%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27 stroke-width=%271.8%27 d=%27m4.2 8.2 2.5 2.5 5.1-5.3%27/%3E%3C/svg%3E") center/16px no-repeat;border-radius:50%;content:"";flex:0 0 auto;height:16px;width:16px}.saddle-chat__calls{color:var(--s-fg-muted);font-size:var(--s-text);margin:0;padding-inline-start:24px}.saddle-chat__replies{animation:saddle-step-in .28s var(--s-ease) both;display:flex;flex-wrap:wrap;gap:8px;margin-block-start:12px;padding-inline-start:40px}.saddle-chat__chip{align-items:center;background:var(--s-bg);border:1px solid var(--s-border-strong);border-radius:var(--s-radius-pill);color:var(--s-fg);cursor:pointer;display:inline-flex;font:inherit;font-size:var(--s-text);font-weight:500;gap:8px;padding:7px 14px 7px 10px;transition:border-color var(--s-dur) var(--s-ease)}.saddle-chat__chip:hover{border-color:var(--s-field-edge)}.saddle-chat__chip--more{color:var(--s-fg-muted);padding-inline-start:14px}.saddle-chat__options{animation:saddle-step-in .28s var(--s-ease) both;display:grid;gap:8px;margin-block-start:12px;max-width:460px;padding-inline-start:40px}.saddle-chat__option{background:var(--s-bg);border:1px solid var(--s-border-strong);border-radius:10px;color:var(--s-fg);cursor:pointer;display:grid;font:inherit;font-size:var(--s-text);gap:1px;justify-items:start;padding:10px 14px;text-align:start;transition:border-color var(--s-dur) var(--s-ease)}.saddle-chat__option:hover:not(:disabled){border-color:var(--s-field-edge)}.saddle-chat__option:disabled{cursor:default;opacity:.6}.saddle-chat__option[aria-busy=true]{border-color:var(--s-brand);opacity:1}.saddle-chat__option strong{font-weight:600}.saddle-chat__option span{color:var(--s-fg-muted)}.saddle-chat__option small{background:var(--saddle-brand-tint);border-radius:var(--s-radius-pill);color:var(--saddle-brand-text);font-size:var(--s-text-sm);margin-block-start:4px;padding:1px 8px}.saddle-chat .pp-snippet__code{font-family:inherit;font-size:var(--s-text);line-height:1.5;overflow-wrap:anywhere;white-space:normal}.saddle-chat .saddle-wizard--embedded{font-size:var(--s-text)}@media(max-width:782px){.saddle-chat__options,.saddle-chat__replies{padding-inline-start:0}}@keyframes saddle-typing{0%,80%,to{opacity:.3}40%{opacity:1}}.saddle-waiting{display:flex;flex-direction:column;gap:10px;margin:4px 0 8px}.saddle-waiting__line{animation:saddle-step-in .28s var(--s-ease) both}.saddle-selfcheck__rows{display:flex;flex-direction:column;gap:12px;margin:4px 0 12px}.saddle-tour__anchor{pointer-events:none;position:fixed;z-index:0}.saddle-home{align-items:start;display:grid;gap:var(--pp-space-6);grid-template-columns:minmax(0,1fr) 280px}.saddle-home__main,.saddle-home__side{display:grid;gap:var(--pp-space-6);grid-template-columns:minmax(0,1fr);min-width:0}.saddle-home--single{grid-template-columns:minmax(0,1fr)}@media(max-width:1100px){.saddle-home{grid-template-columns:minmax(0,1fr)}}.saddle-skeleton{display:grid;gap:var(--pp-space-3)}.saddle-home__connect{align-items:center;display:flex;flex-direction:column;gap:var(--pp-space-4);padding-block:var(--pp-space-8);text-align:center}.saddle-home__connect-text{font-size:var(--s-text);margin:0}.saddle-home__week{padding:var(--pp-space-4) var(--pp-space-5)}.saddle-home__week .pp-card__header{margin-block-end:var(--pp-space-3)}.saddle-home__week .pp-stat{padding-block:0}@media(min-width:561px)and (max-width:900px){.saddle-home__week .pp-stat-grid--fixed{grid-template-columns:repeat(var(--pp-stat-grid-cols,3),minmax(0,1fr))}}.saddle-nudge{align-items:center;display:flex;gap:12px}.saddle-nudge__mark svg{color:var(--s-brand);display:block;height:22px;width:22px}.saddle-nudge__text{flex:1 1 auto;font-size:var(--s-text);margin:0;min-width:0}.saddle-card__empty{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.55;margin:0 0 12px}.saddle-setup-guide{display:grid;gap:20px}.saddle-setup-guide__app{align-items:center;display:flex;font-weight:600;gap:10px}.saddle-setup-guide__app img{height:24px;width:24px}.saddle-setup-guide__step{display:grid;gap:10px;justify-items:start}.saddle-setup-guide__step h3{color:var(--s-fg);font-size:var(--s-text);font-weight:600;margin:0}.saddle-setup-guide__step p{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.55;margin:0}.saddle-setup-guide__step .pp-codeblock,.saddle-setup-guide__step .pp-snippet{width:100%}.saddle-link-danger,.saddle-link-danger:hover:not(:disabled){color:var(--s-danger-text)}.saddle-doc{background:var(--s-surface-2);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);max-height:340px;overflow:auto;padding:16px 18px}.saddle-doc__h{color:var(--s-fg);font-size:var(--s-text);font-weight:600;letter-spacing:.01em;margin:18px 0 8px}.saddle-doc__h:first-child{margin-top:0}.saddle-doc__h--sub,.saddle-doc__p{color:var(--s-fg-muted);font-size:var(--s-text)}.saddle-doc__p{line-height:1.6;margin:0 0 8px}.saddle-doc__list{display:flex;flex-direction:column;gap:5px;margin:0 0 10px;padding-left:18px}.saddle-doc__list li{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.55}.saddle-doc--bare{background:none;border:none;max-height:none;overflow:visible;padding:0}.saddle-memory__compose{align-items:flex-start;display:flex;flex-direction:column;gap:12px;max-width:560px;width:100%}.saddle-memory__compose .pp-field{width:100%}.saddle-memory__compose-actions{align-items:center;display:flex;gap:8px}.saddle-memory .pp-rowlist__body{min-width:0}.saddle-memory__text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-memory__open{border-block-start:1px solid var(--s-border);display:grid;gap:var(--pp-space-2);padding:var(--pp-space-2) var(--pp-space-4) var(--pp-space-3)}.saddle-memory__open-actions{align-items:center;display:flex;gap:12px;justify-content:space-between}.saddle-memory__pin{align-items:center;color:var(--s-fg-muted);display:inline-flex;font-size:var(--s-text);gap:8px}.saddle-memory__setting-label{align-items:center;display:inline-flex;font-weight:600;gap:6px}.saddle-skill-row .pp-rowlist__body{min-width:0}.saddle-skill-row__desc{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-guide__rawtoggle{margin-top:10px}.saddle-guide__skill-actions{align-items:center;display:flex;gap:var(--pp-space-3);justify-content:space-between;margin-top:var(--pp-space-3)}.saddle-guide__skill-actions .saddle-guide__delete{margin-top:0}.saddle-guide__delete{margin-top:var(--pp-space-3)}.saddle-guide__edit{display:grid;gap:var(--pp-space-3)}.saddle-guide__edit textarea{font-family:var(--pp-font-mono,monospace);font-size:var(--s-text);line-height:1.55}.saddle-guide__edit-actions{display:flex;gap:8px}.saddle-guide__system{color:var(--s-fg-muted);margin-top:0;max-height:320px;overflow:auto;white-space:pre-wrap}.saddle-apps__access{min-width:148px;width:auto}.saddle-role{display:grid;gap:2px}.saddle-role__name{font-weight:500}.saddle-role__hint{color:var(--s-fg-muted);font-size:var(--s-text)}.pp-select__trigger .saddle-role__hint{display:none}.saddle-role__list{--pp-accent:var(--saddle-brand);--pp-accent-tint:var(--saddle-brand-tint);min-width:264px}.saddle-role__list .pp-select__item{padding-block:8px}.saddle-role__list .pp-select__item:focus-visible{outline:none}.saddle-apps__address{align-items:center;display:flex;flex-wrap:wrap;font-size:var(--s-text);gap:8px;margin:0}.saddle-apps__address-label{color:var(--s-fg-muted)}.saddle-apps__url{background:var(--s-surface-2);border-radius:var(--s-radius);color:var(--s-fg);font-size:var(--s-text-sm);margin:0;min-width:0;overflow-wrap:anywhere;padding:2px 6px}.saddle-quick__group{display:grid;gap:var(--pp-space-2)}.saddle-quick__group-title{color:var(--s-fg-muted);font-size:var(--s-text);font-weight:600;margin:0}.saddle-quick__cards{display:grid;gap:var(--pp-space-2);grid-template-columns:repeat(auto-fill,minmax(160px,1fr))}.saddle-quick__card{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:6px;box-sizing:border-box;color:var(--s-fg);cursor:pointer;display:flex;font:inherit;font-size:var(--s-text);gap:10px;min-height:44px;min-width:0;padding:0 12px;text-align:start;transition:border-color var(--s-dur) var(--s-ease)}.saddle-quick__card:hover{border-color:var(--s-field-edge)}.saddle-quick__card img{flex:0 0 auto}.saddle-quick__name{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-quick__more{display:grid;gap:var(--pp-space-2)}.saddle-quick__other{font-size:var(--s-text);margin:0}.saddle-apps__empty{color:var(--s-fg-subtle);margin:0}.saddle-apps__advanced{background:var(--s-surface-2);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);display:grid;gap:var(--pp-space-4);margin-top:10px;padding:var(--pp-space-5) var(--pp-space-5)}.saddle-apps__field{display:grid;gap:6px}.saddle-apps__fieldlabel{color:var(--s-fg-muted);font-size:var(--s-text);font-weight:600}.saddle-apps__test{align-items:center;display:flex;gap:12px}.saddle-apps__transport{align-items:center;color:var(--s-fg-subtle);display:flex;font-size:var(--s-text);gap:6px;margin:0}.saddle-wizard{min-width:0}.saddle-wizard__step{animation:saddle-step-in .28s var(--s-ease) both;margin:0 auto;max-width:640px}@keyframes saddle-step-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}.saddle-wizard__lead{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.6;margin:0 0 20px;max-width:60ch;text-wrap:pretty}.saddle-wizard__config{margin:0 0 18px}.saddle-wizard__copy{margin-top:10px}.saddle-wizard__hint{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0 0 16px}.saddle-wizard__actions{align-items:center;display:flex;flex-wrap:wrap;gap:10px;justify-content:flex-end;margin-top:26px}.saddle-wizard__actions--start{justify-content:flex-start;margin-top:0}.saddle-wizard__oauth-gate{margin:0 0 16px}.saddle-wizard__install{align-items:center;display:flex;flex-wrap:wrap;gap:8px 12px;margin:0 0 16px}.saddle-wizard__install .saddle-wizard__hint{margin:0}.saddle-health{font-size:var(--s-text);margin-top:14px}.saddle-health--checking{align-items:center;color:var(--s-fg-subtle);display:flex;gap:6px}.saddle-health--ok{color:var(--s-safe-text);font-weight:600}.saddle-health--muted{color:var(--s-fg-subtle)}.saddle-health__body{color:var(--s-fg-muted);line-height:1.55;margin:0 0 12px}.saddle-health__error{color:var(--s-danger-text)}.saddle-health__actions{align-items:center;display:flex;gap:12px;margin-bottom:12px}@media(prefers-reduced-motion:reduce){.saddle-app *,.saddle-app :after,.saddle-app :before{animation-duration:.01ms!important;transition-duration:.01ms!important}}.saddle-activity__filters{align-items:center;display:flex;flex-wrap:wrap;gap:6px;margin-bottom:16px}.saddle-activity__heading{font-size:var(--s-text-title);font-weight:600;margin:0 auto 0 0}.saddle-activity__loading{display:flex;justify-content:center;padding:60px 0}.saddle-activity__day{margin-bottom:22px}.saddle-activity__daylabel{color:var(--s-fg-subtle);font-size:var(--s-text);font-weight:600;margin:0 0 8px}.saddle-activity__list{background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);box-shadow:var(--s-shadow-sm);margin:0;overflow:hidden}.saddle-activity__row{align-items:center;display:flex;gap:12px;margin:0;padding:10px 16px}.saddle-activity__row+.saddle-activity__row{border-top:1px solid var(--s-border)}.saddle-activity__logo{flex:0 0 auto;height:20px;width:20px}.saddle-activity__mark{background:var(--s-safe);border-radius:50%;flex:0 0 auto;height:8px;margin-inline:6px;width:8px}.saddle-activity__row.is-denied .saddle-activity__mark{background:var(--s-danger)}.saddle-activity__summary{flex:1 1 auto;font-size:var(--s-text);line-height:1.5;min-width:0;overflow:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap}.saddle-activity__time{color:var(--s-fg-subtle);flex:0 0 auto;font-size:var(--s-text);font-variant-numeric:tabular-nums}.saddle-activity__undo{flex:0 0 auto;opacity:0;transition:opacity var(--s-dur) var(--s-ease)}.saddle-activity__row:focus-within .saddle-activity__undo,.saddle-activity__row:hover .saddle-activity__undo,.saddle-activity__undo[aria-busy=true]{opacity:1}@media(hover:none){.saddle-activity__undo{opacity:1}}@media(prefers-reduced-motion:reduce){.saddle-activity__undo{transition:none}}.saddle-activity__undone{color:var(--s-fg-subtle);flex:0 0 auto;font-size:var(--s-text-sm)}.saddle-activity__more{display:flex;justify-content:center;margin-top:4px}.saddle-rows__body{margin:8px 12px 12px;max-height:320px;overflow:auto}.saddle-settings-form{align-content:start;display:grid;gap:var(--pp-space-5)}.saddle-settings-form__agent{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-fields__more,.saddle-fields__more>div{margin-top:var(--pp-space-3)}.saddle-field__control{display:grid;gap:var(--pp-space-1);justify-items:end}.saddle-field__control .pp-control:not(.pp-switch){inline-size:240px;max-inline-size:100%}.saddle-field__status{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-notice__actions{display:flex;flex-wrap:wrap;gap:12px;margin-top:8px}.saddle-svc__row.pp-rowlist__row--clickable:focus-visible{box-shadow:none;outline:2px solid var(--s-focus);outline-offset:-2px}.saddle-svc__line{align-items:center;display:flex;gap:var(--pp-space-3);min-width:0}.saddle-svc__name{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--pp-space-05);min-width:0}.saddle-svc__go{color:var(--s-fg-subtle);flex:0 0 auto}.saddle-svc__empty{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-svc__sends{color:var(--s-fg-muted);font-size:var(--s-text);margin:0}.saddle-svc__detail{display:grid;gap:var(--pp-space-5);min-width:0}.saddle-svc__key{display:grid;gap:var(--pp-space-2);min-width:0}.saddle-svc__key-row{align-items:center;display:flex;flex-wrap:wrap;gap:var(--pp-space-3)}.saddle-svc__key-row code{background:var(--s-surface-2);border-radius:var(--s-radius);font-size:var(--s-text-sm);margin:0;padding:2px 6px}.saddle-svc__key-row .saddle-svc__actions{margin-inline-start:auto}.saddle-svc__key-label{font-weight:600}.saddle-svc__get{font-size:var(--s-text);margin:0}.saddle-svc__actions,.saddle-svc__switch{align-items:center;display:flex;gap:var(--pp-space-2)}.saddle-svc__switch{font-size:var(--s-text)}.saddle-svc__tools .pp-collapsible__trigger:focus-visible{border-radius:var(--s-radius);outline:2px solid var(--s-focus);outline-offset:2px}.saddle-svc__tool-list{border:1px solid var(--s-border);border-radius:var(--s-radius-lg);display:grid;font-size:var(--s-text);list-style:none;margin:0;overflow:hidden;padding:0}.saddle-svc__tool-list li{display:grid;gap:var(--pp-space-3);grid-template-columns:minmax(0,1fr) auto;margin:0;padding:var(--pp-space-2) var(--pp-space-3)}.saddle-svc__tool-list li+li{border-block-start:1px solid var(--s-border)}.saddle-svc__role{color:var(--s-fg-muted);white-space:nowrap}.saddle-adv{background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);box-shadow:var(--s-shadow-sm);overflow:hidden}.saddle-adv>.pp-collapsible+.pp-collapsible{border-block-start:1px solid var(--s-border)}.saddle-adv>.pp-collapsible{scroll-margin-block-start:calc(var(--wp-admin--admin-bar--height, 32px) + var(--pp-space-4))}.saddle-adv .pp-collapsible__trigger{flex-direction:row-reverse;font-size:var(--s-text);font-weight:600;inline-size:100%;justify-content:space-between;padding:var(--pp-space-3) var(--pp-space-4);text-align:start}.saddle-adv .pp-collapsible__panel{padding:0 0 var(--pp-space-3)}.saddle-adv__check{padding-inline:var(--pp-space-4)}.saddle-adv .pp-rowlist{background:transparent;border:0;border-radius:0}.saddle-tools{display:grid;gap:var(--pp-space-3)}.saddle-tools__search{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius);color:var(--s-fg);font:inherit;font-size:var(--s-text);margin-inline:var(--pp-space-4);padding:6px 10px}.saddle-tools__search:focus-visible{outline:2px solid var(--s-focus);outline-offset:1px}.saddle-tools__area{font-weight:600;padding:var(--pp-space-1) var(--pp-space-4)}.saddle-tools__area,.saddle-tools__empty{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-tools__empty{padding-inline:var(--pp-space-4)}.saddle-mcp-diag__health{font-size:var(--s-text);margin:0 0 8px}.saddle-mcp-diag__degraded{color:var(--s-danger-text);font-size:var(--s-text);margin:0 0 12px}.saddle-mcp-diag__actions{align-items:center;display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px}.saddle-mcp-diag__empty,.saddle-mcp-diag__hint{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0 0 12px}.saddle-mcp-diag__table{border-collapse:collapse;font-size:var(--s-text);width:100%}.saddle-mcp-diag__table td,.saddle-mcp-diag__table th{border-bottom:1px solid var(--s-border);padding:6px 8px;text-align:left;vertical-align:middle}.saddle-mcp-diag__table th{font-weight:600}.saddle-mcp-diag__table td:first-child,.saddle-mcp-diag__table th{color:var(--s-fg-subtle);white-space:nowrap}.saddle-needs-ok pre{background:var(--s-surface-2);border:1px solid var(--s-border);border-radius:var(--s-radius);margin:0 0 12px;max-height:240px;overflow:auto;padding:10px 12px;white-space:pre-wrap}.saddle-needs-ok__actions{display:flex;gap:8px;margin-top:16px}@media(max-width:600px){.saddle-apps .pp-rowlist__row,.saddle-needs-ok .pp-rowlist__row{flex-wrap:wrap}.saddle-apps .pp-rowlist__actions,.saddle-needs-ok .pp-rowlist__actions{flex-basis:100%;justify-content:flex-start}}.saddle-module-setup{margin-block-end:16px} +.pp-app,.pp-scope,.saddle-app,body.saddle-admin-page{--saddle-brand:#d000c4;--saddle-brand-hover:#b000a6;--saddle-brand-press:#900088;--saddle-brand-text:#a3009a;--saddle-brand-tint:#fde0fb;--saddle-brand-soft:#f9c2f4;--pp-n-50:#f6f7f7;--pp-n-100:#f0f0f1;--pp-n-200:#f6f7f7;--pp-n-300:#dcdcde;--pp-n-400:#c3c4c7;--pp-n-500:#a7aaad;--pp-n-600:#646970;--pp-n-700:#646970;--pp-n-800:#3c434a;--pp-n-900:#2c3338;--pp-n-950:#1d2327;--pp-canvas:#f0f0f1;--pp-surface:#fff;--pp-surface-hover:#f6f7f7;--pp-accent:var( --wp-admin-theme-color,#2271b1 );--pp-accent-hover:var( --wp-admin-theme-color-darker-10,#135e96 );--pp-accent-text:var( --wp-admin-theme-color,#2271b1 );--pp-accent-tint:color-mix(in srgb,var( --pp-accent ) 7%,#fff);--pp-accent-muted:color-mix(in srgb,var( --pp-accent ) 12%,transparent);--pp-on-accent:#fff;--pp-focus:var( --wp-admin-theme-color,#2271b1 );--pp-action:var( --saddle-brand );--pp-action-hover:var( --saddle-brand-hover );--pp-on-action:#fff;--pp-success:#007017;--pp-success-bg:#edfaef;--pp-info:#2271b1;--pp-info-bg:#f0f6fc;--pp-warning:#8a4f00;--pp-warning-bg:#fef8ee;--pp-danger:#b32d2e;--pp-danger-hover:#8a2424;--pp-danger-bg:#fcf0f1;--pp-r-sm:4px;--pp-r:4px;--pp-r-lg:8px;--pp-shadow-xs:0 1px 2px rgba(0,0,0,.04);--pp-shadow-sm:0 1px 2px rgba(0,0,0,.04);--pp-shadow-btn:none;--pp-shadow-btn-hover:none;--pp-text-2xs:12px;--pp-text-xs:13px;--pp-text-sm:13px;--pp-text-md:15px;--pp-text-lg:15px;--pp-text-xl:15px;--pp-text-2xl:15px;--pp-text-3xl:20px;--pp-text-4xl:20px}.saddle-app,body.saddle-admin-page{--s-canvas:var( --pp-canvas );--s-bg:var( --pp-surface );--s-fg:var( --pp-text );--s-fg-muted:var( --pp-text-secondary );--s-fg-subtle:var( --pp-text-muted );--s-border:var( --pp-border );--s-border-strong:var( --pp-border-strong );--s-surface-2:var( --pp-surface-alt );--s-surface-3:var( --pp-surface-hover );--s-action:var( --pp-action );--s-on-action:var( --pp-on-action );--s-accent:var( --pp-accent );--s-accent-text:var( --pp-accent-text );--s-brand:var( --saddle-brand );--s-safe:var( --pp-success );--s-safe-bg:var( --pp-success-bg );--s-safe-text:var( --pp-success );--s-active:var( --pp-warning );--s-active-bg:var( --pp-warning-bg );--s-active-text:var( --pp-warning );--s-danger:var( --pp-danger );--s-danger-text:var( --pp-danger );--s-rail-safe:var( --s-safe-text );--s-rail-change:var( --pp-accent );--s-rail-remove:var( --s-active-text );--s-focus:var( --pp-focus );--s-field-edge:#8c8f94;--s-tab-indicator:#50575e;--s-nav-hover:var( --pp-surface-hover );--s-overlay:#1d2327;--s-overlay-hover:#101517;--s-paused-mark:#b26200;--s-paused-edge:#f5e1c0;--s-typing:var( --pp-n-500 );--s-code-bg:#1d2327;--s-shadow-sm:var( --pp-shadow-sm );--s-shadow-md:var( --pp-shadow-md );--s-text-sm:var( --pp-text-2xs );--s-text:var( --pp-text-sm );--s-text-title:var( --pp-text-md );--s-radius:var( --pp-r );--s-radius-lg:var( --pp-r-lg );--s-radius-pill:var( --pp-r-pill );--s-ease:cubic-bezier(0.4,0,0.2,1);--s-dur:160ms}.pp-app,.pp-scope,.saddle-app,body.saddle-admin-page{--saddle-chart-ink:var( --saddle-brand );--saddle-chart-ink-soft:color-mix(in srgb,var( --saddle-brand ) 14%,transparent);--saddle-chart-compare:#707070;--saddle-chart-grid:var( --pp-border );--saddle-chart-text:var( --pp-text-secondary )}.pp-tone--success{--pp-tone-text:var( --pp-success )}.pp-tone--info{--pp-tone-text:var( --pp-info )}.pp-tone--warning{--pp-tone-text:var( --pp-warning )}.pp-tone--danger{--pp-tone-text:var( --pp-danger )}.pp-applybar,.pp-bulkbar,.pp-coachmark,.pp-toast,.pp-tooltip{--pp-action:var( --s-overlay );--pp-action-hover:var( --s-overlay-hover )}.pp-btn--danger,.pp-btn--primary{--pp-accent-text:var( --pp-on-action );--pp-accent-hover:var( --pp-on-action )}.pp-btn--secondary{--pp-accent-text:var( --pp-text );--pp-accent-hover:var( --pp-text )}.pp-btn--ghost{--pp-accent-text:var( --pp-text-secondary );--pp-accent-hover:var( --pp-text )}.pp-code--dark{--pp-code-bg:var( --s-code-bg )}.pp-code .pp-code__pre code{background:none;font-size:inherit;margin:0;padding:0}.saddle-app .pp-card__title,body.saddle-admin-page .pp-card__title{font-size:var(--s-text);font-weight:600}.saddle-app .pp-rowlist__title,body.saddle-admin-page .pp-rowlist__title{font-weight:600}.saddle-app .pp-control,body.saddle-admin-page .pp-control{--pp-border:var( --s-field-edge );--pp-border-strong:var( --s-fg-muted )}.saddle-app input.pp-control:not(:focus),body.saddle-admin-page input.pp-control:not(:focus){border-color:var(--pp-border)}.saddle-app input.pp-control:hover:not(:focus):not(:disabled),body.saddle-admin-page input.pp-control:hover:not(:focus):not(:disabled){border-color:var(--pp-border-strong)}.saddle-app .pp-switch:not([aria-checked=true]),body.saddle-admin-page .pp-switch:not([aria-checked=true]){background:var(--s-bg);box-shadow:inset 0 0 0 1px var(--s-field-edge)}.saddle-app .pp-switch:not([aria-checked=true]):after,body.saddle-admin-page .pp-switch:not([aria-checked=true]):after{background:var(--s-field-edge);box-shadow:none}.saddle-app .pp-switch:not([aria-checked=true]):hover:not(:disabled),body.saddle-admin-page .pp-switch:not([aria-checked=true]):hover:not(:disabled){background:var(--s-bg);box-shadow:inset 0 0 0 1px var(--s-fg-muted)}.saddle-app .pp-switch:not([aria-checked=true]):hover:not(:disabled):after,body.saddle-admin-page .pp-switch:not([aria-checked=true]):hover:not(:disabled):after{background:var(--s-fg-muted)}.saddle-app .pp-rowlist__icon,body.saddle-admin-page .pp-rowlist__icon{background:transparent}.saddle-app .pp-rowlist,body.saddle-admin-page .pp-rowlist{box-shadow:var(--pp-shadow-xs)}.saddle-app .pp-card .pp-rowlist,.saddle-app .saddle-adv .pp-card,.saddle-app .saddle-adv .pp-rowlist,body.saddle-admin-page .pp-card .pp-rowlist,body.saddle-admin-page .saddle-adv .pp-card,body.saddle-admin-page .saddle-adv .pp-rowlist{box-shadow:none}body.saddle-admin-page #wpcontent{background:var(--s-canvas);padding-inline-start:0}body.saddle-admin-page #wpbody-content{background:var(--s-canvas);box-sizing:border-box;min-height:calc(100vh - var(--wp-admin--admin-bar--height, 32px));padding-bottom:0}body.saddle-admin-page .wrap{margin:0}body.saddle-admin-page #wpfooter{display:none}.saddle-app{box-sizing:border-box;color:var(--s-fg);-webkit-font-smoothing:antialiased}.saddle-app--loading{display:flex;justify-content:center;padding:120px 0}.saddle-app--setup{padding-block-end:var(--pp-space-8)}.saddle-app [role=radio]:focus-visible,.saddle-app a:focus-visible,.saddle-app button:focus-visible{box-shadow:none;outline:2px solid var(--s-focus);outline-offset:2px}.saddle-app button{touch-action:manipulation}.saddle-app h1,.saddle-app h2,.saddle-app h3,.saddle-app h4{color:inherit}.saddle-app a:not([class]){color:var(--s-accent-text)}.saddle-app a:not([class]):hover{color:var(--pp-accent-hover)}.saddle-foreign{max-height:60vh;max-width:min(520px,100vw - 32px);overflow-y:auto}.saddle-foreign__button{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-pill);color:var(--s-fg-muted);cursor:pointer;display:inline-flex;font-size:var(--s-text);gap:6px;height:28px;line-height:1;padding:0 10px}.saddle-foreign__button svg{height:20px;width:20px}.saddle-foreign__button:hover{border-color:var(--s-border-strong);color:var(--s-fg)}.saddle-foreign__head{color:var(--s-fg-subtle);font-size:var(--s-text);margin-bottom:8px}.saddle-foreign__saddle{display:grid;gap:8px;margin-bottom:12px}.saddle-foreign__list{color-scheme:light}.saddle-foreign__list .notice,.saddle-foreign__list div.error,.saddle-foreign__list div.updated{background:var(--s-bg);color:var(--s-fg);margin:0 0 8px;position:relative}.saddle-foreign__list>:last-child{margin-bottom:0}.saddle-app--frame{display:flex;flex-direction:column;min-height:calc(100vh - var(--wp-admin--admin-bar--height, 32px))}.saddle-header{background:var(--s-bg);border-block-end:1px solid var(--s-border);padding:16px 24px}.saddle-header--tabs{padding-block-end:0}.saddle-header__row{align-items:center;display:flex;gap:16px;justify-content:space-between;min-height:32px}.saddle-app .saddle-header__title{align-items:center;color:var(--s-fg-muted);display:flex;flex-wrap:wrap;font-size:var(--s-text-title);font-weight:400;gap:10px;line-height:1.4;margin:0;min-width:0;padding:0}.saddle-app .saddle-header__title [aria-current]{color:var(--s-fg);font-weight:600}.saddle-header__crumb{align-items:center;display:inline-flex;gap:10px;min-width:0}.saddle-header__crumb>[aria-current]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-header__link{--pp-accent-text:var( --s-fg-muted );--pp-accent-hover:var( --s-fg );text-decoration:none}.saddle-header__link:focus,.saddle-header__link:hover{text-decoration:underline}.saddle-header__home{--pp-accent-text:var( --s-fg-muted );--pp-accent-hover:var( --s-fg );align-items:center;display:inline-flex;gap:8px;text-decoration:none}.saddle-header__home:is(a):focus span,.saddle-header__home:is(a):hover span{text-decoration:underline}.saddle-header__home svg{color:var(--s-brand);height:22px;width:22px}.saddle-header__sep{color:var(--s-fg-subtle)}.saddle-header__actions{align-items:center;display:flex;flex:0 0 auto;gap:10px}.saddle-header__tabs{--pp-tabs-gap:0}.saddle-header__tabs .pp-tabs{border-block-end:0;gap:16px;margin:0}.saddle-header__tabs .pp-tabs__tab{color:var(--s-fg);font-size:var(--s-text);font-weight:400;gap:6px;letter-spacing:normal;min-block-size:48px;padding-block:0;white-space:nowrap}.saddle-header__tabs .pp-tabs__tab .saddle-nav-icon{flex:none}.saddle-header__tabs .pp-tabs__indicator{background:var(--s-tab-indicator);border-radius:0;bottom:0;height:1.5px}.saddle-header__tabs{margin-block-start:8px;margin-inline:-4px;overflow-x:auto;overflow-y:visible;padding-inline:4px}.saddle-module{align-items:stretch;display:flex;flex:1 0 auto;min-width:0}.saddle-sections{background:var(--s-bg);border-inline-end:1px solid var(--s-border);flex:0 0 220px;width:220px}.saddle-sections__list{display:flex;flex-direction:column;gap:2px;inset-block-start:32px;list-style:none;margin:0;padding:16px 12px;position:sticky}.saddle-sections__list>li{margin:0}.saddle-sections__list>.saddle-sections__rule{margin:8px 0}.saddle-sections__item{--pp-accent-text:var( --s-fg );--pp-accent-hover:var( --s-fg );align-items:center;border-radius:6px;color:var(--s-fg);display:flex;font-size:var(--s-text);gap:8px;line-height:1.4;min-block-size:32px;padding-inline:10px;text-decoration:none;transition:background var(--s-dur) var(--s-ease)}.saddle-sections__item .saddle-nav-icon{flex:none;height:16px;width:16px}.saddle-sections__item:hover{background:var(--s-nav-hover)}.saddle-sections__item.is-active{background:var(--s-canvas);font-weight:600}.saddle-sections__rule{background:var(--s-border);height:1px;margin:8px 0}.saddle-module__body{display:flex;flex:1 1 auto;flex-direction:column;min-width:0}.saddle-subtabs{--pp-tabs-gap:0}.saddle-subtabs .pp-tabs{border-block-end:0;gap:16px;margin:0}.saddle-subtabs .pp-tabs__tab{color:var(--s-fg);font-size:var(--s-text);font-weight:400;gap:6px;letter-spacing:normal;min-block-size:48px;padding-block:0;white-space:nowrap}.saddle-subtabs .pp-tabs__tab .saddle-nav-icon{flex:none}.saddle-subtabs .pp-tabs__indicator{background:var(--s-tab-indicator);border-radius:0;bottom:0;height:1.5px}.saddle-subtabs{background:var(--s-bg);border-block-end:1px solid var(--s-border);overflow-x:auto}.saddle-module .saddle-frame,.saddle-subtabs{padding-inline:40px}.saddle-module .saddle-frame .pp-content{margin-inline:0}.saddle-frame{flex:1 0 auto;padding:var(--pp-space-5) var(--pp-space-5) var(--pp-space-8)}.saddle-frame__notice{margin-block-end:var(--pp-space-4)}.saddle-footer{align-items:center;border-block-start:1px solid var(--s-border);color:var(--s-fg-subtle);display:flex;flex-wrap:wrap;font-size:var(--s-text);gap:8px 20px;padding:16px 24px}.saddle-footer__brand{align-items:center;display:inline-flex;gap:6px;margin-inline-end:auto}.saddle-footer__brand svg{color:var(--s-fg-subtle);height:14px;width:14px}.saddle-footer a{--pp-accent-text:var( --s-fg-muted );--pp-accent-hover:var( --s-fg );text-decoration:none}.saddle-footer a:focus,.saddle-footer a:hover{text-decoration:underline}.saddle-ai{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-pill);color:var(--s-fg);cursor:pointer;display:inline-flex;flex:0 0 auto;font:inherit;font-size:var(--s-text);font-weight:500;gap:8px;height:32px;padding:0 10px 0 12px;transition:border-color var(--s-dur) var(--s-ease)}.saddle-ai:hover{border-color:var(--s-field-edge)}.saddle-ai svg{color:var(--s-fg-muted)}.saddle-ai__dot{background:var(--s-safe);border-radius:50%;box-shadow:0 0 0 3px var(--s-safe-bg);height:8px;width:8px}.saddle-ai.is-paused .saddle-ai__dot{background:var(--s-paused-mark);box-shadow:0 0 0 3px var(--s-active-bg)}.saddle-ai__panel{display:grid;gap:6px;padding:14px 16px 16px;width:280px}.saddle-ai__panel p{font-size:var(--s-text);margin:0}.saddle-ai__panel .pp-btn{justify-self:start;margin-top:6px}.saddle-ai__head{font-weight:600}.saddle-ai__text{color:var(--s-fg-muted)}.saddle-paused{align-items:center;background:var(--s-active-bg);border-block-end:1px solid var(--s-paused-edge);color:var(--s-active-text);display:flex;flex-wrap:wrap;font-size:var(--s-text);gap:12px;justify-content:space-between;padding:10px 24px}.saddle-paused strong{font-weight:600}@media(max-width:782px){.saddle-header{padding-inline:16px}.saddle-module{flex-direction:column}.saddle-sections{border-block-end:1px solid var(--s-border);border-inline-end:0;flex:none;inset-block-start:46px;position:sticky;width:auto;z-index:2}.saddle-sections__list{align-items:center;flex-direction:row;gap:4px;overflow-x:auto;padding:8px 16px;position:static}.saddle-sections__item{white-space:nowrap}.saddle-sections__list>.saddle-sections__rule,.saddle-sections__rule{flex:none;height:20px;margin:0 6px;width:1px}.saddle-subtabs{padding-inline:16px}.saddle-module .saddle-frame{padding-inline:var(--pp-space-4)}.saddle-header__row{flex-wrap:wrap}.saddle-frame{padding:var(--pp-space-4) var(--pp-space-4) var(--pp-space-6)}.saddle-footer{padding-inline:16px}}@media(max-width:600px){.saddle-sections{inset-block-start:0}}.saddle-section-head{align-items:center;display:flex;gap:var(--pp-space-4);justify-content:space-between}.saddle-section-head__title{font-size:var(--s-text-title);font-weight:600;line-height:1.4;margin:0}.saddle-section-head__name{align-items:center;display:flex;flex-wrap:wrap;gap:6px}.saddle-section-head__desc{color:var(--s-fg-subtle);font-size:var(--s-text);margin:2px 0 0}.saddle-section-head__actions{display:flex;flex:0 0 auto;gap:8px}.saddle-section{align-content:start;display:grid;gap:var(--pp-space-3);grid-template-columns:minmax(0,1fr);min-width:0}.saddle-settings__label,.saddle-signin__label{align-items:center;display:inline-flex;flex-wrap:wrap;gap:6px}.saddle-connect{display:grid;gap:var(--pp-space-3);min-width:0}.saddle-connect__groups{display:grid;gap:var(--pp-space-4);min-width:0}.saddle-connect__group{display:grid;gap:8px}.saddle-connect__group-title{color:var(--s-fg-muted);font-size:var(--s-text);font-weight:600;margin:0}.saddle-connect__other{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-connect__apps{display:grid;gap:8px;grid-template-columns:repeat(auto-fill,minmax(132px,1fr))}.saddle-connect__app{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius);color:var(--s-fg);cursor:pointer;display:flex;font:inherit;font-size:var(--s-text);gap:8px;min-width:0;padding:8px 10px;text-align:start}.saddle-connect__app img{flex:0 0 auto;height:18px;width:18px}.saddle-connect__app:hover{background:var(--s-surface-2)}.saddle-connect__app[aria-pressed=true]{background:var(--s-surface-3);border-color:var(--s-border-strong);font-weight:600}.saddle-connect__app-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-connect__app-skeleton{border-radius:var(--s-radius);display:block;width:100%}.saddle-connect__steps{display:grid;gap:var(--pp-space-4);min-width:0}.saddle-connect__hint{color:var(--s-fg-muted);font-size:var(--s-text);margin:0}.saddle-connect__steps-head{align-items:center;display:flex;gap:12px;justify-content:space-between}.saddle-connect__steps-title{align-items:center;display:flex;font-size:var(--s-text);font-weight:600;gap:10px;margin:0}.saddle-connect__steps-title img{height:20px;width:20px}.saddle-connect__list{counter-reset:saddle-step;display:grid;gap:var(--pp-space-4);list-style:none;margin:0;padding:0}.saddle-connect__list>li{counter-increment:saddle-step;display:grid;gap:10px;margin:0;padding-inline-start:34px;position:relative}.saddle-connect__list>li:before{align-items:center;border:1px solid var(--s-border);border-radius:50%;color:var(--s-fg-muted);content:counter(saddle-step);display:inline-flex;font-size:var(--s-text-sm);height:22px;inset-inline-start:0;justify-content:center;position:absolute;top:0;width:22px}.saddle-connect__list p{margin:0}.saddle-connect__links{display:flex;flex-wrap:wrap;gap:8px}.saddle-connect__config{min-width:0}.saddle-connect__config code,.saddle-connect__config pre{overflow-wrap:break-word;white-space:pre-wrap}.saddle-connect__config code{background:none;margin:0;padding:0}.saddle-connect__values code{background:var(--s-surface-2);border-radius:var(--s-radius);font-size:var(--s-text-sm);overflow-wrap:anywhere;padding:1px 6px}.saddle-connect__values .pp-btn{margin-inline-start:8px}.saddle-connect__key{display:grid;justify-items:start}.saddle-connect__steps>.pp-btn--link{justify-self:start}.saddle-connect__waiting{align-items:center;color:var(--s-fg-muted);display:flex;font-size:var(--s-text);gap:8px;margin:0}.saddle-connect__spinner{animation:saddle-spin .9s linear infinite;border:2px solid var(--s-border-strong);border-radius:50%;border-top-color:var(--s-accent);height:10px;width:10px}@keyframes saddle-spin{to{transform:rotate(1turn)}}.saddle-connect-page{max-width:640px}.saddle-context{align-content:start;display:grid;gap:var(--pp-space-5)}.saddle-context-field .pp-rowlist__body{min-width:0}.saddle-context-field__label{align-items:center;display:inline-flex;gap:6px}.saddle-context-field__text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-context-field__empty{color:var(--s-fg-subtle)}.saddle-context-field__edit{background:var(--s-surface-2);border-block-start:1px solid var(--s-border);display:grid;gap:var(--pp-space-3);padding:var(--pp-space-3) var(--pp-space-4)}.saddle-context-field__actions{align-items:center;display:flex;gap:8px}.saddle-stack{align-content:start;display:grid;gap:var(--pp-space-4)}.saddle-app .pp-content{align-content:start;display:grid;gap:var(--pp-space-5)}.saddle-tabpane{align-content:start;animation:saddle-step-in .24s var(--s-ease) both;display:grid;gap:var(--pp-space-6)}.saddle-chat{margin:0 auto;max-width:680px;padding-block-end:var(--pp-space-8)}.saddle-chat__bar{display:flex;justify-content:flex-end;margin-block-end:8px}.saddle-chat__bar .pp-btn{color:var(--s-fg-muted)}.saddle-chat__thread{display:grid;gap:4px}.saddle-chat__say{align-items:start;animation:saddle-step-in .28s var(--s-ease) both;display:grid;gap:12px;grid-template-columns:28px minmax(0,1fr);margin-block-start:14px}.saddle-chat__say.is-cont{margin-block-start:6px}.saddle-chat__say.is-cont .saddle-chat__avatar{visibility:hidden}.saddle-chat__avatar svg{color:var(--s-brand);display:block;height:28px;width:28px}.saddle-chat__body{display:grid;font-size:var(--s-text-title);gap:12px;grid-template-columns:minmax(0,1fr);line-height:1.5;min-width:0;padding-block-start:3px}.saddle-chat__body>p{margin:0;text-wrap:pretty}.saddle-chat__muted{color:var(--s-fg-muted)}.saddle-chat__typing{display:inline-flex;gap:4px;padding-block-start:10px}.saddle-chat__typing i{animation:saddle-typing 1s ease-in-out infinite;background:var(--s-typing);border-radius:50%;height:6px;width:6px}.saddle-chat__typing i:nth-child(2){animation-delay:.15s}.saddle-chat__typing i:nth-child(3){animation-delay:.3s}.saddle-chat__you{animation:saddle-step-in .22s var(--s-ease) both;display:flex;justify-content:flex-end;margin-block-start:14px}.saddle-chat__bubble{align-items:center;background:var(--s-bg);border-radius:18px 18px 4px 18px;box-shadow:inset 0 0 0 1px var(--s-border);display:inline-flex;font-size:var(--s-text-title);gap:8px;max-width:80%;padding:8px 14px}.saddle-chat__done{align-items:center;color:var(--s-safe-text);display:flex;font-size:var(--s-text);gap:8px;margin:0}.saddle-chat__done:before{background:var(--s-safe) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 16 16%27%3E%3Cpath fill=%27none%27 stroke=%27%23fff%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27 stroke-width=%271.8%27 d=%27m4.2 8.2 2.5 2.5 5.1-5.3%27/%3E%3C/svg%3E") center/16px no-repeat;border-radius:50%;content:"";flex:0 0 auto;height:16px;width:16px}.saddle-chat__calls{color:var(--s-fg-muted);font-size:var(--s-text);margin:0;padding-inline-start:24px}.saddle-chat__replies{animation:saddle-step-in .28s var(--s-ease) both;display:flex;flex-wrap:wrap;gap:8px;margin-block-start:12px;padding-inline-start:40px}.saddle-chat__chip{align-items:center;background:var(--s-bg);border:1px solid var(--s-border-strong);border-radius:var(--s-radius-pill);color:var(--s-fg);cursor:pointer;display:inline-flex;font:inherit;font-size:var(--s-text);font-weight:500;gap:8px;padding:7px 14px 7px 10px;transition:border-color var(--s-dur) var(--s-ease)}.saddle-chat__chip:hover{border-color:var(--s-field-edge)}.saddle-chat__chip--more{color:var(--s-fg-muted);padding-inline-start:14px}.saddle-chat__options{animation:saddle-step-in .28s var(--s-ease) both;display:grid;gap:8px;margin-block-start:12px;max-width:460px;padding-inline-start:40px}.saddle-chat__option{background:var(--s-bg);border:1px solid var(--s-border-strong);border-radius:10px;color:var(--s-fg);cursor:pointer;display:grid;font:inherit;font-size:var(--s-text);gap:1px;justify-items:start;padding:10px 14px;text-align:start;transition:border-color var(--s-dur) var(--s-ease)}.saddle-chat__option:hover:not(:disabled){border-color:var(--s-field-edge)}.saddle-chat__option:disabled{cursor:default;opacity:.6}.saddle-chat__option[aria-busy=true]{border-color:var(--s-brand);opacity:1}.saddle-chat__option strong{font-weight:600}.saddle-chat__option span{color:var(--s-fg-muted)}.saddle-chat__option small{background:var(--saddle-brand-tint);border-radius:var(--s-radius-pill);color:var(--saddle-brand-text);font-size:var(--s-text-sm);margin-block-start:4px;padding:1px 8px}.saddle-chat .pp-snippet__code{font-family:inherit;font-size:var(--s-text);line-height:1.5;overflow-wrap:anywhere;white-space:normal}.saddle-chat .saddle-wizard--embedded{font-size:var(--s-text)}@media(max-width:782px){.saddle-chat__options,.saddle-chat__replies{padding-inline-start:0}}@keyframes saddle-typing{0%,80%,to{opacity:.3}40%{opacity:1}}.saddle-waiting{display:flex;flex-direction:column;gap:10px;margin:4px 0 8px}.saddle-waiting__line{animation:saddle-step-in .28s var(--s-ease) both}.saddle-selfcheck__rows{display:flex;flex-direction:column;gap:12px;margin:4px 0 12px}.saddle-tour__anchor{pointer-events:none;position:fixed;z-index:0}.saddle-home{align-items:start;display:grid;gap:var(--pp-space-6);grid-template-columns:minmax(0,1fr) 280px}.saddle-home__main,.saddle-home__side{display:grid;gap:var(--pp-space-6);grid-template-columns:minmax(0,1fr);min-width:0}.saddle-home--single{grid-template-columns:minmax(0,1fr)}@media(max-width:1100px){.saddle-home{grid-template-columns:minmax(0,1fr)}}.saddle-skeleton{display:grid;gap:var(--pp-space-3)}.saddle-home__connect{align-items:center;display:flex;flex-direction:column;gap:var(--pp-space-4);padding-block:var(--pp-space-8);text-align:center}.saddle-home__connect-text{font-size:var(--s-text);margin:0}.saddle-home__week{padding:var(--pp-space-4) var(--pp-space-5)}.saddle-home__week .pp-card__header{margin-block-end:var(--pp-space-3)}.saddle-home__week .pp-stat{padding-block:0}@media(min-width:561px)and (max-width:900px){.saddle-home__week .pp-stat-grid--fixed{grid-template-columns:repeat(var(--pp-stat-grid-cols,3),minmax(0,1fr))}}.saddle-nudge{align-items:center;display:flex;gap:12px}.saddle-nudge__mark svg{color:var(--s-brand);display:block;height:22px;width:22px}.saddle-nudge__text{flex:1 1 auto;font-size:var(--s-text);margin:0;min-width:0}.saddle-card__empty{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.55;margin:0 0 12px}.saddle-setup-guide{display:grid;gap:20px}.saddle-setup-guide__app{align-items:center;display:flex;font-weight:600;gap:10px}.saddle-setup-guide__app img{height:24px;width:24px}.saddle-setup-guide__step{display:grid;gap:10px;justify-items:start}.saddle-setup-guide__step h3{color:var(--s-fg);font-size:var(--s-text);font-weight:600;margin:0}.saddle-setup-guide__step p{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.55;margin:0}.saddle-setup-guide__step .pp-codeblock,.saddle-setup-guide__step .pp-snippet{width:100%}.saddle-link-danger,.saddle-link-danger:hover:not(:disabled){color:var(--s-danger-text)}.saddle-doc{background:var(--s-surface-2);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);max-height:340px;overflow:auto;padding:16px 18px}.saddle-doc__h{color:var(--s-fg);font-size:var(--s-text);font-weight:600;letter-spacing:.01em;margin:18px 0 8px}.saddle-doc__h:first-child{margin-top:0}.saddle-doc__h--sub,.saddle-doc__p{color:var(--s-fg-muted);font-size:var(--s-text)}.saddle-doc__p{line-height:1.6;margin:0 0 8px}.saddle-doc__list{display:flex;flex-direction:column;gap:5px;margin:0 0 10px;padding-left:18px}.saddle-doc__list li{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.55}.saddle-doc--bare{background:none;border:none;max-height:none;overflow:visible;padding:0}.saddle-memory__compose{align-items:flex-start;display:flex;flex-direction:column;gap:12px;max-width:560px;width:100%}.saddle-memory__compose .pp-field{width:100%}.saddle-memory__compose-actions{align-items:center;display:flex;gap:8px}.saddle-memory .pp-rowlist__body{min-width:0}.saddle-memory__text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-memory__open{border-block-start:1px solid var(--s-border);display:grid;gap:var(--pp-space-2);padding:var(--pp-space-2) var(--pp-space-4) var(--pp-space-3)}.saddle-memory__open-actions{align-items:center;display:flex;gap:12px;justify-content:space-between}.saddle-memory__pin{align-items:center;color:var(--s-fg-muted);display:inline-flex;font-size:var(--s-text);gap:8px}.saddle-memory__setting-label{align-items:center;display:inline-flex;font-weight:600;gap:6px}.saddle-skill-row .pp-rowlist__body{min-width:0}.saddle-skill-row__desc{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-guide__rawtoggle{margin-top:10px}.saddle-guide__skill-actions{align-items:center;display:flex;gap:var(--pp-space-3);justify-content:space-between;margin-top:var(--pp-space-3)}.saddle-guide__skill-actions .saddle-guide__delete{margin-top:0}.saddle-guide__delete{margin-top:var(--pp-space-3)}.saddle-guide__edit{display:grid;gap:var(--pp-space-3)}.saddle-guide__edit textarea{font-family:var(--pp-font-mono,monospace);font-size:var(--s-text);line-height:1.55}.saddle-guide__edit-actions{display:flex;gap:8px}.saddle-guide__system{color:var(--s-fg-muted);margin-top:0;max-height:320px;overflow:auto;white-space:pre-wrap}.saddle-apps__access{min-width:148px;width:auto}.saddle-role{display:grid;gap:2px}.saddle-role__name{font-weight:500}.saddle-role__hint{color:var(--s-fg-muted);font-size:var(--s-text)}.pp-select__trigger .saddle-role__hint{display:none}.saddle-role__list{--pp-accent:var(--saddle-brand);--pp-accent-tint:var(--saddle-brand-tint);min-width:264px}.saddle-role__list .pp-select__item{padding-block:8px}.saddle-role__list .pp-select__item:focus-visible{outline:none}.saddle-apps__address{align-items:center;display:flex;flex-wrap:wrap;font-size:var(--s-text);gap:8px;margin:0}.saddle-apps__address-label{color:var(--s-fg-muted)}.saddle-apps__url{background:var(--s-surface-2);border-radius:var(--s-radius);color:var(--s-fg);font-size:var(--s-text-sm);margin:0;min-width:0;overflow-wrap:anywhere;padding:2px 6px}.saddle-quick__group{display:grid;gap:var(--pp-space-2)}.saddle-quick__group-title{color:var(--s-fg-muted);font-size:var(--s-text);font-weight:600;margin:0}.saddle-quick__cards{display:grid;gap:var(--pp-space-2);grid-template-columns:repeat(auto-fill,minmax(160px,1fr))}.saddle-quick__card{align-items:center;background:var(--s-bg);border:1px solid var(--s-border);border-radius:6px;box-sizing:border-box;color:var(--s-fg);cursor:pointer;display:flex;font:inherit;font-size:var(--s-text);gap:10px;min-height:44px;min-width:0;padding:0 12px;text-align:start;transition:border-color var(--s-dur) var(--s-ease)}.saddle-quick__card:hover{border-color:var(--s-field-edge)}.saddle-quick__card img{flex:0 0 auto}.saddle-quick__name{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.saddle-quick__more{display:grid;gap:var(--pp-space-2)}.saddle-quick__other{font-size:var(--s-text);margin:0}.saddle-apps__empty{color:var(--s-fg-subtle);margin:0}.saddle-apps__advanced{background:var(--s-surface-2);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);display:grid;gap:var(--pp-space-4);margin-top:10px;padding:var(--pp-space-5) var(--pp-space-5)}.saddle-apps__field{display:grid;gap:6px}.saddle-apps__fieldlabel{color:var(--s-fg-muted);font-size:var(--s-text);font-weight:600}.saddle-apps__test{align-items:center;display:flex;gap:12px}.saddle-apps__transport{align-items:center;color:var(--s-fg-subtle);display:flex;font-size:var(--s-text);gap:6px;margin:0}.saddle-wizard{min-width:0}.saddle-wizard__step{animation:saddle-step-in .28s var(--s-ease) both;margin:0 auto;max-width:640px}@keyframes saddle-step-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}.saddle-wizard__lead{color:var(--s-fg-muted);font-size:var(--s-text);line-height:1.6;margin:0 0 20px;max-width:60ch;text-wrap:pretty}.saddle-wizard__config{margin:0 0 18px}.saddle-wizard__copy{margin-top:10px}.saddle-wizard__hint{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0 0 16px}.saddle-wizard__actions{align-items:center;display:flex;flex-wrap:wrap;gap:10px;justify-content:flex-end;margin-top:26px}.saddle-wizard__actions--start{justify-content:flex-start;margin-top:0}.saddle-wizard__oauth-gate{margin:0 0 16px}.saddle-wizard__install{align-items:center;display:flex;flex-wrap:wrap;gap:8px 12px;margin:0 0 16px}.saddle-wizard__install .saddle-wizard__hint{margin:0}.saddle-health{font-size:var(--s-text);margin-top:14px}.saddle-health--checking{align-items:center;color:var(--s-fg-subtle);display:flex;gap:6px}.saddle-health--ok{color:var(--s-safe-text);font-weight:600}.saddle-health--muted{color:var(--s-fg-subtle)}.saddle-health__body{color:var(--s-fg-muted);line-height:1.55;margin:0 0 12px}.saddle-health__error{color:var(--s-danger-text)}.saddle-health__actions{align-items:center;display:flex;gap:12px;margin-bottom:12px}@media(prefers-reduced-motion:reduce){.saddle-app *,.saddle-app :after,.saddle-app :before{animation-duration:.01ms!important;transition-duration:.01ms!important}}.saddle-activity__filters{align-items:center;display:flex;flex-wrap:wrap;gap:6px;margin-bottom:16px}.saddle-activity__heading{font-size:var(--s-text-title);font-weight:600;margin:0 auto 0 0}.saddle-activity__loading{display:flex;justify-content:center;padding:60px 0}.saddle-activity__day{margin-bottom:22px}.saddle-activity__daylabel{color:var(--s-fg-subtle);font-size:var(--s-text);font-weight:600;margin:0 0 8px}.saddle-activity__list{background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);box-shadow:var(--s-shadow-sm);margin:0;overflow:hidden}.saddle-activity__row{align-items:center;display:flex;gap:12px;margin:0;padding:10px 16px}.saddle-activity__row+.saddle-activity__row{border-top:1px solid var(--s-border)}.saddle-activity__logo{flex:0 0 auto;height:20px;width:20px}.saddle-activity__mark{background:var(--s-safe);border-radius:50%;flex:0 0 auto;height:8px;margin-inline:6px;width:8px}.saddle-activity__row.is-denied .saddle-activity__mark{background:var(--s-danger)}.saddle-activity__summary{flex:1 1 auto;font-size:var(--s-text);line-height:1.5;min-width:0;overflow:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap}.saddle-activity__time{color:var(--s-fg-subtle);flex:0 0 auto;font-size:var(--s-text);font-variant-numeric:tabular-nums}.saddle-activity__undo{flex:0 0 auto;opacity:0;transition:opacity var(--s-dur) var(--s-ease)}.saddle-activity__row:focus-within .saddle-activity__undo,.saddle-activity__row:hover .saddle-activity__undo,.saddle-activity__undo[aria-busy=true]{opacity:1}@media(hover:none){.saddle-activity__undo{opacity:1}}@media(prefers-reduced-motion:reduce){.saddle-activity__undo{transition:none}}.saddle-activity__undone{color:var(--s-fg-subtle);flex:0 0 auto;font-size:var(--s-text-sm)}.saddle-activity__more{display:flex;justify-content:center;margin-top:4px}.saddle-rows__body{margin:8px 12px 12px;max-height:320px;overflow:auto}.saddle-settings-form{align-content:start;display:grid;gap:var(--pp-space-5)}.saddle-settings-form__agent{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-fields__more,.saddle-fields__more>div{margin-top:var(--pp-space-3)}.saddle-field__control{display:grid;gap:var(--pp-space-1);justify-items:end}.saddle-field__control .pp-control:not(.pp-switch){inline-size:240px;max-inline-size:100%}.saddle-field__status{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-notice__actions{display:flex;flex-wrap:wrap;gap:12px;margin-top:8px}.saddle-svc__row.pp-rowlist__row--clickable:focus-visible{box-shadow:none;outline:2px solid var(--s-focus);outline-offset:-2px}.saddle-svc__line{align-items:center;display:flex;gap:var(--pp-space-3);min-width:0}.saddle-svc__name{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--pp-space-05);min-width:0}.saddle-svc__go{color:var(--s-fg-subtle);flex:0 0 auto}.saddle-svc__empty{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0}.saddle-svc__sends{color:var(--s-fg-muted);font-size:var(--s-text);margin:0}.saddle-svc__detail{display:grid;gap:var(--pp-space-5);min-width:0}.saddle-svc__key{display:grid;gap:var(--pp-space-2);min-width:0}.saddle-svc__key-row{align-items:center;display:flex;flex-wrap:wrap;gap:var(--pp-space-3)}.saddle-svc__key-row code{background:var(--s-surface-2);border-radius:var(--s-radius);font-size:var(--s-text-sm);margin:0;padding:2px 6px}.saddle-svc__key-row .saddle-svc__actions{margin-inline-start:auto}.saddle-svc__key-label{font-weight:600}.saddle-svc__get{font-size:var(--s-text);margin:0}.saddle-svc__actions,.saddle-svc__switch{align-items:center;display:flex;gap:var(--pp-space-2)}.saddle-svc__switch{font-size:var(--s-text)}.saddle-svc__tools .pp-collapsible__trigger:focus-visible{border-radius:var(--s-radius);outline:2px solid var(--s-focus);outline-offset:2px}.saddle-svc__tool-list{border:1px solid var(--s-border);border-radius:var(--s-radius-lg);display:grid;font-size:var(--s-text);list-style:none;margin:0;overflow:hidden;padding:0}.saddle-svc__tool-list li{display:grid;gap:var(--pp-space-3);grid-template-columns:minmax(0,1fr) auto;margin:0;padding:var(--pp-space-2) var(--pp-space-3)}.saddle-svc__tool-list li+li{border-block-start:1px solid var(--s-border)}.saddle-svc__role{color:var(--s-fg-muted);white-space:nowrap}.saddle-adv{background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius-lg);box-shadow:var(--s-shadow-sm);overflow:hidden}.saddle-adv>.pp-collapsible+.pp-collapsible{border-block-start:1px solid var(--s-border)}.saddle-adv>.pp-collapsible{scroll-margin-block-start:calc(var(--wp-admin--admin-bar--height, 32px) + var(--pp-space-4))}.saddle-adv .pp-collapsible__trigger{flex-direction:row-reverse;font-size:var(--s-text);font-weight:600;inline-size:100%;justify-content:space-between;padding:var(--pp-space-3) var(--pp-space-4);text-align:start}.saddle-adv .pp-collapsible__panel{padding:0 0 var(--pp-space-3)}.saddle-adv__check{padding-inline:var(--pp-space-4)}.saddle-adv .pp-rowlist{background:transparent;border:0;border-radius:0}.saddle-tools{display:grid;gap:var(--pp-space-3)}.saddle-tools__search{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:var(--s-bg);border:1px solid var(--s-border);border-radius:var(--s-radius);color:var(--s-fg);font:inherit;font-size:var(--s-text);margin-inline:var(--pp-space-4);padding:6px 10px}.saddle-tools__search:focus-visible{outline:2px solid var(--s-focus);outline-offset:1px}.saddle-tools__area{color:var(--s-fg-subtle);font-size:var(--s-text);font-weight:600;margin:0;padding:var(--pp-space-1) var(--pp-space-4)}.saddle-tools__empty,.saddle-tools__intro{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0;padding-inline:var(--pp-space-4)}.saddle-tools__mode{min-width:112px}.saddle-mcp-diag__health{font-size:var(--s-text);margin:0 0 8px}.saddle-mcp-diag__degraded{color:var(--s-danger-text);font-size:var(--s-text);margin:0 0 12px}.saddle-mcp-diag__actions{align-items:center;display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px}.saddle-mcp-diag__empty,.saddle-mcp-diag__hint{color:var(--s-fg-subtle);font-size:var(--s-text);margin:0 0 12px}.saddle-mcp-diag__table{border-collapse:collapse;font-size:var(--s-text);width:100%}.saddle-mcp-diag__table td,.saddle-mcp-diag__table th{border-bottom:1px solid var(--s-border);padding:6px 8px;text-align:left;vertical-align:middle}.saddle-mcp-diag__table th{font-weight:600}.saddle-mcp-diag__table td:first-child,.saddle-mcp-diag__table th{color:var(--s-fg-subtle);white-space:nowrap}.saddle-needs-ok pre{background:var(--s-surface-2);border:1px solid var(--s-border);border-radius:var(--s-radius);margin:0 0 12px;max-height:240px;overflow:auto;padding:10px 12px;white-space:pre-wrap}.saddle-needs-ok__actions{display:flex;gap:8px;margin-top:16px}@media(max-width:600px){.saddle-apps .pp-rowlist__row,.saddle-needs-ok .pp-rowlist__row{flex-wrap:wrap}.saddle-apps .pp-rowlist__actions,.saddle-needs-ok .pp-rowlist__actions{flex-basis:100%;justify-content:flex-start}}.saddle-module-setup{margin-block-end:16px} diff --git a/admin/src/components/NeedsYourOk.jsx b/admin/src/components/NeedsYourOk.jsx index d66e7bbc..d4556e65 100644 --- a/admin/src/components/NeedsYourOk.jsx +++ b/admin/src/components/NeedsYourOk.jsx @@ -106,11 +106,25 @@ export default function NeedsYourOk( { onChange } ) { + } + /> + } actions={ - flip( tool.short ) } - aria-label={ tool.label } - /> + <> + { tool.new && + 'ask' === + modes.get( tool.short ) && ( + + + + { __( + 'New', + 'saddle' + ) } + + + + ) } +