/* ==========================================================================
   次元小窝 · 上传图片弹窗（upload.css）
   ==========================================================================
   谁在用：七个页面都引这一份（index / top / wall / char / notice / mail / my）。
   弹窗的 HTML **不写在页面里**，由 js/upload.js 动态注入到 <body> 末尾 ——
   否则要在七个文件里各维护同样一坨结构，迟早抄歪（侧栏那个教训）。

   沿用 §0 设计令牌，不新增颜色 / 圆角 / 间距数值。
   层级：本弹窗 100，压在移动端侧栏(50) 之上。
   目录：1 遮罩  2 面板  3 主体  4 错误条  5 底部  6 成功视图  7 卡片高亮
        8 作品网格  9 响应式  10 果冻交互
   ========================================================================== */


/* ---------- 1. 遮罩 ---------- */
.up-mask{
	position: fixed;
	inset: 0;
	z-index: 100;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--sp-4);
	background-color: rgba(43, 58, 69, 0.42);
	animation: up-fade var(--dur) var(--ease);
}
/* ⚠️ 用 [hidden] 而不是 display:none 类 —— hidden 是原生属性，JS 只管切它 */
.up-mask[hidden]{ display: none; }

@keyframes up-fade{
	from{ opacity: 0; }
	to  { opacity: 1; }
}


/* ---------- 2. 面板 ---------- */
.up-panel{
	width: 100%;
	max-width: 560px;
	max-height: calc(100vh - var(--sp-4) * 2);
	display: flex;
	flex-direction: column;
	overflow: hidden;                     /* 圆角靠它裁住里面滚动的 body */
	border-radius: var(--r-lg);
	/* ⚠️ 实底。弹窗上挂 backdrop-filter 尤其亏：面板里还有一块会滚动的 .up-body，
	   每滚一帧都要把背后的整页重新模糊一次。遮罩本身已经压暗了背景，不需要再糊。 */
	background-color: var(--surface);
	border: 1px solid var(--line-blue);
	box-shadow: var(--sh-3);
	animation: up-pop var(--dur) var(--ease);
}
/* ⚠️ 这里故意**不写** animation-fill-mode（both/forwards）：
   写了的话终帧的 transform 会一直压着，以后想在这块上加 hover 位移就永远不生效。
   不写 fill-mode，动画放完自然回到 CSS 里的值（none / 0），视觉一样。 */
@keyframes up-pop{
	from{ opacity: 0; transform: translateY(12px) scale(0.985); }
	to  { opacity: 1; transform: translateY(0) scale(1); }
}

/* ---------- 2.1 头部 ---------- */
.up-head{
	display: flex;
	align-items: center;
	gap: var(--sp-3);
	padding: var(--sp-4) var(--sp-5);
	border-bottom: 1px solid var(--line-soft);
}
.up-head-text{ min-width: 0; }
.up-eyebrow{
	margin: 0 0 2px;
	font-family: var(--font-display);
	font-size: 10px;
	font-weight: 500;
	letter-spacing: 0.18em;
	color: var(--ink-500);
}
.up-title{
	margin: 0;
	font-size: 15px;
	font-weight: 600;
	letter-spacing: 0.06em;
	color: var(--ink-900);
}

/* 关闭按钮：圆形描边小按钮，靠 margin-left:auto 顶到最右 */
.up-close{
	flex-shrink: 0;
	margin-left: auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	padding: 0;
	border-radius: 50%;
	border: 1px solid var(--blue-200);
	background-color: var(--surface);
	color: var(--ink-500);
	cursor: pointer;
	transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
		background-color var(--dur-fast) var(--ease);
}
.up-close svg{ width: 14px; height: 14px; }
.up-close:hover{ border-color: var(--blue-400); background-color: var(--blue-50); color: var(--ink-900); }


/* ---------- 3. 主体 ---------- */
/* ⚠️ 这里原来是 display:grid。grid 的 auto 行在容器高度不够时会被**压到 min-content**，
   而图片在算 min-content 时高度被当成 0 → 装图的那一行只剩 min-height 兜着，
   324px 的预览图就从上下各溢出 78px，把提示条和标题框全盖住（踩过这个坑）。
   换成 flex 列：行为直接、可预测。 */
