/*!
 * SaaS Experience Bridge — Ô bị khoá, đường đồ thị mờ, lưới an toàn của nguyên tắc #1
 *
 * Một phần của giao diện Experience. Thứ tự nạp do public/ui/manifest.php quy
 * định và là RÀNG BUỘC: nhiều quy tắc dựa vào việc file sau ghi đè file trước
 * ở cùng độ đặc hiệu. Đảo thứ tự là đổi giao diện.
 */

/* --------------------------------------------------------------------------
 * NGUYÊN TẮC UI-UX #1 — ổ khoá chỉ xuất hiện SAU dữ liệu thật
 *
 * Lưới an toàn cho nguyên tắc ở locksAllowed(). Chưa có kết quả thật thì không
 * một phần tử mang ổ khoá nào được lọt ra màn hình, kể cả khi chỗ dựng nó quên
 * tự kiểm. Thêm khối khoá mới về sau: đeo `data-spe-lock` là xong.
 * ----------------------------------------------------------------------- */

[data-spe-locks="0"] [data-spe-lock] {
	display: none !important;
}

/* --------------------------------------------------------------------------
 * Ô bị khoá — giá chưa đọc được, và các cột chỉ có trong app
 *
 * Khối mờ + ổ khoá, KHÔNG có số. Nó nói "có một giá trị ở đây" chứ không nói
 * giá trị đó là bao nhiêu; bịa một con số rồi làm mờ đi vẫn là bịa.
 * ----------------------------------------------------------------------- */

.spe-locked-cell {
	align-items: center;
	color: var(--spe-ink-2);
	cursor: default;
	display: inline-flex;
	justify-content: center;
	position: relative;
}

/*
 * Khối mờ — vệt mờ được VẼ SẴN, không phải một vệt sắc nét rồi cho `filter`
 * làm mờ.
 *
 * Đây là chỗ đắt nhất của cả giao diện nếu làm sai. Mỗi dòng nơi bán mang 3–5
 * ô khoá, một bảng 40 dòng là hơn 150 khối; mỗi `filter: blur()` là một bề mặt
 * vẽ riêng mà trình duyệt phải dựng lại mỗi khi nó trôi vào khung nhìn. Cộng
 * lại đúng bằng việc cuộn giật từng nấc.
 *
 * Một `repeating-linear-gradient` với điểm dừng MỀM (không có đoạn phẳng) cho
 * ra đúng hình mà blur tạo ra từ vệt sọc cũ — sóng chuyển màu đều — nhưng nó
 * là một lần tô nền, không phải một bộ lọc. Hình giữ nguyên, chi phí về gần 0.
 */
.spe-locked-cell__mask {
	background: repeating-linear-gradient(
		90deg,
		var(--spe-surface-3) 0,
		color-mix(in oklab, var(--spe-surface-3) 45%, var(--spe-surface)) 5px,
		var(--spe-surface-3) 9px,
		color-mix(in oklab, var(--spe-surface-3) 55%, var(--spe-surface)) 13px,
		var(--spe-surface-3) 17px
	);
	border-radius: 5px;
	display: block;
	height: 1.05rem;
	opacity: 0.9;
	width: 72px;
	/*
	 * Mép nhoè: blur cũ tràn ra ngoài khung nên khối không có viền rõ, và đó là
	 * thứ khiến nó đọc ra "một giá trị bị che" chứ không phải "một viên thuốc
	 * xám". `mask-image` dựng lại đúng chỗ mờ đó — vẫn là tô, không phải lọc.
	 */
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
	mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}

/*
 * Ổ khoá đè lên giữa khối mờ.
 *
 * Trên khối mờ xám thì nét đậm sẵn đã đủ tương phản, không cần quầng. Chỗ nào
 * nền có MÀU (ô biểu đồ, vòng tròn tồn kho) thì lockIcon(true) vẽ thêm một lượt
 * viền màu nền ôm theo hình ổ khoá — xem .spe-lock__halo, và xem lockIcon() để
 * biết vì sao không còn `drop-shadow()` nào ở đây.
 */
