yz-yuki-plugin 2.0.4-2 → 2.0.4-4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,6 @@
1
1
  # 2.0.4
2
+ * 优化B站风控相关,新增bili_tiket参数
3
+ * fix Repeated Instantiation Puppeteer
2
4
  * 优化获取B站登录ck
3
5
  * 添加截图列队,优化配置文件注释
4
6
 
@@ -24,6 +24,5 @@ const Account = ({ data }) => {
24
24
  data.appName === 'weibo' && renderLogo(Weibilogo, 'weibo-logo'),
25
25
  React.createElement(LogoText, { data: data })))));
26
26
  };
27
- var Account$1 = Account;
28
27
 
29
- export { Account$1 as default };
28
+ export { Account as default };
@@ -118,6 +118,5 @@ const Content = ({ data }) => {
118
118
  picItems)));
119
119
  }
120
120
  };
121
- var Content$1 = Content;
122
121
 
123
- export { Content$1 as default };
122
+ export { Content as default };
@@ -34,6 +34,5 @@ const Footer = ({ data }) => {
34
34
  React.createElement("span", { className: "italic" }, yukiPluginVersion))),
35
35
  React.createElement("img", { src: data.urlImgData, alt: "\u4E8C\u7EF4\u7801", className: "qr-code" }))));
36
36
  };
37
- var Footer$1 = Footer;
38
37
 
39
- export { Footer$1 as default };
38
+ export { Footer as default };
@@ -13,6 +13,5 @@ const ForwardContent = ({ data }) => (React.createElement(React.Fragment, null,
13
13
  React.createElement("div", { className: "orig-container", id: "orig-container" },
14
14
  React.createElement(Account, { data: data }),
15
15
  React.createElement(Content, { data: data })))));
16
- var ForwardContent$1 = ForwardContent;
17
16
 
18
- export { ForwardContent$1 as default };
17
+ export { ForwardContent as default };
@@ -1,7 +1,7 @@
1
1
  import axios from 'axios';
2
2
  import lodash from 'lodash';
3
3
  import { BiliApi } from './bilibili.api.js';
4
- import { readSyncCookie, readSavedCookieItems, readSavedCookieOtherItems } from './bilibili.models.js';
4
+ import { readSyncCookie, cookieWithBiliTicket, readSavedCookieItems, readSavedCookieOtherItems } from './bilibili.models.js';
5
5
  import { getWbiSign } from './bilibili.wbi.js';
6
6
 