.up-body{
	display: flex;
	flex-direction: column;
	gap: var(--sp-4);
	padding: var(--sp-5);
	overflow-y: auto;                     /* 内容超长时只滚主体，头和脚留着 */
}
/* ★ 钉死不许收缩。flex 子项默认 flex-shrink:1，弹窗高度不够时会被压扁，
   .up-drop 里的预览图就会溢出去盖内容。高度不够就让 .up-body 自己滚。 */
.up-body > *{ flex-shrink: 0; }
.up-body[hidden]{ display: none; }


/* ---------- 3.1 选图区（点击 / 拖拽 / 粘贴） ---------- */
.up-drop{
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 6px;
	min-height: 168px;
	padding: var(--sp-4);
	border: 1.5px dashed var(--blue-300);
	border-radius: var(--r-md);
	background-color: var(--blue-50);
	color: var(--ink-500);
	text-align: center;
	cursor: pointer;
	transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.up-drop:hover,
.up-drop.is-over{
	border-color: var(--blue-600);
	background-color: var(--blue-100);
}
.up-drop.is-over{ border-style: solid; }

.up-drop svg{ width: 26px; height: 26px; color: var(--blue-600); }
.up-drop-main{ margin: 0; font-size: 13px; color: var(--ink-700); }
.up-drop-hint{ margin: 0; font-size: 11px; color: var(--ink-400); }

/* 真正的 file input：藏起来，由 JS 触发；别用 display:none 以外的方式，
   opacity:0 的绝对定位元素在部分浏览器里点了没反应 */
.up-file{ display: none; }


/* ---------- 3.2 选好图之后的预览 ---------- */
.up-preview{ display: none; }
/* 选好图之后容器里只放预览。justify-content 必须改成 flex-start ——
   万一图片还是比容器高，也从顶部开始排，绝不能靠 center 往上顶出去盖住提示条 */
.up-drop.has-file{
	padding: 0;
	border-style: solid;
	border-color: var(--line-blue);
	background-color: var(--paper);
	justify-content: flex-start;
}
.up-drop.has-file .up-drop-hints{ display: none; }
.up-drop.has-file .up-preview{
	display: block;
	width: 100%;
	padding: var(--sp-3);
	text-align: center;                    /* 小图比容器窄时居中，否则会贴左边 */
}

/* 图片的「框」：宽度跟着图片走，尺寸标签才能正好压在图片右下角 */
.up-preview-frame{
	position: relative;
	display: inline-block;
	vertical-align: bottom;
	max-width: 100%;
}

/* ⚠️ 这个 img 反复调了四轮，每轮都踩到不同的坑，别再乱改：
   ① width:100% + max-height:240 + contain + 浅蓝底
      → 图片被塞进扁框，左右露出一圈浅蓝；
   ② max-width:100% + max-height:240
      → 不露蓝了，但 240 太小，4096×2560 只画到 384px 宽，左右照样空着；
   ③ width:100%（无条件撑满）
      → 横图铺满了，可竖图会被 max-height 压扁、左右又留白。
   现在：max-height 提到 420 —— 大到横图根本碰不到它，
   于是横图按宽度铺满、竖图按高度收窄，两种都不留白。 */
.up-preview-frame img{
	display: block;
	max-width: 100%;
	max-height: 420px;
	width: auto;
	height: auto;
	border-radius: var(--r-sm);
	background-color: var(--paper);
	box-shadow: var(--sh-2);   /* 预览图也浮起来，和发布后在页面上看到的分量一致 */
}

/* 尺寸 · 体积：半透明小胶囊，压在图片右下角 */
.up-preview-meta{
	position: absolute;
	right: 6px;
	bottom: 6px;
	display: flex;
	align-items: center;
	max-width: calc(100% - 12px);
	padding: 2px 8px;
	border-radius: var(--r-full);
	background-color: rgba(15, 23, 42, .62);   /* 深底浅字：浅图深图上都读得清 */
	font-size: 11px;
	line-height: 1.5;
	color: #fff;
}
.up-preview-name{
	min-width: 0;                          /* ⚠️ 想让它出省略号，min-width:0 是必须的 */
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
}

/* 纯文字小按钮（重选 / 移除） */
/* 「重选 / 移除」：原来是一行带下划线的裸文字，跟旁边「取消 / 发布」两个实体按钮
   摆一起显得没做完。改成小胶囊，图标 + 文字，「移除」用警示色跟「重选」拉开。 */
.up-link{
	display: inline-flex;
	align-items: center;
	gap: 5px;
	height: 30px;
	padding: 0 11px;
	border: 1px solid var(--line-blue);
	border-radius: var(--r-full);
	background-color: var(--surface);
	font-family: inherit;
	font-size: 12px;
	color: var(--blue-700);
	cursor: pointer;
	transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease),
		color var(--dur-fast) var(--ease);
}
.up-link svg{ width: 13px; height: 13px; }
/* ⚠️ 原来是 var(--blue-800)，但四份 :root 里最深只到 --blue-700 ——
   这条 hover 颜色一直在静默失效（CSS 变量取不到值，整条声明被丢掉）。
   要更深就得先在 :root 加档位，别在这里凭空写。 */