.spe-locked-cell .spe-lock {
	height: 18px;
	inset-block-start: 50%;
	inset-inline-start: 50%;
	position: absolute;
	transform: translate(-50%, -50%);
	width: 18px;
}

/*
 * Lượt viền của ổ khoá: cùng hình, nét dày hơn, màu nền, nằm dưới nét thật.
 *
 * `paint-order` không giúp được gì ở đây (nó không đổi màu), nên hai lượt là
 * hai nhóm path thật — xem lockIcon().
 */
.spe-lock__halo {
	fill: none;
	stroke: var(--spe-surface);
	stroke-opacity: 0.92;
}

/* Lỗ khoá là hình tô đặc: lượt viền của nó phải tô, không chỉ kẻ. */
circle.spe-lock__halo {
	fill: var(--spe-surface);
	fill-opacity: 0.92;
}

/* --------------------------------------------------------------------------
 * Đường đồ thị mờ sau ổ khoá
 *
 * Hình dạng sinh từ URL của dòng, không phải từ dữ liệu — xem sparkMask().
 * Làm mờ vừa đủ để còn đọc được hướng và màu, không đọc được giá trị nào.
 * ----------------------------------------------------------------------- */

.spe-locked-cell--chart {
	inline-size: 72px;
}

/*
 * Cũng không có `filter` ở đây: đường đồ thị được vẽ MỀM sẵn.
 *
 * sparkMask() phát ra hai nét chồng nhau — một nét dày mờ làm quầng, một nét
 * mảnh đậm làm lõi. Mắt đọc ra đúng thứ như trước (hướng đi và màu, không đọc
 * được giá trị nào), còn trình duyệt thì chỉ tô hai đường, không dựng thêm bề
 * mặt lọc nào.
 */
.spe-spark {
	block-size: 24px;
	display: block;
	inline-size: 72px;
	opacity: 0.9;
	/*
	 * Hai đầu nhoè dần. Blur cũ làm nét cụt ở mép ô tan ra; không có nó thì
	 * đường kết thúc bằng một đầu cắt gọn, và một đồ thị gọn gàng thì đọc ra là
	 * DỮ LIỆU THẬT — đúng thứ hình này không được phép nói (xem "Không bịa số").
	 */
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
	mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
}

.spe-spark--up {
	color: var(--spe-good);
}

.spe-spark--down {
	color: var(--spe-critical);
}

.spe-spark--flat {
	color: var(--spe-accent-strong);
}

.spe-locked-cell--up {
	color: var(--spe-good);
}

.spe-locked-cell--down {
	color: var(--spe-critical);
}

.spe-locked-cell--flat {
	color: var(--spe-accent-strong);
}

/*
 * Ba nét chồng nhau thay cho một lần blur: dày-mờ, vừa, rồi lõi.
 *
 * Ba bậc chứ không phải hai là có lý do — hai bậc cho ra một dải màu có mép,
 * đọc ra thành "một cái băng", không phải "một đường bị làm mờ". Bậc giữa lấp
 * đúng khoảng chuyển đó.
 */