7
7
  class BiliGetWebData {
@@ -11,6 +11,7 @@ class BiliGetWebData {
11
11
  async getBiliDynamicListDataByUid(uid) {
12
12
  const url = BiliApi.BILIBIL_API.biliDynamicInfoList;
13
13
  let { cookie } = await readSyncCookie();
14
+ cookie = await cookieWithBiliTicket(cookie);
14
15
  const data = {
15
16
  offset: '',
16
17
  host_mid: uid,
@@ -40,6 +41,7 @@ class BiliGetWebData {
40
41
  async getBilibiUserInfoByUid(uid) {
41
42
  const url = BiliApi.BILIBIL_API.biliSpaceUserInfoWbi;
42
43
  let { cookie } = await readSyncCookie();
44
+ cookie = await cookieWithBiliTicket(cookie);
43
45
  const data = {
44
46
  mid: uid,
45
47
  jsonp: 'jsonp',
@@ -62,6 +64,7 @@ class BiliGetWebData {
62
64
  async searchBiliUserInfoByKeyword(keyword) {
63
65
  const url = BiliApi.BILIBIL_API.biliSearchUpWbi;
64
66
  let { cookie } = await readSyncCookie();
67
+ cookie = await cookieWithBiliTicket(cookie);
65
68
  const data = {
66
69
  keyword: keyword,
67
70
  page: 1,
@@ -55,3 +55,9 @@ export declare function postGateway(cookie: string): Promise<import("axios").Axi
55
55
  * @param {string} uuid
56
56
  */
57
57
  export declare function get_buvid_fp(cookie: string): Promise<string>;
58
+ /**
59
+ * 获取有效bili_ticket并添加到cookie
60
+ * @param {string} cookie
61
+ * @returns {Promise<{ cookie: string; }>} 返回包含最新有效的bili_ticket的cookie
62
+ */
63
+ export declare function cookieWithBiliTicket(cookie: string): Promise<string>;
@@ -10,6 +10,7 @@ import { Segment, Bot, Redis } from 'yunzai';
10
10
  import { renderPage } from '../../utils/image.js';
11
11
  import { _paths } from '../../utils/paths.js';
12
12
  import { BiliApi } from './bilibili.api.js';
13
+ import { getBiliTicket } from './bilibili.ticket.js';
13
14
 
14
15
  /**
15
16
  * *******************************************************************
@@ -362,11 +363,11 @@ async function getNewTempCk() {
362
363
  let newTempCk = `${uuid}${buvid3_buvid4}${b_lsid}`; //${buvid_fp}`;
363
364
  await saveTempCk(newTempCk);
364
365
  const result = await postGateway(newTempCk);
365
- const { code, data } = await result.data; // 解析校验结果
366
- if (code !== 0) {
367
- logger?.mark(`优纪插件:tempCK,Gateway校验失败:${JSON.stringify(data)}`);
366
+ const data = await result.data; // 解析校验结果
367
+ if (data?.code !== 0) {
368
+ logger?.error(`优纪插件:tempCK,Gateway校验失败:${JSON.stringify(data)}`);
368
369
  }
369
- else if (code === 0) {
370
+ else if (data?.code === 0) {
370
371
  logger?.mark(`优纪插件:tempCK,Gateway校验成功:${JSON.stringify(data)}`);
371
372
  }
372
373
  }
@@ -526,19 +527,20 @@ async function getPayload(cookie) {
526
527
  * @returns 返回POST请求的结果
527
528
  */
528
529
  async function postGateway(cookie) {
529
- const payload = getPayload(cookie);
530
+ const data = { payload: await getPayload(cookie) };
530
531
  const requestUrl = 'https://api.bilibili.com/x/internal/gaia-gateway/ExClimbWuzhi';
531
- const headers = lodash.merge({}, BiliApi.BILIBILI_HEADERS, {
532
- 'Cookie': cookie,
533
- 'Content-type': 'Application/json',
534
- 'Charset': 'UTF-8',
535
- }, {
536
- 'Host': 'api.bilibili.com',
537
- 'Origin': 'https://www.bilibili.com',
538
- 'Referer': 'https://www.bilibili.com/',
539
- });
532
+ const config = {
533
+ headers: lodash.merge({}, BiliApi.BILIBILI_HEADERS, {
534
+ 'Cookie': cookie,
535
+ 'Content-type': 'application/json;charset=UTF-8',
536
+ }, {
537
+ 'Host': 'api.bilibili.com',
538
+ 'Origin': 'https://www.bilibili.com',
539
+ 'Referer': 'https://www.bilibili.com/',
540
+ })
541
+ };
540
542
  try {
541
- const res = await axios.post(requestUrl, { payload }, { headers });
543
+ const res = await axios.post(requestUrl, data, config);
542
544
  return res;
543
545
  }
544
546
  catch (error) {
@@ -546,5 +548,30 @@ async function postGateway(cookie) {
546
548
  throw error;
547
549
  }
548
550
  }
551
+ /**
552
+ * 获取有效bili_ticket并添加到cookie
553
+ * @param {string} cookie
554
+ * @returns {Promise<{ cookie: string; }>} 返回包含最新有效的bili_ticket的cookie
555
+ */
556
+ async function cookieWithBiliTicket(cookie) {
557
+ const BiliJctKey = "Yz:yuki:bili:bili_ticket";
558
+ cookie = await readSavedCookieItems(cookie, ['bili_ticket'], true);
559
+ const biliTicket = await Redis.get(BiliJctKey);
560
+ if (!biliTicket) {
561
+ try {
562
+ const csrf = await readSavedCookieItems(cookie, ['bili_jct'], false);
563
+ const { ticket, ttl } = await getBiliTicket(csrf);
564
+ await Redis.set(BiliJctKey, ticket, { EX: ttl });
565
+ return cookie + `;bili_ticket=${ticket};`;
566
+ }
567
+ catch (error) {
568
+ logger?.error(`${error}`);
569
+ return cookie;
570
+ }
571
+ }
572
+ else {
573
+ return cookie + `;bili_ticket=${biliTicket};`;
574
+ }
575
+ }
549
576
 
550
- export { applyLoginQRCode, checkBiliLogin, exitBiliLogin, genUUID, gen_b_lsid, getNewTempCk, pollLoginQRCode, postGateway, readSavedCookieItems, readSavedCookieOtherItems, readSyncCookie, readTempCk, saveLocalBiliCk, saveLoginCookie, saveTempCk };
577
+ export { applyLoginQRCode, checkBiliLogin, cookieWithBiliTicket, exitBiliLogin, genUUID, gen_b_lsid, getNewTempCk, pollLoginQRCode, postGateway, readSavedCookieItems, readSavedCookieOtherItems, readSyncCookie, readTempCk, saveLocalBiliCk, saveLoginCookie, saveTempCk };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Get Bilibili web ticket
3
+ * @param {string | null} csrf CSRF token, can be empty or null, or the cookie's bili_jct value
4
+ * @returns {Promise<{ code: number, ticket: string, created_at: number, ttl: number }>}
5
+ * Promise that resolves to an object containing code, ticket, created_at, and ttl values
6
+ */
7
+ export declare function getBiliTicket(csrf: string | null): Promise<{
8
+ code?: number;
9
+ ticket?: string;
10
+ created_at?: number;
11
+ ttl?: number;
12
+ }>;
@@ -0,0 +1,59 @@
1
+ import { createHmac } from 'crypto';
2
+ import { BiliApi } from './bilibili.api.js';
3
+
4
+ /**
5
+ * Generate HMAC-SHA256 signature
6
+ * @param {string} key The key string to use for the HMAC-SHA256 hash
7
+ * @param {string} message The message string to hash
8
+ * @returns {string} The HMAC-SHA256 signature as a hex string
9
+ */
10
+ function hmacSha256(key, message) {
11
+ return createHmac('sha256', key).update(message).digest('hex');
12
+ }
13
+ /**
14
+ * Get Bilibili web ticket
15
+ * @param {string | null} csrf CSRF token, can be empty or null, or the cookie's bili_jct value
16
+ * @returns {Promise<{ code: number, ticket: string, created_at: number, ttl: number }>}
17
+ * Promise that resolves to an object containing code, ticket, created_at, and ttl values
18
+ */
19
+ async function getBiliTicket(csrf) {
20
+ const ts = Math.floor(Date.now() / 1000);
21
+ const hexSign = hmacSha256('XgwSnGZ1p', `ts${ts}`);
22
+ const url = 'https://api.bilibili.com/bapis/bilibili.api.ticket.v1.Ticket/GenWebTicket';
23
+ const params = new URLSearchParams({
24
+ key_id: 'ec02',
25
+ hexsign: hexSign,
26
+ 'context[ts]': String(ts),
27
+ csrf: csrf ?? '' // 使用空字符串代替null
28
+ });
29
+ try {
30
+ const response = await fetch(`${url}?${params}`, {
31
+ method: 'POST',
32
+ headers: {
33
+ 'User-Agent': BiliApi.BILIBILI_HEADERS['User-Agent']
34
+ }
35
+ });
36
+ if (!response.ok) {
37
+ throw new Error(`get bili_jct HTTP error! status: ${response.status}`);
38
+ }
39
+ const data = await response.json();
40
+ if (data.code !== 0) {
41
+ if (data.code === 400) {
42
+ throw new Error(`get bili_jct Parameter error! ${data.message}`);
43
+ }
44
+ throw new Error(`Failed to retrieve bili ticket: ${data.message}`);
45
+ }
46
+ // 返回所需的对象结构
47
+ return {
48
+ code: data.code,
49
+ ticket: data.data?.ticket,
50
+ created_at: data.data?.created_at,
51
+ ttl: data.data?.ttl
52
+ };
53
+ }
54
+ catch (error) {
55
+ throw new Error(`Failed to fetch Bilibili ticket: ${error instanceof Error ? error.message : String(error)}`);
56
+ }
57
+ }
58
+
59
+ export { getBiliTicket };
@@ -9,14 +9,15 @@ class Image extends Picture {
9
9
  yukiPuppeteerRender;
10
10
  /**
11
11
  * 构造函数,整合截图方法
12
+ * @param launchOptions Puppeteer 启动选项,可选。父类有默认的设置。
12
13
  */
13
14
  constructor() {
14
15
  // 继承父类实例
15
16
  super();
16
- // 组件渲染对象
17
- this.Com;
18
- // 启动 Puppeteer
19
- this.Pup.start();
17
+ // 父类已经实例化组件渲染对象
18
+ //this.Com;
19
+ // 父类已经实例化启动 Puppeteer
20
+ //this.Pup.start();
20
21
  // 初始化 YukiPuppeteerRender 实例
21
22
  this.yukiPuppeteerRender = new YukiPuppeteerRender(this.Pup);
22
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yz-yuki-plugin",
3
- "version": "2.0.4-2",
3
+ "version": "2.0.4-4",
4
4
  "description": "优纪插件,yunzaijs 关于 微博推送、B站推送 等功能的拓展插件",
5
5
  "author": "snowtafir",
6
6
  "type": "module",
@@ -21,7 +21,7 @@
21
21
  "check-format": "git diff --exit-code"
22
22
  },
23
23
  "dependencies": {
24
- "axios": "^1.7.3",
24
+ "axios": "^1.7.7",
25
25
  "chalk": "^5.3.0",
26
26
  "chokidar": "^3.6.0",
27
27
  "debug": "^4.3.6",
@@ -30,7 +30,7 @@
30
30
  "md5": "^2.3.0",
31
31
  "moment": "^2.30.1",
32
32
  "node-fetch": "^3.3.2",
33
- "puppeteer": "^23.3.0",
33
+ "puppeteer": "^23.3.1",
34
34
  "qrcode": "^1.5.4",
35
35
  "react": "^18.3.1",
36
36
  "react-dom": "^18.3.1",
@@ -63,7 +63,7 @@
63
63
  "@types/react": "^18.3.3",
64
64
  "@types/react-dom": "^18.3.0",
65
65
  "@types/yaml": "1.9.7",
66
- "axios": "^1.7.3",
66
+ "axios": "^1.7.7",
67
67
  "babel-plugin-module-resolver": "^5.0.2",
68
68
  "chokidar": "^3.6.0",
69
69
  "jsdom": "^24.1.1",
@@ -72,7 +72,7 @@
72
72
  "node-fetch": "^3.3.2",
73
73
  "nodemon": "^3.1.4",
74
74
  "prettier": "^3.3.3",
75
- "puppeteer": "^23.3.0",
75
+ "puppeteer": "^23.3.1",
76
76
  "qrcode": "^1.5.4",
77
77
  "react": "^18.3.1",
78
78
  "react-dom": "^18.3.1",
@@ -87,7 +87,7 @@
87
87
  "ts-node": "^10.9.2",
88
88
  "tsx": "^4.19.0",
89
89
  "typescript": "^5.5.4",
90
- "yunzai": "^1.1.4",
90
+ "yunzai": "^1.1.6",
91
91
  "yunzai-mys": "^1.0.6"
92
92
  },
93
93
  "files": [
package/public/output.css CHANGED
@@ -1 +1 @@
1
- /*! tailwindcss v3.4.11 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.m-auto{margin:auto}.mb-3{margin-bottom:.75rem}.ml-7{margin-left:1.75rem}.mt-3{margin-top:.75rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-72{height:18rem}.max-h-96{max-height:24rem}.w-32{width:8rem}.w-72{width:18rem}.w-96{width:24rem}.p-1{padding:.25rem}.p-5{padding:1.25rem}.text-center{text-align:center}.text-lg{font-size:1.125rem;line-height:1.75rem}.italic{font-style:italic}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}
1
+ *,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.12 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.m-auto{margin:auto}.mb-3{margin-bottom:.75rem}.ml-7{margin-left:1.75rem}.mt-3{margin-top:.75rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-72{height:18rem}.max-h-96{max-height:24rem}.w-32{width:8rem}.w-72{width:18rem}.w-96{width:24rem}.p-1{padding:.25rem}.p-5{padding:1.25rem}.text-center{text-align:center}.text-lg{font-size:1.125rem;line-height:1.75rem}.italic{font-style:italic}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}