.up-link:hover{ border-color: var(--blue-400); background-color: var(--blue-50); color: var(--blue-700); }
/* 「移除」是破坏性操作，单独用警示色 */
.up-link--danger{ color: var(--alert); border-color: var(--alert-line); }
.up-link--danger:hover{ border-color: var(--alert); background-color: var(--alert-soft); color: var(--alert); }


/* ---------- 3.3 表单行 ---------- */
.up-row{ display: flex; flex-direction: column; gap: 6px; }
.up-row-label{
	display: flex;
	align-items: baseline;
	gap: 6px;
	font-size: 12px;
	font-weight: 500;
	letter-spacing: 0.08em;
	color: var(--ink-500);
}
/* 必填星号。⚠️ 用 --alert，全站唯一允许的第二个色相 */
.up-req{ color: var(--alert); }
.up-row-hint{ font-size: 11px; font-weight: 400; letter-spacing: 0; color: var(--ink-400); }

.up-input{
	width: 100%;
	height: 38px;
	padding: 0 12px;
	border: 1px solid var(--blue-200);
	border-radius: var(--r-sm);
	background-color: var(--blue-50);
	font-family: inherit;                  /* ⚠️ 表单控件不继承字体，不写会变宋体 */
	font-size: 13px;
	letter-spacing: inherit;
	color: var(--ink-900);
	transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease),
		box-shadow var(--dur-fast) var(--ease);
}
.up-input::placeholder{ color: var(--ink-400); }
.up-input:focus{
	outline: none;
	border-color: var(--blue-600);
	background-color: var(--surface);
	box-shadow: 0 0 0 3px rgba(29, 107, 166, 0.22);
}
/* 校验不通过的行描红 */
.up-input.is-bad{ border-color: var(--alert); background-color: var(--alert-soft); }


/* ---------- 3.4 分区 / AI 标记：藏起来的 radio + label，零 JS 画选中态 ---------- */
/* ⚠️ 这跟 §6 筛选栏是同一套路子。父容器要 position:relative，
   否则 position:absolute 的 input 会去找更外层的定位祖先，位置乱飘 */
.up-cats{ position: relative; display: flex; flex-wrap: wrap; gap: var(--sp-2); }

.up-cat-input{
	position: absolute;
	width: 1px;
	height: 1px;
	opacity: 0;
	pointer-events: none;
}
/* 分区选择器。⚠️ 和筛选栏的 .fl-chip 是**同一套交互**（藏起来的 input + label 画选中态），
   所以形状也得是一套：都收成全胶囊。原来这里 10px 圆角、筛选栏 10px 圆角，
   现在两边一起变胶囊，不会出现"弹窗里是方的、关掉弹窗筛选栏是圆的"。 */
.up-cat{
	display: inline-flex;
	align-items: center;
	height: 34px;
	padding: 0 16px;                          /* 胶囊两端吃掉一点宽度，比方角版多给 2px */
	border: 1px solid var(--blue-200);
	border-radius: var(--r-full);
	background-color: var(--blue-50);
	font-size: 13px;
	color: var(--ink-700);
	cursor: pointer;
	user-select: none;
	transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease),
		color var(--dur-fast) var(--ease);
}
.up-cat:hover{ border-color: var(--blue-400); background-color: var(--blue-100); }
/* 选中态：描边加深 + 底色加深 + 加粗，不用实心主色（那是「上传作品」按钮的专属） */
.up-cat-input:checked + .up-cat{
	border-color: var(--blue-600);
	background-color: var(--blue-200);
	color: var(--ink-900);
	font-weight: 600;
}
/* ⚠️ input 自己 opacity:0 看不见焦点环，必须把焦点画到 label 上 */
.up-cat-input:focus-visible + .up-cat{ outline: 2px solid var(--blue-700); outline-offset: 2px; }
/* 本页只收某个分区时，另一个选项禁用 */
.up-cat-input:disabled + .up-cat{
	opacity: 0.4;
	cursor: not-allowed;
	border-color: var(--line);
	background-color: var(--paper);
}