.spe-spark__line,
.spe-spark__mid,
.spe-spark__halo {
	fill: none;
	stroke: currentColor;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/*
 * KHÔNG nét nào chạy ở độ đục đầy.
 *
 * Một nét đặc là một đường đồ thị SẮC NÉT, và sắc nét thì đọc ra là dữ liệu
 * thật — trong khi hình này sinh từ hash của URL. Bậc đục cao nhất dừng ở 0.42:
 * đủ để thấy hướng đi và màu, không đủ để tin đó là một phép đo.
 */
.spe-spark__line {
	opacity: 0.42;
	stroke-width: 2.4;
}

.spe-spark__mid {
	opacity: 0.24;
	stroke-width: 4.2;
}

.spe-spark__halo {
	opacity: 0.16;
	stroke-width: 6.5;
}

.spe-spark__area {
	fill: currentColor;
	opacity: 0.16;
	stroke: none;
}

.spe-locked-cell--lg .spe-locked-cell__mask {
	block-size: 1.55rem;
	inline-size: 104px;
}

.spe-locked-cell--lg .spe-lock {
	height: 22px;
	width: 22px;
}

.spe-locked-cell--sm .spe-locked-cell__mask {
	height: 0.85rem;
	width: 30px;
}

.spe-locked-cell--sm .spe-lock {
	height: 12px;
	width: 12px;
}

a.spe-offer:hover .spe-locked-cell {
	color: var(--spe-accent-strong);
}

/* Một nơi bán duy nhất: giá là câu trả lời, nên cho nó cỡ chữ của một con số chính. */
.spe-offers:has(> .spe-offer:only-child) .spe-offer__price {
	font-size: 1.5rem;
	letter-spacing: -0.02em;
}

/*
 * Khổ hẹp: bảng gãy thành thẻ. Hàng tiêu đề biến mất, mỗi ô tự mang nhãn của
 * cột mình — một ổ khoá không nhãn thì không ai biết nó khoá cái gì.
 */
@container (max-width: 620px) {
	.spe-offers__head {
		display: none;
	}

	/*
	 * Hết ngang để xếp cột: tên nơi bán chiếm trọn một dòng, các ô số xuống
	 * dòng dưới thành những viên nhãn-giá-trị và tự xuống hàng khi hết chỗ.
	 * Ép chúng vào lưới hai cột sẽ bóp tên sản phẩm còn vài chữ.
	 */
	/*
	 * Lưới hai cột, không phải flex-wrap: hai viên nhãn một hàng là điều kiện
	 * cứng ở đây, mà flex-wrap thì quyết định xuống dòng theo kích thước nội
	 * dung — chỉ cần một nhãn cột dài là cả hàng vỡ thành mỗi ô một dòng.
	 */
	.spe-offer {
		display: grid;
		gap: 0.5rem;
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.spe-offer__seller {
		grid-column: 1 / -1;
	}

	/* Số ô lẻ: ô cuối trải hết hàng thay vì để trống một nửa. */
	.spe-offer__cell:last-child:nth-child(odd) {
		grid-column: 1 / -1;
	}

	.spe-offer__aside,
	.spe-offer__cell {
		flex: 1 1 auto;
	}

	/*
	 * Hai viên nhãn một hàng. Mỗi cái một dòng thì một dòng nơi bán cao gần nửa
	 * màn hình, và danh sách giá — thứ người dùng tới đây để xem — bị đẩy đi mất.
	 */
	.spe-offer__aside,
	.spe-offer__cell {
		background: var(--spe-surface-2);
		border-radius: var(--spe-radius-pill);
		justify-content: space-between;
		min-inline-size: 0;
		padding: 0.3rem 0.7rem;
	}

	.spe-offer__cell-label {
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	/*
	 * Ô giá bỏ nhãn KHI ĐÃ CÓ SỐ: con số không co được, nên nhãn lãnh hết phần
	 * bị bóp và cụt thành "Pr…". Một con số đậm ngay dưới tên nơi bán thì không
	 * ai đọc nhầm ra thứ khác. Còn khi ô đang chờ hoặc bị khoá thì nhãn phải ở
	 * lại — một ổ khoá không tên giữa mấy ổ khoá khác thì không nói được gì.
	 */
	.spe-offer__aside:has( .spe-offer__price ) .spe-offer__cell-label {
		display: none;
	}

	.spe-offer__aside:has( .spe-offer__price ) {
		justify-content: center;
	}

	/*
	 * Khối mờ và đường đồ thị co lại để hai viên nhãn thật sự vừa một hàng —
	 * không co thì chúng nở quá nửa dòng và mỗi cái lại rơi xuống một dòng riêng,
	 * đúng thứ vừa tìm cách tránh.
	 */
	.spe-offer__cell .spe-locked-cell,
	.spe-offer__aside .spe-locked-cell {
		flex: none;
	}

	.spe-offer__cell .spe-locked-cell__mask,
	.spe-offer__aside .spe-locked-cell__mask {
		inline-size: 44px;
	}

	.spe-offer__cell .spe-locked-cell--chart,
	.spe-offer__cell .spe-spark {
		inline-size: 48px;
	}

	.spe-offer__pos-wait,
	.spe-offer__price-wait {
		width: 44px;
	}

	.spe-offer__cell-label {
		display: block;
	}

	.spe-offer__open {
		opacity: 1;
	}
}