/* ---------- 4. 错误提示 ---------- */
.up-alert{
	display: flex;
	align-items: flex-start;
	gap: 8px;
	padding: 10px 14px;
	border: 1px solid var(--alert-line);
	border-radius: var(--r-sm);
	background-color: var(--alert-soft);
	font-size: 13px;
	line-height: 1.6;
	color: var(--alert);
}
.up-alert[hidden]{ display: none; }
.up-alert svg{ flex-shrink: 0; width: 15px; height: 15px; margin-top: 3px; }


/* ---------- 5. 底部按钮 ---------- */
.up-foot{
	display: flex;
	align-items: center;
	gap: var(--sp-3);
	padding: var(--sp-4) var(--sp-5);
	border-top: 1px solid var(--line-soft);
	background-color: var(--paper);
}
.up-foot[hidden]{ display: none; }
/* 左边那句"纯前端演示"说明，靠 margin-right:auto 把按钮顶到右边 */
.up-foot-note{
	margin-right: auto;
	font-size: 11px;
	line-height: 1.5;
	color: var(--ink-400);
}
/* 选了图之后，弹窗左下角出现「重选 / 移除」。
   ⚠️ [hidden] 必须显式写成 display:none —— 只写 display:flex 会把它盖掉，藏不住 */
.up-foot-file{
	margin-right: auto;
	display: flex;
	align-items: center;
	gap: var(--sp-3);
}
.up-foot-file[hidden]{ display: none; }

.up-btn{
	display: inline-flex;
	align-items: center;
	justify-content: center;
	height: 38px;
	padding: 0 20px;
	border-radius: var(--r-sm);
	font-family: inherit;
	font-size: 13px;
	font-weight: 500;
	letter-spacing: 0.06em;
	cursor: pointer;
	transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
		color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
/* ⚠️ 次要按钮的 1px 边框不能省，否则比主按钮矮 2px、窄 2px */
.up-btn--ghost{
	color: var(--ink-700);
	background-color: var(--surface);
	border: 1px solid var(--blue-200);
}
.up-btn--ghost:hover{ color: var(--ink-900); background-color: var(--blue-50); border-color: var(--blue-400); }

/* 发布 = 整个弹窗唯一的"该点的那个"，所以用最深的蓝配白字（5.67:1）。
   ⚠️ 上一版是浅蓝底 + 深墨字：和旁边"取消"长得几乎一样，得来回看才分得清。 */
.up-btn--primary{
	color: #ffffff;
	font-weight: 600;
	background-color: var(--blue-600);
	border: 1px solid transparent;
}
.up-btn--primary:hover{ background-color: var(--blue-700); transform: translateY(-1px); }
.up-btn--primary:active{ transform: translateY(0) scale(0.99); }
.up-btn:disabled{ opacity: 0.55; cursor: not-allowed; transform: none; }


/* ---------- 6. 发布成功 ---------- */
.up-done{
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--sp-3);
	padding: var(--sp-6) var(--sp-5);
	text-align: center;
}
.up-done[hidden]{ display: none; }

.up-done-icon{
	display: flex;
	align-items: center;
	justify-content: center;
	width: 54px;
	height: 54px;
	border-radius: 50%;
	color: var(--blue-700);
	background-color: var(--blue-100);
	border: 1px solid var(--blue-200);
}
.up-done-icon svg{ width: 26px; height: 26px; }
.up-done-title{ margin: 0; font-size: 16px; font-weight: 600; color: var(--ink-900); }
.up-done-text{ margin: 0; max-width: 34em; font-size: 13px; color: var(--ink-500); }
.up-done-text code{
	padding: 1px 5px;
	border-radius: var(--r-xs);
	background-color: var(--blue-50);
	font-size: 12px;
}


/* ---------- 7. 刚发布的那张卡片闪一下 ---------- */
/* ⚠️ 高亮加在 .img-show 上而不是 .item-show 上：
   .item-show 自己挂着入场动画 card-in，两个 animation 会互相顶掉 */
.item-show.is-new .img-show{
	animation: up-flash 1.4s var(--ease) 2;
}
@keyframes up-flash{
	0%, 100%{ box-shadow: 0 0 0 0 rgba(29, 107, 166, 0); }
	50%     { box-shadow: 0 0 0 4px rgba(29, 107, 166, 0.5); }
}


/* ---------- 8.「我的作品」的瀑布流容器（my.html 用） ---------- */
/* 卡片结构直接复用首页那套 .item-show / .img-show（样式在 css/waterFlow.css），
   位置由 js/waterFlow.js 现算 —— 所以这里只管两件事：
   ① 给卡片一个定位参照（卡片是 absolute，left/top 都相对容器算）
   ② 卡片下面那两行字（标题 / 分类·AI）的样式 */
.panel-images .wf-box{
	position: relative;      /* ⚠️ 少了它，absolute 的卡片会跑到整个页面的左上角 */
}
.wf-cap{
	margin: 8px 2px 0;
	font-size: 13px;
	color: var(--ink-900);
	overflow: hidden;        /* 标题太长就省略号，别把卡片撑破 */
	white-space: nowrap;
	text-overflow: ellipsis;
}
.wf-cap-sub{
	margin: 2px 2px 0;
	font-size: 11px;
	color: var(--ink-400);
}


/* ---------- 9. 响应式 ---------- */
@media (max-width: 760px){
	.up-mask{ padding: 0; align-items: flex-end; }
	.up-panel{
		max-width: none;
		max-height: 92vh;
		border-radius: var(--r-lg) var(--r-lg) 0 0;   /* 从底部升起的抽屉感 */
		border-bottom: 0;
	}
	.up-head{ padding: var(--sp-3) var(--sp-4); }
	.up-body{ padding: var(--sp-4); gap: var(--sp-3); }
	.up-foot{ padding: var(--sp-3) var(--sp-4); flex-wrap: wrap; }
	.up-foot-note{ flex: 1 1 100%; margin: 0 0 var(--sp-2); }
	.up-btn{ flex: 1; }
	.up-drop{ min-height: 140px; }
}


/* ================== 10. 果冻交互（按压回弹） ==================
   ⚠️ 两条规则必须成对：基础态用过冲曲线 --ease-q（松手才弹），
      :active 改用 --dur-press + --ease（按下跟手，不过冲）。
   ⚠️ 不给 .up-drop / .up-panel 加 scale —— 大块缩放像弹窗在抖。 */

.up-close, .up-link, .up-cat, .up-btn{
	transition:
		border-color var(--dur-fast) var(--ease),
		background-color var(--dur-fast) var(--ease),
		color var(--dur-fast) var(--ease),
		transform var(--dur) var(--ease-q);
}

.up-close:active, .up-link:active, .up-cat:active, .up-btn:active{
	transform: translateY(0) scale(0.94);
	transition:
		border-color var(--dur-fast) var(--ease),
		background-color var(--dur-fast) var(--ease),
		color var(--dur-fast) var(--ease),
		transform var(--dur-press) var(--ease);
}

/* 30×30 的圆形关闭键：面积小，缩狠一点才有"啪"的回弹 */
.up-close:active{ transform: translateY(0) scale(0.88); }

/* 分区 chip 一行三四个，0.94 会互相挤，收一点 */
.up-cat:active{ transform: translateY(0) scale(0.96); }


/* ============ 11. 无障碍：减弱动态效果 / 触摸目标 ============ */

/* ⚠️ 开了「减弱动态效果」时，弹窗的弹出/淡入/按压回弹全关。
   注意弹窗是"突然出现"的 —— 位移一关就只剩显隐切换，这是刻意的：
   对前庭失调用户来说，最安全的过渡就是没有过渡。 */
@media (prefers-reduced-motion: reduce){
	*, *::before, *::after{
		animation-duration: 1ms !important;
		animation-delay: 0ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 1ms !important;
		transition-delay: 0ms !important;
	}
	.up-close:active, .up-link:active, .up-cat:active, .up-btn:active{ transform: none !important; }
}

/* 触摸设备：30×30 的关闭键撑到 44px 热区，视觉尺寸不变 */
@media (pointer: coarse){
	.up-close{ position: relative; }
	.up-close::after{
		content: "";
		position: absolute;
		top: 50%;
		left: 50%;
		width: max(100%, 44px);
		height: max(100%, 44px);
		transform: translate(-50%, -50%);
	}
}
