:root{
  box-sizing:border-box;
  padding-top:env(safe-area-inset-top,0px);
  padding-bottom:env(safe-area-inset-bottom,0px);
  --paper:#FBF8F1; --ink:#1F2A33; --muted:#5C6872; --line:#E3DCCB;
  --pine:#1F5F52; --pine-soft:#E4EFEA; --gold:#C9A227; --gold-soft:#F6EDCF;
  --card:#FFFFFF; --user:#1F5F52; --bot:#F1EFE7;
  /* Màu của phòng chat, tách riêng khỏi --user/--bot: từ 018 người quản nhóm đổi được màu của
     nhóm mình, mà bong bóng ở mục Hỏi thì không đổi theo (bên đó là .msg, vẫn --user/--bot).
     Mặc định trỏ thẳng về bộ màu app, nên nhóm chưa chọn màu trông y như trước, và chế độ tối
     không phải khai lại một lần nữa ở đây. */
  --c-mine:var(--user); --c-them:var(--bot); --c-accent:var(--pine); --c-accent-soft:var(--pine-soft);
  --sans:"Be Vietnam Pro",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --serif:"Noto Serif",Georgia,"Times New Roman",serif;
  --fs:17px;
  /* Bảy màu của bảy thứ (thẻ sự kiện), CN → T7. Đậm vừa đủ để thấy trên nền giấy. */
  --wd0:#D9483B; --wd1:#7A8794; --wd2:#2F6FD6; --wd3:#14967F; --wd4:#8B5CF6; --wd5:#D69A0E; --wd6:#D6457F;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --paper:#14191D; --ink:#EEE9DC; --muted:#A6AEB5; --line:#2C343A;
    --pine:#7FC3AE; --pine-soft:#1B2B27; --gold:#E0BE55; --gold-soft:#2E2914;
    --card:#1C2328; --bot:#232B31;
    --wd0:#F07A6E; --wd1:#9AA6B2; --wd2:#6FA0F0; --wd3:#4CC3AA; --wd4:#B095FA; --wd5:#E8BA4C; --wd6:#EE7BA8;
  }
  :root:not([data-theme="light"]) .ev-mi.evening{color:#9AA6E8}
  :root:not([data-theme="light"]) .ev-mi.online{color:#6FA0F0}
}
:root[data-theme="dark"]{
  --paper:#14191D; --ink:#EEE9DC; --muted:#A6AEB5; --line:#2C343A;
  --pine:#7FC3AE; --pine-soft:#1B2B27; --gold:#E0BE55; --gold-soft:#2E2914;
  --card:#1C2328; --bot:#232B31;
  --wd0:#F07A6E; --wd1:#9AA6B2; --wd2:#6FA0F0; --wd3:#4CC3AA; --wd4:#B095FA; --wd5:#E8BA4C; --wd6:#EE7BA8;
}
:root[data-theme="dark"] .ev-mi.evening{color:#9AA6E8}
:root[data-theme="dark"] .ev-mi.online{color:#6FA0F0}
/* KHÔNG đặt height:100% cho html và body. Trang này dài hơn màn hình ở gần như mọi thẻ, nên
   height:100% chỉ bó cái hộp lại còn đúng một màn hình trong khi nội dung tràn ra ngoài nó —
   và WebKit trên iPhone tính chỗ đứng của những thanh position:fixed từ chính cái hộp bị bó
   ấy. Hậu quả thấy được: thanh thẻ ở đáy trôi lên giữa màn hình khi cuộn, nằm đè lên bài
   đang đọc. Bỏ nó ra thì html cao đúng bằng nội dung, và bottom:0 lại là đáy màn hình.
   Chiều cao tối thiểu một màn hình — thứ duy nhất height:100% còn làm ở đây — chuyển sang
   .app, xem ở dưới. */
html{scroll-padding-top:env(safe-area-inset-top,0px)}
body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--sans);font-size:var(--fs);line-height:1.5;-webkit-font-smoothing:antialiased}
body.big{--fs:21px}
*{box-sizing:border-box}
/* Thuộc tính `hidden` của HTML chỉ là `[hidden]{display:none}` trong bảng kiểu của trình duyệt,
   mà bất kỳ quy tắc nào của ta có `display` đều đè lên nó. Thanh ghi âm và thanh "đang trả lời"
   cùng khai `display:flex`, nên `el.hidden = true` trong JS không giấu được gì — hai thanh đó
   hiện suốt từ lúc dựng phần chat, kể cả khi chưa ai bấm micro.
   Đặt ở đây một lần cho cả app, có `!important`, để lần sau viết `display` cho một khối lúc ẩn
   lúc hiện thì không phải nhớ ra cái bẫy này nữa. */
[hidden]{display:none !important}
a{color:var(--pine)}
button{font:inherit;color:inherit;cursor:pointer}

/* min-height thay cho height:100% đã bỏ ở html/body: main{flex:1} vẫn căng hết màn hình trên
   những thẻ ngắn, mà không còn bó hộp cuộn lại. Trừ đi hai lề an toàn vì :root đã đệm sẵn
   chúng — không trừ thì trang nào cũng thừa ra 80px và cuộn được một đoạn trống.
   svh chứ không dvh: dvh đổi theo lúc thanh địa chỉ lên xuống, và mỗi lần đổi là một lần
   dựng lại trang giữa lúc người ta đang cuộn. Dòng vh ở trên là đường lui cho máy cũ. */
.app{max-width:720px;margin:0 auto;display:flex;flex-direction:column;
  min-height:calc(100vh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px));
  min-height:calc(100svh - env(safe-area-inset-top,0px) - env(safe-area-inset-bottom,0px))}
/* Lề dọc .5rem chứ không .7rem: đầu trang dính trên màn hình suốt, nên mỗi pixel của nó bị trừ
   khỏi MỌI trang, ở mọi lần cuộn. Chiều cao hàng vẫn do cái cao nhất trong hàng quyết định —
   tên app hai dòng, hay ô hình 2.2rem — nên bóp lề lấy về chừng 7px mà không cái nút nào nhỏ
   đi và cái tên không sát mép: còn .5rem trên dưới, vẫn thở được.
   Con số này không phải ghi lại ở đâu khác: app.js đo đầu trang thật rồi ghi vào --head-h. */
header{position:sticky;top:env(safe-area-inset-top,0px);z-index:5;background:var(--paper);border-bottom:1px solid var(--line);padding:.5rem 1rem;display:flex;flex-wrap:wrap;align-items:center;gap:.6rem}
/* nowrap: ở 360px, cạnh cụm VI/EN + chữ lớn + tài khoản chỉ còn 170px cho cái tên, mà
   "VGC Walk With Us" rộng 164px — sát đến mức chỉ cần một chữ nữa là "Us" rớt xuống dòng
   riêng, nhìn như tên app bị gãy đôi. nowrap khiến cả cụm nút tụt xuống dòng dưới (ở cỡ chữ
   lớn thì đúng là vậy) chứ không bao giờ cắt ngang cái tên. Cỡ chữ ở @media dưới 420px. */
/* Cái tên chỉ rộng bằng chữ của nó, không giãn ra chiếm hết chỗ trống nữa: nếu còn flex:1 thì
   nó đẩy quả địa cầu văng sang tận cụm nút bên phải, tức là đứng sau ba cái nút chứ không còn
   đứng sau cái tên. Chỗ trống để .tools tự đẩy mình về bên phải bằng margin-left:auto như cũ. */
/* Cỡ tên app đo bằng rem, KHÔNG bằng em. Nút chữ lớn đổi --fs của <body>, tức là đổi cỡ chữ của
   những thứ người ta ĐỌC. Mọi cái nút quanh cái tên này đã đo bằng rem nên chúng đứng yên khi
   bấm chữ lớn; riêng cái tên thì em, nên nó một mình phình ra giữa một hàng không phình — và
   đẩy cả cụm nút bên phải xuống hàng dưới. Cái tên hội thánh không phải thứ đọc lại mỗi sáng,
   nó là chỗ để nhận ra mình đang ở đâu; nó thuộc về hàng nút chứ không thuộc về bài đọc.
   1.22rem ≈ 1.15em × 17px, tức là đúng cỡ cũ ở chữ thường — chỉ khác ở chỗ giờ nó không còn
   chạy theo nút chữ lớn nữa. Ai chỉnh cỡ chữ mặc định của trình duyệt thì rem vẫn đi theo. */
.brand{font-family:var(--serif);font-weight:600;font-size:1.22rem;letter-spacing:.01em;line-height:1.2;white-space:nowrap;flex:none}
/* Dòng phụ nằm hẳn dưới tên app, không nối đuôi cùng hàng: trên màn hình 360px thì nối đuôi
   sẽ đẩy nút VI/EN xuống hàng dưới, làm đầu trang cao gấp đôi mà chẳng được gì. */
.brand small{display:block;font-family:var(--sans);font-weight:400;font-style:italic;font-size:.6em;color:var(--muted);letter-spacing:0;margin-top:.15em}
/* Nút tài khoản chỉ là cái hình tròn, không nền không viền của riêng nó: vòng tròn bên trong
   đã là hình dạng rồi, thêm một khung nữa thì thành nút lồng trong nút. */
.acctbtn{border:0;background:none;padding:0;display:inline-flex;align-items:center;flex:none}
/* VI/EN, nút chữ lớn và nút tài khoản đi chung một cụm: ở cỡ chữ lớn, cái tên dài hơn nên cụm
   này phải xuống dòng — để rời nhau thì chỉ vài nút rớt xuống, đứng trơ một mình. */
.tools{display:flex;align-items:center;gap:.6rem;margin-left:auto}
/* Quả địa cầu dẫn ra trang hội thánh. Ô chạm 2.2rem bằng các nút đầu trang, nhưng lề âm kéo lại
   phần đệm dư ra hai bên để nó ngồi sát cái tên — đọc thành "Đồng Hành ⟶ hội thánh" chứ không
   thành một nút rời. Màu thông như mọi liên kết khác trong app. Không viền không nền: xem ghi
   chú ở #weblink trong index.html.
   Nó từng đi thành cặp với nút chia sẻ; nút đó đã xuống thẻ Kết nối để nhường chỗ đầu trang cho
   cái chuông; giờ nó đứng đầu một cụm ba — nhà thờ, Facebook hội thánh, Instagram giới trẻ — cùng
   một kiểu, cùng một chỗ. */
.weblink{flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:2.2rem;height:2.2rem;color:var(--pine);border-radius:999px;
  border:0;background:none;padding:0}
/* Lề âm hai bên để ba cái liên kết ngồi sát cái tên và sát nhau; chỉ riêng bên trái quả địa cầu
   kéo mạnh hơn một chút, vì bên đó là khoảng cách tới chữ "Đồng Hành". */
#weblink,#fblink,#iglink{margin:-.4rem -.2rem}
#weblink{margin-left:-.35rem}
.weblink svg{width:1.35rem;height:1.35rem;fill:none;stroke:currentColor;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
@media (hover:hover){.weblink:hover{background:var(--card)}}
.pill{border:1px solid var(--line);background:var(--card);border-radius:999px;padding:.3rem .7rem;font-size:.9rem;display:inline-flex;gap:.4rem;align-items:center}
/* Cả ô là một cái <button>, hai chữ bên trong chỉ là <span> — xem ghi chú ở #langbtn.
   <button> mang mặt chữ của hệ điều hành chứ không thừa kế của trang: không kéo về inherit thì
   VI/EN đổi mặt chữ và đổi cả bề ngang so với bản hai nút, tức là hàng nút đầu trang đo lại từ
   đầu. Cỡ chữ thì .pill ở trên đã đặt rồi nên không nhắc lại ở đây. */
.langbtn{font-family:inherit;font-weight:inherit;line-height:inherit;color:inherit;flex:none}
.pill span{padding:.05rem .35rem;border-radius:999px;color:var(--muted)}
.pill span.on{background:var(--pine);color:#fff}
.iconbtn{border:1px solid var(--line);background:var(--card);border-radius:999px;width:2.2rem;height:2.2rem;font-size:1em;font-weight:700}
.iconbtn[aria-pressed="true"]{background:var(--gold-soft);border-color:var(--gold)}
/* Nút cỡ chữ mang hình vẽ, mấy nút iconbtn khác mang chữ (‹ ⚙ ← →) — nên chỉ nút này cần căn
   giữa theo kiểu hình. Gạch dọc của dấu cộng tắt khi chữ đã lớn: còn lại dấu trừ, tức là bấm
   nữa thì nhỏ lại. */
#bigbtn{display:inline-flex;align-items:center;justify-content:center;flex:none}
/* Lịch cũng mang hình chứ không mang chữ. Thiếu dòng này thì <svg> đứng trên dòng chữ như một
   chữ cái, chừa khoảng chân chữ ở dưới, nên cuốn lịch bị đẩy lệch lên trong vòng tròn. */
#calbtn{display:inline-flex;align-items:center;justify-content:center;flex:none;padding:0}
#calbtn svg{width:1.25rem;height:1.25rem}
#bigbtn svg{width:1.25rem;height:1.25rem;fill:none;stroke:currentColor;stroke-width:2.1;
  stroke-linecap:round;stroke-linejoin:round}
#bigbtn[aria-pressed="true"] .up{display:none}
/* Nút tài khoản. Cùng cỡ với nút chữ lớn nên hai cái đứng cạnh nhau thành một cặp, nhưng
   khác hẳn về chất: cái kia là công tắc, cái này là một chỗ để đi tới.
   Chưa đăng nhập thì hình người viền xám, im lặng. Đăng nhập rồi thì nền xanh, hai chữ cái
   đầu tên — nhìn một cái là biết mình đang là ai, ở bất cứ thẻ nào. */
.avatar{flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:2.2rem;height:2.2rem;border-radius:999px;border:1px solid var(--line);background:var(--card);
  font-size:.9rem;font-weight:700;letter-spacing:.02em;line-height:1}
.avatar svg{width:1.3rem;height:1.3rem;fill:none;stroke:var(--muted);stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round}
.avatar.in{background:var(--pine);border-color:var(--pine);color:#fff}
/* Chưa đăng nhập: hình người nền vàng nhạt, viền vàng, và một vòng sóng tỏa ra mời bấm.
   Sóng chạy từng đợt (ba nhịp, nghỉ, lặp lại) chứ không đập liên tục — vẫn là cùng lý do với cái
   chuông: thứ gì nhúc nhích không ngừng trên đầu trang thì mắt học cách bỏ qua. */
.acctbtn.invite .avatar{position:relative;background:var(--gold-soft);border:2px solid var(--gold)}
.acctbtn.invite .avatar svg{stroke:var(--gold)}
.acctbtn.invite .avatar::after{content:"";position:absolute;inset:-2px;border-radius:inherit;
  border:2px solid var(--gold);animation:acct-invite 6s ease-out infinite;pointer-events:none}
@keyframes acct-invite{
  0%{transform:scale(1);opacity:.9}  12%{transform:scale(1.55);opacity:0}
  13%{transform:scale(1);opacity:.9} 25%{transform:scale(1.55);opacity:0}
  26%{transform:scale(1);opacity:.9} 38%,100%{transform:scale(1.55);opacity:0}
}
@media (prefers-reduced-motion:reduce){.acctbtn.invite .avatar::after{animation:none;opacity:0}}
/* ===== Hình đại diện =====
   Một tấm hình, sáu chỗ đứng: góc đầu trang, hộp thư, cạnh bong bóng trong nhóm, đầu phòng nhắn
   riêng, danh sách người, bảng Quản trị. Mọi ô đi qua cùng một hàm avaHtml() trong app.js, nên ở
   đây chỉ còn phải nói hai điều: ô hình che được cái gì, và ô ấy to bằng nào ở từng chỗ.

   HAI CHỮ CÁI LUÔN ĐƯỢC VẼ TRƯỚC và nằm dưới; tấm hình là một <img> phủ kín lên trên, đắp vào khi
   link ký tên về tới. Nên không ô nào có một nhịp trống nhấp nháy, người chưa có hình thì trông y
   như app cũ, và lúc link xin không được thì ô tự trở về hai chữ cái chứ không thành ô vỡ. */
.pic{position:relative;overflow:hidden}
/* inset:0 phủ tới mép TRONG của viền, nên chỗ nào có viền (nút tài khoản trên đầu trang) thì viền
   vẫn còn lại thành một vòng mảnh quanh tấm hình. */
.pic > img{position:absolute;inset:0;display:block;width:100%;height:100%;object-fit:cover;border-radius:inherit}

/* Ô hình đứng cạnh một cái tên trong danh sách: người trong nhóm, ô tìm người để nhắn riêng hay
   để thêm vào nhóm, và bảng Quản trị. */
.prow-ava{flex:none;width:2.1rem;height:2.1rem;border-radius:999px;display:grid;place-items:center;
  background:var(--c-mine);color:#fff;font:600 .75em/1 var(--sans);letter-spacing:.02em;user-select:none}
/* Bảng Quản trị: một hàng ở đó là <summary> xếp DỌC (tên trên, email và số điện thoại dưới), nên
   ô hình phải được đặt hẳn vào cột một và mọi thứ còn lại dồn sang cột hai — để nguyên thì cái
   tên bị ô hình đẩy xuống hàng dưới. */
.ruser > summary{grid-template-columns:auto 1fr;column-gap:.6rem;align-items:center}
/* 1/-1 chứ không phải "span 2": dòng phụ giờ là hai dòng riêng (email, rồi số điện thoại), nên
   số dòng thay đổi theo từng người — ai chưa điền số thì còn hai dòng, ai điền đủ thì ba. Ghim
   cứng hai dòng thì ô hình lệch lên trên ở những hàng dài hơn. */
.ruser > summary > .prow-ava{grid-column:1;grid-row:1/-1;align-self:center}
.ruser > summary > *:not(.prow-ava){grid-column:2}

/* Trang Tài khoản: ô to, vì đây là chỗ duy nhất người ta cần THẤY RÕ tấm hình mình vừa chọn —
   mọi chỗ khác trong app ô hình chỉ bằng đầu ngón tay. */
.avaedit{display:flex;align-items:center;gap:1rem;margin:.6rem 0 .2rem}
.avaacts{display:flex;flex-wrap:wrap;gap:.5rem}
.ava-big{flex:none;width:4.5rem;height:4.5rem;border-radius:999px;display:grid;place-items:center;
  background:var(--c-mine);color:#fff;font:600 1.3em/1 var(--sans);user-select:none}

/* Ô hình trên form lập nhóm: vuông bo góc, vì nó là một nhóm — cùng hình dáng mà hàng trong hộp
   thư sẽ mang sau khi lập xong, nên cái người ta thấy lúc chọn đúng là cái sẽ hiện ra. */
.ava-mid{flex:none;width:3rem;height:3rem;border-radius:12px;display:grid;place-items:center;
  background:var(--c-mine);color:#fff;font:600 .95em/1 var(--sans);user-select:none}
/* .sq: cùng một ô, nhưng của một NHÓM. Tròn là một người, vuông bo góc là một nhóm — một thứ
   tiếng duy nhất cho cả hộp thư, đầu phòng và trang cài đặt. */
.ava-big.sq{border-radius:18px}
.room-ava.sq{border-radius:9px}
.prow-ava.sq{border-radius:8px}

/* Dấu "một người / nhiều người" (ICON_DM, ICON_GROUP trong app.js), đứng cạnh tên nhóm trong hộp
   thư và trên hai nút lọc. height + width:auto chứ không ghim cả hai: hai cái dấu có khung hình
   khác nhau (một người thì vuông, hai người thì ngang), ghim cứng là bóp méo cái thứ hai. */
.kind{flex:none;height:.8em;width:auto;fill:currentColor;color:var(--c-accent);opacity:.8}
.chip .kind{margin-right:.35rem;color:inherit;opacity:.65;vertical-align:-.02em}

/* Mặt người đang quản nhóm, nằm trong dòng phụ ngay dưới tên nhóm. Nhỏ hơn hẳn ô hình chính vì
   nó trả lời một câu khác: ô lớn nói "nhóm nào", mấy cái mặt này nói "hỏi ai". Xếp chồng mép
   như mọi app: ba cái mặt chiếm chỗ của chừng hai. Vòng sáng quanh mỗi cái là nền của chính
   cái thẻ, nên chúng tách nhau ra mà không cần thêm một màu viền nào. */
.modfaces{display:inline-flex;flex:none;margin-right:.4rem}
.modface{flex:none;width:1.2rem;height:1.2rem;border-radius:999px;display:grid;place-items:center;
  background:var(--c-accent);color:#fff;font:600 .5rem/1 var(--sans);user-select:none;
  box-shadow:0 0 0 1.5px var(--card)}
.modfaces .modface + .modface{margin-left:-.35rem}

/* Cạnh bong bóng, chỉ trong nhóm. .gap là ô RỖNG giữ chỗ cho những câu tiếp theo của cùng một
   người: không có nó thì câu thứ hai trở đi thụt ra sát lề và cả cột bong bóng so le.
   Đứng theo đáy vì .line xếp theo đáy — ô hình ngang câu CUỐI của một lượt nói, đúng chỗ mà mọi
   app nhắn tin để nó, nên không phải dạy ai cả. */
.bava{flex:none;width:1.7rem;height:1.7rem;border-radius:999px;display:grid;place-items:center;
  background:var(--c-accent);color:#fff;font:600 .62em/1 var(--sans);user-select:none;align-self:flex-end}
.bava.gap{background:none}

/* Đầu phòng nhắn riêng. Trong phòng ấy bong bóng KHÔNG mang ô hình (chỉ có hai người), nên đây là
   chỗ duy nhất trả lời "mình đang nói với ai" — và là chỗ người ta nhìn trước khi gõ câu đầu. */
.room-ava{flex:none;width:2rem;height:2rem;border-radius:999px;display:grid;place-items:center;
  background:var(--c-mine);color:#fff;font:600 .72em/1 var(--sans);user-select:none}

/* Đang mở trang Tài khoản: một vòng vàng ra ngoài. Không nhuộm nền vàng như nút chữ lớn, vì
   hai chữ cái trắng nằm trên nền vàng nhạt thì đọc không ra. */
.acctbtn[aria-pressed="true"] .avatar{box-shadow:0 0 0 2px var(--gold)}
/* Chấm vàng trên icôn đầu trang: cùng thứ tiếng với chấm trên thanh thẻ (nav.tabs .newdot), vì
   nó nói cùng một điều — "trong này có gì đó mới". Nhô hẳn ra ngoài mép nút và mang một vòng
   màu nền, để nó đọc được cả khi nút đang sáng (icôn lịch lúc đang mở trang Lịch có nền vàng
   nhạt, mà một chấm vàng trên nền vàng thì coi như không có chấm). */
/* Chuông thông báo. Cùng ô 2.2rem với các nút đầu trang khác, nhưng KHÔNG dùng aria-pressed như
   nút lịch và nút tài khoản: ở hai nút đó, "pressed" nghĩa là "trang này đang mở", và app tô nền
   vàng nhạt để nói điều đó. Chuông nói một chuyện khác hẳn — máy này có đang nhận thông báo —
   nên nếu mượn cùng một nền vàng thì lúc đứng ở trang Lịch sẽ có hai nút vàng cạnh nhau mang hai
   nghĩa khác nhau. Trạng thái của chuông nằm ở data-on, và nói ra bằng chính hình cái chuông.
   Gạch chéo chỉ hiện khi đang tắt; nét "cut" bên dưới tô màu nền nút, xẻ một rãnh để gạch chéo
   không dính vào nét chuông. */
.bellbtn{display:inline-flex;align-items:center;justify-content:center;color:var(--ink)}
.bellbtn svg{width:1.35rem;height:1.35rem}
.bellbtn .slash{display:none}
.bellbtn[data-on="0"]{color:var(--muted)}
.bellbtn[data-on="0"] .slash{display:inline}
.bellbtn .slash.cut{stroke:var(--card);stroke-width:3.8}
/* Chuông đang TẮT thì rung một đợt ngắn để mời bật lên; đang bật thì đứng yên.
   Từng đợt, KHÔNG rung liên tục. Một cái chuông lắc không ngừng nằm trên đầu mọi trang là cái
   gai trong mắt suốt lúc người ta đọc bài sáng, và với người đã cố ý tắt thì nó thành cằn nhằn
   — mà cằn nhằn thì người ta học cách không nhìn tới nó nữa, tức là mất luôn cả cái chấm vàng.
   Ba cái lắc rồi thôi nói đủ ý: chỗ này có thứ đang tắt.
   Xoay quanh cái quai chuông (transform-origin ở đỉnh) chứ không quanh tâm hình: chuông thật
   treo ở quai, lắc quanh tâm thì thành một cái đĩa xoay và mắt đọc ra ngay là sai.
   Biên độ nhỏ dần chứ không đều: một cái chuông vừa bị gõ thì tắt dần, đó là cách vật thật
   chuyển động, và nó cũng khiến đợt rung tự kết thúc chứ không cắt ngang. */
@keyframes bell-nudge{
  0%,100%{transform:rotate(0)}
  8%{transform:rotate(-15deg)}   22%{transform:rotate(13deg)}
  36%{transform:rotate(-9deg)}   50%{transform:rotate(6deg)}
  64%{transform:rotate(-4deg)}   78%{transform:rotate(2deg)}
}
.bellbtn.nudge svg{animation:bell-nudge 1.1s ease-in-out;transform-origin:50% 18%}
/* Ai đã dặn máy bớt chuyển động thì bớt thật — không phải giảm, mà là bỏ. Chấm vàng vẫn nằm đó
   và nó mới là thứ mang tin; cái rung chỉ là để mắt tìm thấy cái chấm nhanh hơn. */
@media (prefers-reduced-motion:reduce){.bellbtn.nudge svg{animation:none}}
.iconbtn.newdot{position:relative}
.iconbtn.newdot::after{content:"";position:absolute;top:-.15rem;right:-.15rem;width:.55rem;height:.55rem;
  border-radius:50%;background:var(--gold);box-shadow:0 0 0 2px var(--paper)}
/* ĐẦU TRANG CHẬT — ngưỡng 540px, rộng hơn ngưỡng bóp lề nội dung ngay dưới, và cố ý vậy.
   iPhone 14/15 Pro Max rộng 430px: thừa chỗ cho lề bài đọc như máy lớn, nhưng KHÔNG đủ chỗ cho
   một hàng nút vừa nhận thêm cái chuông. Gộp hai chuyện vào một ngưỡng thì phải chọn: hoặc
   430px rớt hàng, hoặc bài đọc trên 430px bị bóp lề vô cớ. Hai chuyện khác nhau, hai ngưỡng.
   Ngưỡng này là chỗ ĐẦU TRANG bắt đầu chật, nên nó phải đo lại mỗi lần đầu trang nhận thêm một
   thứ. Nó từng là 480px; đo trong Chrome với cỡ chữ đầy đủ thì hàng nút chỉ nằm gọn một dòng từ
   532px trở lên khi có ba cửa ra ngoài (trước đó là 492px với hai cửa). Để 480 thì cả dải
   481–531px — cửa sổ trình duyệt hẹp, máy gập mở ra, chia đôi màn hình iPad — xuống hai hàng với
   cỡ chữ to, đúng chỗ màn hình đã rộng ra. Kéo lên 540px thì dải ấy dùng cỡ gọn ở dưới đây và về
   lại một dòng. (Bản trước cũng đã để lọt 481–491px xuống hai hàng vì lẽ này; giờ thì hết.) */
@media (max-width:540px){
  /* Hàng nút đầu trang ở đây được đo bằng pixel, không bằng mắt: cả hàng phải nằm gọn một dòng
     trên những máy hội thánh thật sự cầm trong tay. iPhone 12–16 rộng 390–393, iPhone SE và
     mấy máy cũ rộng 375, Android phổ thông 384–412. Mỗi lần thêm một cửa ra ngoài là thêm một ô
     vào một hàng vốn đã chật, nên chỗ đó phải lấy lại từ chính hàng này: khe hở hẹp lại, và ba
     cái liên kết ra ngoài thu ô chạm xuống 1.6rem.
     Đo thật trong Chrome, đầu trang ở trạng thái chật nhất (chuông đã hiện, hình tròn tài khoản
     đã có hai chữ) — ngưỡng một dòng, theo từng bước đã đi:

       374px  hai cửa ra ngoài, bản trước khi có Instagram
       395px  thêm Instagram, chưa sửa gì
       383px  khe hở hẹp lại + ba liên kết thu ô chạm xuống 1.6rem
       374px  + VI|EN bỏ khe giữa hai chữ và bóp đệm (xem .pill ở dưới)
       369px  + bốn nút viền xuống 2.1rem (xem .iconbtn ở dưới)

     Nên 375px — iPhone SE, iPhone 8, mấy máy Android cũ — về lại một dòng, và còn dôi 6px chứ
     không sát mép: mặt chữ trên máy thật không trùng từng pixel với Chrome ở đây, nên một cái
     ngưỡng đúng sít sao là một cái ngưỡng sẽ hỏng ở đâu đó.
     360px vẫn hai hàng. Nó đã hai hàng từ trước khi có Instagram, và kéo nốt 9px cuối cùng ấy
     thì phải cắt một thứ trong cụm .tools — chưa đáng. */
  header{gap:.2rem}
  /* Ba cái liên kết thu lại một chút cùng với cái tên, và ngồi sát nhau: lề âm ăn hết khe hở
     giữa các ô, nên mắt đọc ra MỘT cụm "đây là hội thánh ở ngoài kia", chứ không phải ba cái nút
     rời nhau. Ô chạm 1.6rem nhỏ hơn 2.2rem của mấy nút kia, và đó là đánh đổi có chủ ý: mấy thứ
     này là liên kết dẫn ra ngoài, bấm hụt thì không mất gì và bấm lại được ngay; còn một đầu
     trang hai hàng thì ngày nào cũng ăn mất một khúc màn hình.
     Lề âm -.3rem là đáy, không phải một con số tròn cho đẹp: ở đây tâm hai ô cách nhau 19.2px
     trong khi cái hình rộng 16.8px, tức là còn đúng 2.4px hở giữa hai hình. Kéo sâu hơn nữa thì
     hình nọ liếm vào hình kia, và phần đè lên nhau thuộc về cái đứng sau — bấm đúng Facebook mà
     mở ra Instagram. */
  .weblink{width:1.6rem;height:1.6rem}
  #weblink,#fblink,#iglink{margin:-.4rem -.3rem}
  #weblink{margin-left:-.4rem}
  #fblink{margin-left:-.3rem}
  #iglink{margin-left:-.3rem}
  .weblink svg{width:1.05rem;height:1.05rem}
  /* Bốn nút viền xuống 2.1rem — 33.6px. Dòng ghi chú cũ ở đây từng nói "giữ nguyên 2.2rem", và
     nó đặt ra một cái ngưỡng đúng: bàn tay cần một mục tiêu HƠN BA MƯƠI PIXEL, vì hội thánh này
     nhiều người lớn tuổi và một cái nút bấm ba lần mới trúng thì tệ hơn một đầu trang hai hàng.
     33.6px vẫn ở trên ngưỡng ấy, và nó còn to hơn hình tròn tài khoản 2rem ngay cạnh — thứ đã
     đứng đó từ lâu mà không ai bấm trượt. Xuống 2rem thì lấy thêm được 5px nữa, nhưng lúc ấy
     không còn gì đệm cho lần đo sai, nên dừng ở đây.

     padding:0 mới là chỗ mấu chốt, không phải con số 2.1rem. <button> mang sẵn đệm ngang của hệ
     điều hành (~6px mỗi bên) ăn vào lòng ô, mà cái hình bên trong thì co theo lòng ô: thu ô lại
     mà không bỏ đệm thì ô nhỏ đi 1px nhưng cái hình méo đi 3px, và chuông với lịch bị bóp hẹp
     lại thành hình trứng. Bỏ đệm rồi đặt cỡ hình thẳng tay thì ô nhỏ đi mà hình giữ nguyên.
     Đặt cỡ cho .iconbtn svg vì cái lịch trước giờ không có cỡ nào cả — nó chỉ vừa vặn nhờ đúng
     cái đệm mặc định vừa bỏ đi. Chuông nhỏ hơn một nấc vì hình nó có gạch chéo cắt ngang, ở cỡ
     lớn hơn thì nét gạch dính vào nét chuông. Dòng .bellbtn svg phải đứng SAU .iconbtn svg:
     hai cái cùng độ ưu tiên, cái sau thắng. */
  .iconbtn{width:2.1rem;height:2.1rem;padding:0}
  .iconbtn svg{width:1.25rem;height:1.25rem}
  .bellbtn svg{width:1.2rem;height:1.2rem}
  .tools{gap:.1rem}
  /* VI|EN tốn chỗ nhất trong cụm, và nó vẫn giữ CẢ HAI chữ: cặp hai chữ cho thấy ngay rằng có
     hai lựa chọn VÀ mình đang ở bên nào, còn một nút chỉ ghi "EN" thì phải bấm thử mới biết, mà
     bấm thử ở đây nghĩa là cả app đổi sang thứ tiếng mình không đọc được.
     Nên bóp phần TRỐNG, không bóp nghĩa: chữ giữ nguyên cỡ .85rem, chỉ bớt đệm quanh nó.
     gap:0 được là nhờ giờ đây cả ô chỉ còn MỘT nút (xem #langbtn trong index.html). Hồi hai chữ
     là hai nút rời, khe hở ấy là ranh giới giữa hai chỗ bấm — bỏ đi thì người ta bấm trúng mép
     của cái kia. Giờ chạm đâu trong ô cũng ra một việc, nên khe hở không còn mang nghĩa gì; hai
     chữ vẫn cách nhau .4rem đệm của chính chúng, đủ thoáng để đọc ra là hai chữ.
     Đo ở 375px: nút tiếng 62px → 52px, và ngưỡng rớt hàng của cả đầu trang 383px → 374px. */
  .pill{padding:.28rem .2rem;gap:0;font-size:.85rem}
  .pill span{padding:.05rem .2rem}
  /* Cái tên nhỏ đi một chút và hình tròn tài khoản nhỏ đi một chút. Không ai đọc ra hai thay
     đổi này khi nhìn đầu trang; ai cũng đọc ra một đầu trang chiếm hai hàng. Hình tròn vẫn trên
     ngưỡng chạm được vì nó là hình tròn đặc, không phải một icôn nét mảnh trong ô rỗng. */
  .brand{font-size:1.02rem}
  .avatar{width:2rem;height:2rem}
  .avatar svg{width:1.2rem;height:1.2rem}
}

/* Lề nội dung trên màn hình hẹp. Bóp thì bóp cả header, main và thanh soạn tin cùng lúc — lệch
   nhau vài pixel là chữ trong bài không còn thẳng hàng với tên app ngay trên nó. */
@media (max-width:420px){
  header{padding:.5rem .8rem}
  main{padding:1rem .8rem calc(var(--tabs-h,calc(3.6rem + env(safe-area-inset-bottom,0px))) - env(safe-area-inset-bottom,0px) + .8rem)}
  .composer{padding:.6rem .8rem}
}

/* "Quên mật khẩu?" là chữ, không phải nút: nó là lối thoát hiểm, đừng mời bấm ngang với
   Đăng nhập. Gạch chân để vẫn thấy rõ là bấm được. */
.forgot{margin:.6rem 0 0;text-align:center}
.forgot button{border:0;background:none;color:var(--pine);text-decoration:underline;
  font-size:inherit;padding:.2rem}

/* Đáy trang chừa chỗ cho thanh thẻ, và chừa ĐÚNG bằng thanh thẻ thật. 6rem trước đây là một
   con số đoán: thanh thẻ chỉ cao 3.6rem, nên mọi trang đều thừa ra một dải trống 39px dưới thẻ
   cuối cùng — cuộn tới đáy là thấy một mảng giấy trắng không có gì trong đó.
   Trừ đi lề an toàn dưới đáy máy vì :root đã đệm sẵn nó một lần rồi; --tabs-h thì có mang nó
   (nav.tabs tự padding). Không trừ thì app đã cài lại thừa ra đúng 34px mà trình duyệt không
   thừa, tức là chữa được một chỗ và làm hỏng chỗ kia.
   --tabs-h do app.js đo thanh thật rồi ghi vào — nên bật chữ lớn, thanh cao lên, chỗ chừa cũng
   cao theo. Đó là lý do phải đo chứ không viết cứng: rem tính theo cỡ chữ gốc, còn thanh thẻ
   cao theo cỡ chữ ở <body>. Còn .8rem là khoảng thở giữa thẻ cuối và thanh, không hơn. */
main{flex:1;padding:1rem 1rem calc(var(--tabs-h,calc(3.6rem + env(safe-area-inset-bottom,0px))) - env(safe-area-inset-bottom,0px) + .8rem)}
/* Thẻ Hỏi có thêm thanh soạn tin nằm đè lên đáy màn hình, trên cả hàng thẻ. Không chừa chỗ
   cho nó thì đuôi câu trả lời — chỗ có nút "muốn gặp mục sư" — nằm khuất sau thanh đó.
   --composer-h do app.js đo thanh thật rồi ghi vào, vì cỡ chữ lớn làm thanh cao lên. */
#v-ask.on{padding-bottom:var(--composer-h,4rem)}
.view{display:none}.view.on{display:block}
/* 1.45em, không phải 1.7em — và cùng một cỡ với .dev-h, nên tựa bài tĩnh nguyện và tựa bài
   giảng đọc ngang nhau. Ở 1.7em một cái tựa dài hai dòng chiếm gần 70px, mà trên điện thoại
   đó là khúc mở đầu của bài đọc bị đẩy xuống dưới màn hình. Còn ở những trang mà tựa chỉ
   nhắc lại tên cái thẻ đang sáng ở đáy màn hình ("Hỏi", "Lịch hội thánh", "Quản trị") thì cỡ
   to chẳng nói thêm gì cả. Vẫn là serif, vẫn là thứ lớn nhất trên trang.
   Lề trên bỏ hẳn: main đã có 1rem đệm trên, thêm .2rem nữa chỉ là một khe lẻ. */
h1{font-family:var(--serif);font-weight:600;font-size:1.45em;line-height:1.2;margin:0 0 .3rem}
h2{font-family:var(--serif);font-weight:600;font-size:1.25em;margin:1.4rem 0 .5rem}
p{margin:.4rem 0}
/* Dòng dẫn nhỏ hơn chữ trong bài (.9em): nó giải thích trang này làm gì, người ta đọc đúng một
   lần rồi không đọc lại nữa — trong khi nó đứng chắn trước nội dung ở mọi lần mở. Mấy đoạn dài
   ba dòng ở Hỏi và Quản trị nhờ vậy co lại còn hai. Vẫn màu nhạt, vẫn là câu trọn vẹn, chỉ là
   không còn to ngang thứ nó đang giới thiệu. */
.lead{color:var(--muted);font-size:.9em;margin:0 0 .7rem}
.verse{font-family:var(--serif);background:var(--gold-soft);border-left:4px solid var(--gold);padding:.9rem 1rem;border-radius:0 12px 12px 0;margin:.8rem 0}
.verse .ref{display:block;font-family:var(--sans);font-size:.85em;color:var(--muted);margin-top:.4rem}
/* Toàn bài giảng: lời mục sư, đọc dài — chữ serif, dòng thưa, đề mục và câu trích tự nổi lên. */
/* Nổi hơn một thẻ thường: tông xanh thông của app — khác màu vàng của câu gốc ngay phía trên —,
   viền đậm, chữ lớn, mũi tên mở/gập. Mở ra thì nền về màu thẻ cho dễ đọc bài dài. */
/* Gọn: một dòng mảnh viền 1px, vẫn tông xanh thông để nhận ra là bấm được, nhưng không còn là
   khối viền đậm có bóng chen giữa video và dãy chọn nhóm — thứ phần lớn người mở trang đến tìm. */
.fullsermon{background:var(--pine-soft);border:1px solid var(--pine);border-radius:12px;padding:0 .85rem;margin:.7rem 0}
.fullsermon[open]{background:var(--card)}
.fullsermon summary{cursor:pointer;font-weight:600;color:var(--pine);padding:.55rem 0;min-height:2.5rem;list-style:none;display:flex;align-items:center;gap:.45rem}
.fullsermon summary::-webkit-details-marker{display:none}
.fullsermon summary::after{content:"›";margin-left:auto;font-size:1.3em;line-height:1;transition:transform .2s}
.fullsermon[open] summary::after{transform:rotate(90deg)}
.fullsermon[open] summary{border-bottom:1px solid var(--line);margin-bottom:.4rem}
.fullsermon .actions{margin:.1rem 0 .3rem}
.fullsermon .actions .btn{padding:.35rem .75rem;font-size:.9em}
/* Cùng dáng với dòng "Đọc toàn bài giảng" ngay trên, để hai lối đi cạnh nhau đọc như một cặp. */
.livelink{display:flex;align-items:center;gap:.45rem;min-height:2.5rem;padding:.55rem .85rem;margin:.7rem 0;
  background:var(--pine-soft);border:1px solid var(--pine);border-radius:12px;color:var(--pine);font-weight:600;text-decoration:none}
.livelink::after{content:"↗";margin-left:auto}
#wk-full-body{font-family:var(--serif);line-height:1.65;padding-bottom:.8rem}
#wk-full-body p{margin:.55rem 0}
#wk-full-body h3{font-size:1.05em;margin:1.3rem 0 .4rem}
#wk-full-body blockquote{margin:.6rem 0;padding:.5rem .9rem;border-left:3px solid var(--gold);background:var(--gold-soft);border-radius:0 10px 10px 0;font-style:italic}

/* Đường quay về là một dòng chữ có gạch chân, không còn là cái nút viền cao 42px chiếm hẳn một
   hàng ngang trang. Nó nói một điều người ta chỉ cần TỚI KHI đọc xong, mà lại đứng trên đầu
   cái cần đọc. Vẫn mang tên chỗ sẽ về (xem renderBack trong app.js), và vẫn cao 2rem để ngón
   tay người lớn tuổi bấm được — chỉ khác là giờ nó nằm gọn cạnh dòng ngày tháng trong .ph.
   Gạch chân màu --line chứ không màu chữ: thấy là bấm được, mà không kêu to hơn cái tựa. */
.back{margin:0 0 .4rem;font-size:.9em}
/* Rỗng thì biến mất hẳn. wk-back và dev-back chỉ có chữ khi đang đọc bài cũ; để nó là một ô
   rỗng trong hàng flex thì cái gap .55rem vẫn tính, và dòng ngày tháng lệch sang phải nửa
   phân — đúng ở trang hay mở nhất, nơi không có đường quay về nào cả. */
.back:empty{display:none}
.backlink{border:0;background:none;padding:0;color:var(--pine);font:inherit;font-weight:500;
  text-decoration:underline;text-decoration-color:var(--line);text-underline-offset:3px;
  min-height:2rem;display:inline-flex;align-items:center;gap:.25rem}
@media (hover:hover){.backlink:hover{text-decoration-color:var(--pine)}}

/* Hàng đầu trang: đường quay về và dòng ngay sau nó — ngày tháng ở Bài giảng và Tĩnh nguyện,
   cái tên ở những trang mà tên ngắn (Tài khoản, cài đặt nhóm) — nằm trên CÙNG một hàng.
   Xếp chồng thì mở trang nào cũng mất hai hàng chỉ để nói mình đang ở đâu và về đâu, chừng
   70px trên điện thoại, mà đầu trang dính bên trên đã ăn 55px nữa. Đây đúng là cách hàng tên
   nhóm trong phòng chat đã gói từ trước (.room-head) — giờ cả app nói cùng một thứ tiếng.
   flex-wrap: tên nhóm hay tên trang có thể dài. Chật thì nó tụt xuống hàng dưới nguyên vẹn,
   chứ không bị bóp thành một cột chữ hẹp — thà cao thêm một hàng ở vài trường hợp còn hơn cụt
   ở mọi trường hợp. align-items:center vì hai thứ trong hàng khác cỡ nhau hẳn một bậc. */
.ph{display:flex;flex-wrap:wrap;align-items:center;gap:0 .55rem;margin:0 0 .5rem}
.ph > *{margin:0}
.ph > .lead:empty{display:none}
.ph:not(:has(> :not(:empty))){display:none}
/* Ảnh đứng đầu bài giảng thay chỗ khung video, nên mang đúng khuôn của khung ấy: cùng tỉ lệ
   16/9, cùng bo góc, cùng khoảng cách trên dưới. Dùng lại .art-cover của bài viết thì được một
   dải cao 13rem — đúng cho một bài viết, nhưng ở đây nó làm bài có video và bài không có video
   mở ra thành hai hình dáng khác nhau ở đúng khúc người ta nhìn trước nhất. */
.wk-cover{display:block;width:100%;aspect-ratio:16/9;object-fit:cover;border-radius:16px;margin:.8rem 0;background:#000}
.video{display:none}
.video.on{display:grid;gap:.45rem;justify-items:start;margin:.8rem 0}
.video iframe{width:100%;aspect-ratio:16/9;border:0;border-radius:16px;display:block;background:#000}
.video a{color:var(--muted);font-size:.85em}

.archive{display:grid;gap:.5rem}
.archive button{text-align:left;border:1px solid var(--line);background:var(--card);border-radius:14px;padding:.7rem .9rem;display:grid;gap:.15rem;font:inherit;color:inherit}
.archive button b{font-family:var(--serif);font-weight:600}
/* Hàng bài cũ có nhãn ngày tờ lịch nhỏ bên trái (smallBadge), tựa và dòng phụ bên phải. */
.archive button:not(.art-row):has(> .ev-badge){display:flex;align-items:center;gap:.75rem}
.archive .ar-t{display:grid;gap:.15rem;min-width:0}
.ev-badge.sm{width:2.9rem;padding:.15rem .1rem .2rem;border-radius:10px}
.ev-badge.sm .bd-wd{font-size:.64em}
.ev-badge.sm .bd-d{font-size:1.2em}
.ev-badge.sm .bd-m{font-size:.6em}
.archive button[aria-pressed="true"]{border-color:var(--pine);background:var(--pine-soft)}
/* Nền vàng nhạt cho dòng gộp tháng: giữa một cột thẻ trắng, nó là thứ duy nhất khác màu,
   nên mắt dừng lại và người đọc biết bên dưới còn bài của những tháng trước. */
.archive-month > summary{cursor:pointer;border:1px solid var(--gold);background:var(--gold-soft);border-radius:14px;padding:.7rem .9rem;font-weight:600}
.archive-month > summary .note{font-weight:400;margin-left:.3rem;color:var(--ink);opacity:.7}
.archive-month .archive{margin:.5rem 0 0 .6rem}
/* Lớp thứ hai của kho tĩnh nguyện: năm bọc ngoài tháng. Viền xanh chứ không vàng — hai dòng gộp
   lồng nhau mà cùng một màu thì không đọc ra được cái nào chứa cái nào, và xanh là màu đã dành
   sẵn cho thứ đứng trên trong app này. */
.archive-year > summary{cursor:pointer;border:1px solid var(--pine);background:var(--pine-soft);border-radius:14px;padding:.7rem .9rem;font-weight:700}
.archive-year > summary .note{font-weight:400;margin-left:.3rem;color:var(--ink);opacity:.7}
.archive-year > .archive{margin:.5rem 0 0 .6rem}

.who{display:flex;gap:.5rem;overflow-x:auto;padding:.2rem 0 .6rem;scrollbar-width:none}
.who::-webkit-scrollbar{display:none}
.who button{flex:0 0 auto;border:1px solid var(--line);background:var(--card);border-radius:999px;padding:.5rem .9rem;white-space:nowrap}
.who button[aria-pressed="true"]{background:var(--pine);border-color:var(--pine);color:var(--paper)}

.card{background:var(--card);border:1px solid var(--line);border-radius:16px;padding:.85rem 1rem;margin:.65rem 0}
.card h3{margin:0 0 .4rem;font-size:1.05em;font-weight:700}
.card ul,.card ol{padding-left:1.3rem;margin:.4rem 0}
.card li{margin:.35rem 0}
.actions{display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.7rem}
.btn{border:1px solid var(--line);background:var(--card);border-radius:12px;padding:.55rem .85rem;font-weight:500}
.btn.primary{background:var(--pine);border-color:var(--pine);color:#fff}
.btn.google{background:#fff;border-color:#4285f4;color:#1a73e8}
.btn.gold{background:var(--gold-soft);border-color:var(--gold)}
.btn:disabled,select:disabled{opacity:.5;cursor:default}

/* ---- Icôn trong nút ----
   Vẽ bằng mask chứ không phải một thẻ <svg> nằm trong nút, và đó là một quyết định chứ không phải
   một mẹo: applyLang() ghi textContent lên MỌI thẻ có data-vi, còn relabel() thì ghi lên cả nút —
   nên một cái icôn nằm làm con của nút sẽ bị xoá sạch ngay lần đầu ai đó bấm VI/EN. Cùng cái bẫy
   đã ghi ở relabel() và rephrase(). Mask thì nằm ngoài tầm với của textContent, và `currentColor`
   cho nó tự đổi màu theo nút — trắng trên nút xanh, xanh trên nút viền, không phải khai hai lần.
   1em: icôn lớn theo cỡ chữ, nên nút "A" phóng chữ thì nó phóng theo. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:.4rem}
.btn[class*="ic-"]::before{content:"";flex:none;width:1.05em;height:1.05em;background:currentColor;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center;mask-position:center;
  -webkit-mask-size:contain;mask-size:contain}
.btn.ic-plus::before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M12%205v14M5%2012h14%27%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M12%205v14M5%2012h14%27%2F%3E%3C%2Fsvg%3E")}
.btn.ic-enter::before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M10%2017l5-5-5-5%27%2F%3E%3Cpath%20d%3D%27M15%2012H3%27%2F%3E%3Cpath%20d%3D%27M13%203h6a2%202%200%200%201%202%202v14a2%202%200%200%201-2%202h-6%27%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M10%2017l5-5-5-5%27%2F%3E%3Cpath%20d%3D%27M15%2012H3%27%2F%3E%3Cpath%20d%3D%27M13%203h6a2%202%200%200%201%202%202v14a2%202%200%200%201-2%202h-6%27%2F%3E%3C%2Fsvg%3E")}
.btn.ic-chat::before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M20%2011.5a7.5%207.5%200%200%201-7.5%207.5H8l-4%203v-5.6A7.5%207.5%200%200%201%208%204h4.5a7.5%207.5%200%200%201%207.5%207.5z%27%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M20%2011.5a7.5%207.5%200%200%201-7.5%207.5H8l-4%203v-5.6A7.5%207.5%200%200%201%208%204h4.5a7.5%207.5%200%200%201%207.5%207.5z%27%2F%3E%3C%2Fsvg%3E")}
.btn.ic-share::before{-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M4%2012v7a2%202%200%200%200%202%202h12a2%202%200%200%200%202-2v-7%27%2F%3E%3Cpath%20d%3D%27M12%203v13%27%2F%3E%3Cpath%20d%3D%27m8%207%204-4%204%204%27%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M4%2012v7a2%202%200%200%200%202%202h12a2%202%200%200%200%202-2v-7%27%2F%3E%3Cpath%20d%3D%27M12%203v13%27%2F%3E%3Cpath%20d%3D%27m8%207%204-4%204%204%27%2F%3E%3C%2Fsvg%3E")}
/* Lịch có dấu cộng: cùng hình cuốn lịch với nút 📅 trên đầu trang, nên nút "Đăng sự kiện" đọc ra
   là "thêm vào CÁI LỊCH ấy" — dấu cộng trơn thì chỉ nói "thêm một cái gì đó". To hơn 1.05em một
   chút vì nó có nét bên trong, ở 1.05em dấu cộng dính vào khung. */
.btn.ic-calplus::before{width:1.25em;height:1.25em;-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Crect%20x%3D%273%27%20y%3D%275%27%20width%3D%2718%27%20height%3D%2716%27%20rx%3D%272.5%27%2F%3E%3Cpath%20d%3D%27M3%2010h18M8%203v4M16%203v4M12%2012.5v6M9%2015.5h6%27%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Crect%20x%3D%273%27%20y%3D%275%27%20width%3D%2718%27%20height%3D%2716%27%20rx%3D%272.5%27%2F%3E%3Cpath%20d%3D%27M3%2010h18M8%203v4M16%203v4M12%2012.5v6M9%2015.5h6%27%2F%3E%3C%2Fsvg%3E")}
/* Nút gọn: cho những chỗ cái nút là việc PHỤ bên cạnh một danh sách — "Mở một lớp" dưới danh mục,
   "Nhóm mới" trên hộp thư. Không bóp .btn của cả app: phần lớn nút trong đây là nút một-lần-một-
   việc mà người lớn tuổi phải nhắm trúng, và mấy pixel lấy được không đáng đổi bằng một cú chạm
   trượt. Vẫn cao chừng 2.1rem, tức là vẫn trên ngưỡng ngón tay. */
.btn.sm{padding:.4rem .72rem;font-size:.94em}

/* ---- Đầu trang của năm cửa mở ra từ trang chủ ----
   Mũi ‹ về trang chủ, icôn của đúng cái ô vừa bấm, rồi tên trang. Mũi ‹ đứng đây vì năm trang này
   KHÔNG có nút riêng dưới thanh thẻ — đường về duy nhất là nút Trang chủ ở góc trái đáy màn hình,
   và đi tới đi lui cả màn hình để quay lại một bước là một quãng đường không ai xin. Cùng hình
   mũi tên với mũi ‹ trong phòng chat, nên hai chỗ dạy nhau: học một lần, đọc được cả hai.
   Icôn cũng chính là icôn trên ô ở trang chủ — nó nối cái ô người ta vừa chạm với trang vừa mở ra,
   và đó là cách rẻ nhất để nói "đúng, bạn đang ở chỗ mình vừa bấm". */
.phead{display:flex;align-items:center;gap:.45rem;margin:0 0 .3rem}
.phead h1{margin:0;min-width:0;flex:1}
.phead .backbtn{flex:none;border:0;background:none;color:var(--pine);width:2.2rem;height:2.2rem;
  padding:0;margin-left:-.55rem;display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
.phead .backbtn::before{content:"";width:1.4rem;height:1.4rem;background:currentColor;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center;
  -webkit-mask-size:contain;mask-size:contain;
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M15%205l-7%207%207%207%27%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M15%205l-7%207%207%207%27%2F%3E%3C%2Fsvg%3E")}
.phead-ic{flex:none;font-size:1.2rem;line-height:1}
.note{font-size:.85em;color:var(--muted)}
.bi{display:grid;gap:.3rem}
.bi .en{color:var(--muted);font-style:italic}

/* chat */
.chat{display:flex;flex-direction:column;gap:.6rem;min-height:40vh}
.msg{max-width:88%;padding:.75rem 1rem;border-radius:16px;white-space:pre-wrap;word-wrap:break-word;
  scroll-margin-bottom:calc(var(--composer-h,4rem) + var(--tabs-h,calc(3.6rem + env(safe-area-inset-bottom,0px))) + .5rem)}
.msg.user{align-self:flex-end;background:var(--user);color:#fff;border-bottom-right-radius:4px}
.msg.bot{align-self:flex-start;background:var(--bot);border-bottom-left-radius:4px}
.msg.bot .meet{display:block;margin-top:.7rem}
.suggest{display:flex;flex-wrap:wrap;gap:.45rem;margin:.6rem 0}
.suggest button{border:1px dashed var(--line);background:transparent;border-radius:999px;padding:.4rem .8rem;font-size:.9em;color:var(--pine)}
.suggest button.more{border-style:solid;opacity:.7}
.composer{position:fixed;left:0;right:0;bottom:var(--tabs-h,calc(3.6rem + env(safe-area-inset-bottom,0px)));background:var(--paper);border-top:1px solid var(--line);padding:.6rem 1rem;display:none}
.composer.on{display:block}
.composer .row{max-width:720px;margin:0 auto;display:flex;gap:.5rem}
.composer textarea{flex:1;resize:none;border:1px solid var(--line);border-radius:14px;padding:.6rem .9rem;font:inherit;background:var(--card);color:var(--ink);max-height:6em}
.composer button{border:0;background:var(--pine);color:#fff;border-radius:14px;padding:0 1rem;font-weight:700}
/* Nút bắt đầu lại nằm ngay trên ô soạn tin: trò chuyện dài thì cuộn ngược lên đầu trang cũng
   mệt, mà chỗ này thì lúc nào cũng thấy. Viết sau .composer button, và thêm .composer cho đủ
   nặng, để nó không bị nút Gửi nhuộm xanh — đây là nút phụ, đừng mời bấm nhầm. */
.composer .newchat{max-width:720px;margin:0 auto .45rem;text-align:center}
.composer .newchat button{border:1px dashed var(--line);background:transparent;color:var(--muted);
  border-radius:999px;padding:.3rem .85rem;font-size:.85em;font-weight:400}

/* connect */
.care{display:flex;gap:.8rem;align-items:flex-start}
.care .dot{width:.7rem;height:.7rem;border-radius:50%;margin-top:.5rem;flex:none}
.dot.q{background:var(--gold)}.dot.s{background:#B04A3A}.dot.j{background:var(--pine)}
.care .t{flex:1}
.care .t b{display:block}
/* Tên, loại việc, tình huống, gợi ý — mỗi thứ một dòng. Trước đây chúng dính nhau bằng một thẻ
   <br> viết cứng, nên hàng nào thiếu gợi ý thì vẫn chừa lại một dòng trống. */
.care .t > span{display:block}
/* Hàng nút loại việc nằm ngay dưới nhãn "Việc gì", mà nhãn đã chừa sẵn khoảng của nó rồi. */
#care-kinds{margin-top:.25rem}
/* Ô gõ loại mới chỉ hiện khi bấm "Khác…", nên nó cần khoảng thở với hàng nút ngay trên. */
#care-other{margin-top:.45rem}
.done{opacity:.45;text-decoration:line-through}
input,select{font:inherit;color:var(--ink);background:var(--card);border:1px solid var(--line);border-radius:12px;padding:.55rem .75rem;width:100%}
/* Ô chọn giọng đọc (094): ô chọn giãn hết chỗ, nút Nghe thử đứng cạnh, rớt xuống dòng dưới khi chật. */
.voice-pick{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center}
.voice-pick select{flex:1 1 14rem;width:auto;min-width:0}
.field{margin:.4rem 0}
.field label{display:block;font-size:.85em;color:var(--muted);margin-bottom:.2rem}
.field textarea{font:inherit;color:var(--ink);background:var(--card);border:1px solid var(--line);border-radius:12px;padding:.55rem .75rem;width:100%;resize:vertical}

/* KHÔNG đặt transform / will-change lên thanh này. Ngày 29/09/2026 có một lượt thử đẩy nó lên
   lớp vẽ riêng để chữa chuyện thanh trôi; nó không chữa được gì, và trên WebKit thì transform
   đặt lên chính một thẻ position:fixed lại là một đường quen thuộc để thẻ ấy THÔI fixed. Gỡ đi.
   Chỗ hỏng thật nằm ở chỗ nó ĐỨNG trong cây thẻ, không phải ở lớp vẽ — xem chú thích trong
   index.html, ngay trên thẻ <nav>. */
nav.tabs{position:fixed;left:0;right:0;bottom:0;background:var(--card);border-top:1px solid var(--line);padding-bottom:env(safe-area-inset-bottom,0px);z-index:5}
/* Chế độ neo theo trang (app.js, setBarsPinned): iOS 26 kẹt visualViewport sau khi bàn phím đóng
   và mọi thẻ fixed đứng lệch theo nó. Lúc ấy ba thanh đáy thôi fixed, đứng ở toạ độ trang
   --pin-foot = chỗ cuộn + chiều cao khung nhìn. Thanh thẻ trừ chiều cao của chính nó bằng --tabs-h
   chứ không bằng transform, để đáy nó không thò qua đáy trang mà làm trang dài thêm. */
html.bars-pinned nav.tabs{position:absolute;bottom:auto;top:calc(var(--pin-foot) - var(--tabs-h,0px))}
html.bars-pinned .composer,html.bars-pinned .chatbar{position:absolute;bottom:auto;
  top:calc(var(--pin-foot) - var(--tabs-h,0px));transform:translateY(-100%)}
nav.tabs .row{max-width:720px;margin:0 auto;display:grid;grid-template-columns:repeat(var(--tabs,4),minmax(0,1fr))}
/* Mặc định 4, bằng số nút KHÔNG ẩn trong index.html (Trang chủ, Bài giảng, Tĩnh nguyện, Bài
   viết — hai nút của mục sư và quản trị mang class="hidden"): lần vẽ đầu tiên, trước khi app.js
   kịp đếm, cũng đã đúng một hàng. Con số này phải đi theo index.html — thêm một thẻ mà quên sửa
   đây thì nút cuối rớt xuống hàng hai cho tới lúc fitTabs() chạy, và cái nhịp đó mắt thấy được. */
/* minmax(0,…) chứ không phải 1fr trơn: nhãn không xuống dòng thì một ô rộng bằng chữ trong nó,
   và với 1fr trơn cả thanh nút đẩy trang rộng hơn màn hình. */
nav.tabs button{min-width:0}
/* Nhãn XUỐNG DÒNG, không cắt cụt bằng dấu ba chấm. Quy tắc này trước đây chỉ đi kèm nấc `tighter`
   (sáu nút trở lên); từ khi thanh chỉ còn nhiều nhất năm nút thì nấc ấy không còn, và nhãn quay về
   nowrap + ellipsis — đo ở 320px với nút chữ lớn bật lên thì đọc ra "Trang …", "Bài giả…",
   "Tĩnh ng…". Đúng cái cỡ chữ mà người bật nó lên là người cần đọc được nhất.
   Bốn nhãn ngắn ở cỡ chữ thường thì không chạm tới quy tắc này — chúng vừa một dòng như cũ. Chặn ở
   HAI dòng: một nhãn ba dòng thì thanh thẻ cao bằng cả một tấm thẻ, và --tabs-h (đo bằng
   ResizeObserver, xem sizeTabs) kéo theo cả ba thanh cố định khác lùi xuống. */
nav.tabs button span:last-child{max-width:100%;overflow:hidden;white-space:normal;overflow-wrap:break-word;
  line-height:1.15;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}
/* Năm nút trở lên (mục sư, quản trị): ở 360px mỗi ô chỉ còn 72px, mà "Tĩnh nguyện" cần 78px.
   Thu chữ lại chỉ cho những vai trò có thêm nút; hội thánh thường chỉ thấy bốn nút và giữ
   nguyên cỡ chữ — nhiều người ở đây đọc màn hình điện thoại không còn dễ. */
nav.tabs .row.tight button{font-size:.78em}
nav.tabs button{background:none;border:0;padding:.6rem 0 .5rem;font-size:.85em;color:var(--muted);display:flex;flex-direction:column;align-items:center;gap:.15rem}
nav.tabs button .ic{font-size:1.35em;line-height:1}
nav.tabs button[aria-pressed="true"]{color:var(--pine);font-weight:700}

/* ===== Trang chủ: lưới ô vuông bo góc ===== */
/* Đầu trang nhỏ: logo hội thánh, một câu chào, một dòng nói trang này là gì. Gọn đúng một hàng —
   cái đáng thấy ở trang này là lưới ô bên dưới, và trên màn hình điện thoại thì mỗi hàng chữ ở
   trên là một ô bị đẩy xuống dưới mép màn hình. */
/* ---- Dải đầu trang chủ ----
   Nền là banner.svg phủ kín bằng cover, nên nó đúng hình ở mọi bề ngang mà không cần một @media
   nào. Thêm một lớp tối vắt từ trái sang: hình vẽ đã để khoảng bên trái lặng sẵn, nhưng "lặng"
   còn tuỳ chỗ bị xén, và chữ trắng thì phải đọc được kể cả trong cái khung hẹp nhất.
   KHÔNG đổi theo chế độ sáng/tối: đây là một tấm hình, không phải một mặt giấy — một buổi sớm
   thì sáng như nhau ở cả hai chế độ, và chữ trên nó luôn trắng. */
.hbanner{position:relative;display:flex;align-items:center;gap:.7rem;
  margin:.1rem 0 .8rem;padding:1rem;min-height:6.4rem;border-radius:20px;overflow:hidden;
  background:url("banner.svg") center/cover no-repeat,#103E35}
.hbanner::before{content:"";position:absolute;inset:0;
  background:linear-gradient(100deg,rgba(6,28,24,.82) 0%,rgba(6,28,24,.55) 55%,rgba(6,28,24,.12) 100%)}
.hbanner > *{position:relative}
/* Logo hội thánh là một ô ĐEN chữ trắng, nên trên nền tối nó tự biến mất nếu không có gì viền
   lại. Một vòng sáng mảnh là đủ — đóng khung nó thành một cái dấu, đúng thứ nó vốn là. */
.hblogo{flex:none;width:3.2rem;height:3.2rem;border-radius:14px;display:block;
  box-shadow:0 0 0 1px rgba(255,255,255,.28),0 2px 10px rgba(0,0,0,.35)}
.hbname{margin:0;min-width:0;color:#FBF8F1;font-family:var(--serif);font-weight:600;
  font-size:1.02em;line-height:1.25;text-shadow:0 1px 3px rgba(0,0,0,.5)}
.hhero{display:flex;align-items:center;gap:.75rem;margin:.1rem 0 1rem}
.hhi{min-width:0}
.hhi h1{margin:0}
.hhi .note{margin:.1rem 0 0}
/* auto-fit + minmax, KHÔNG phải một con số cột cố định: bốn ô một hàng trên máy tính, ba trên
   điện thoại thường, hai ở 320px hay khi bật chữ lớn — không cần một @media nào, và thêm một ô
   thứ sáu sau này (Hình ảnh, chẳng hạn) cũng không phải sửa gì ở đây.
   minmax đo bằng rem chứ không em: nút chữ lớn đổi --fs của <body>, và chữ TRONG ô thì nên lớn
   lên theo, nhưng cái ô thì đã đủ rộng cho ngón tay từ trước — để nó phình theo nữa thì hai ô
   cũng không còn vừa một hàng. */
.hgrid{display:grid;gap:.7rem;grid-template-columns:repeat(auto-fit,minmax(6.2rem,1fr))}
/* aspect-ratio:1 — ô vuông, như trên màn hình chính của điện thoại. Đó là cả lý do của kiểu này:
   người ở hội thánh này đã bấm những ô vuông bo góc ấy mỗi ngày từ nhiều năm, nên không ai phải
   học cách đọc lưới này. */
.htile{position:relative;aspect-ratio:1;border:1px solid var(--line);background:var(--card);
  border-radius:22px;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:.35rem;padding:.5rem;text-align:center;color:inherit}
.htile .ic{font-size:1.9rem;line-height:1}
/* overflow-wrap:anywhere cho nhãn: "Tĩnh nguyện" hay "Messages" thì vừa, nhưng ở 320px với chữ
   lớn thì một ô chỉ còn chừng 5rem, và một nhãn không gãy được sẽ đẩy cả lưới rộng hơn màn hình. */
.htile .lb{font-size:.85em;font-weight:500;line-height:1.2;overflow-wrap:anywhere}
.htile[aria-pressed="true"]{border-color:var(--pine);color:var(--pine)}
/* Ô bị khoá: MỜ ĐI, không biến mất. Người đang chờ hội thánh nhận vào mà mở trang chủ ra thấy
   thiếu ba ô thì tưởng app hỏng hoặc tưởng mình bị bỏ sót; thấy chúng mờ thì hiểu là có cửa, chỉ
   là chưa mở. Và vẫn bấm được — bấm vào thì một dòng chữ nói vì sao. Cố ý KHÔNG dùng `disabled`:
   nút disabled không nhận cú chạm nào, nên dòng chữ ấy sẽ không bao giờ hiện ra, và ô mờ thành
   một ô chết. */
.htile.locked{opacity:.45}
.htile.locked .ic{filter:grayscale(1)}
/* Có gì mới: THẮP SÁNG CẢ CÁI Ô, không chỉ chấm một hạt ở góc. Cái chấm vẫn ở đó — nó là thứ
   tiếng chung với chấm trên thanh thẻ và trên icôn lịch, bỏ đi thì ba chỗ nói ba kiểu — nhưng
   một hạt .6rem ở góc một cái ô 6rem thì phải ĐI TÌM mới thấy, mà trang chủ là chỗ người ta
   liếc một cái rồi quyết định mở gì. Nền vàng nhạt với viền vàng thì thấy từ khoảng cách cầm
   máy, trước cả khi đọc được chữ trên ô.
   --gold-soft đổi theo chế độ sáng/tối ngay từ :root, nên một dòng này đúng cho cả hai. */
.htile.newdot{border-color:var(--gold);background:var(--gold-soft)}
.htile.newdot .lb{font-weight:700}
.htile.newdot::after{content:"";position:absolute;top:.6rem;right:.6rem;width:.6rem;height:.6rem;
  border-radius:50%;background:var(--gold)}
/* Ô đang mở VÀ có gì mới: viền xanh của ô đang mở thắng, vì nó trả lời câu "tôi đang ở đâu" —
   câu hỏi cấp trên. Nền vàng ở lại, nên vẫn đọc được là có gì mới. */
.htile.newdot[aria-pressed="true"]{border-color:var(--pine)}

.toast{position:fixed;left:50%;transform:translateX(-50%);bottom:calc(var(--tabs-h,calc(3.6rem + env(safe-area-inset-bottom,0px))) + .9rem);background:var(--ink);color:var(--paper);padding:.6rem 1rem;border-radius:999px;font-size:.9em;opacity:0;transition:opacity .25s;pointer-events:none;z-index:9}
.toast.on{opacity:1}
/* Có bản mới nhưng người ta đang soạn dở: không giật trang khỏi tay họ, chỉ mời một dòng.
   Ngồi ngay trên thanh thẻ, nơi ngón cái vốn đã ở đó. */
.update-bar{position:fixed;left:50%;transform:translateX(-50%);bottom:calc(var(--tabs-h,calc(3.6rem + env(safe-area-inset-bottom,0px))) + .9rem);
  max-width:calc(100% - 2rem);background:var(--pine);color:var(--paper);border:0;border-radius:999px;
  padding:.6rem 1.1rem;font:inherit;font-size:.9em;font-weight:500;box-shadow:0 2px 12px rgb(0 0 0 / .22);z-index:10}
/* Dòng mời chiếm đúng chỗ toast hay đứng, mà lúc đang soạn thì toast "Đã lưu" lại hiện luôn tay.
   Có dòng mời thì đẩy toast lên trên nó. */
body.has-update .toast{bottom:calc(7.6rem + env(safe-area-inset-bottom,0px))}

/* TOAST NEO VÀO MỘT CÁI NÚT. Toast thường nằm sát đáy màn hình — chỗ không che mất gì, đúng cho
   phần lớn việc, vì phần lớn nút trong app này nằm giữa trang hoặc dưới thấp. Nhưng cái chuông
   nằm ở ĐẦU trang, nên với nó thì đáy màn hình là chỗ xa nhất có thể: bấm xong phải đưa mắt hết
   chiều dọc màn hình mới thấy câu trả lời, và trên điện thoại lớn thì thường chưa kịp tìm ra nó
   đã tắt. Neo vào nút thì câu trả lời nằm ngay chỗ câu hỏi vừa được đặt.
   Toạ độ do JS đặt (xem toast() trong app.js) vì nó phải đo cái nút thật: đầu trang cao bao
   nhiêu còn tuỳ cỡ chữ, tuỳ đầu trang có gãy hai hàng hay không.
   Không còn là viên thuốc bo tròn một dòng nữa: câu ở đây dài hơn một dòng ("Xong. Sáng mai app
   sẽ nhắc bạn, và ai nhắn cho bạn thì máy này báo ngay."), mà một viên thuốc dài hết bề ngang
   màn hình thì đọc như một vệt chữ, không ra câu. */
.toast.pinned{
  border-radius:14px;padding:.62rem .85rem;text-align:left;line-height:1.35;
  max-width:min(21rem, calc(100vw - 1.6rem));white-space:normal;
  box-shadow:0 6px 22px rgba(0,0,0,.22);
}
/* Mũi nhọn chỉ lên đúng cái nút vừa bấm. Không có nó thì hộp chữ chỉ là "một hộp chữ ở gần
   đấy"; có nó thì nó nói ra mình đang trả lời cho cái nào, giữa năm cái nút đứng sát nhau trên
   đầu trang. JS đặt --tip = khoảng cách từ mép trái hộp tới tâm cái nút. */
.toast.pinned::before{
  content:"";position:absolute;top:-.3rem;left:var(--tip,50%);
  width:.66rem;height:.66rem;background:var(--ink);border-radius:2px;
  transform:translateX(-50%) rotate(45deg);
}
:focus-visible{outline:3px solid var(--gold);outline-offset:2px}
@media (prefers-reduced-motion:reduce){*{transition:none!important}}

/* ---- thêm cho bản PWA ---- */
.auth{display:flex;gap:.5rem}
.auth input{flex:1}
.auth-stack{display:grid;gap:.5rem}
.auth-stack .actions{margin-top:0}
/* Trong thẻ tạo tài khoản mỗi ô có nhãn riêng, nên khoảng cách đã do gap của auth-stack lo;
   để .field giữ thêm margin của nó nữa thì ba ô rời nhau ra như ba khối riêng biệt. */
.auth-stack .field{margin:0}
/* "hoặc" cắt ngang thẻ bằng một vạch mờ: nó nói rằng phần dưới là cùng một việc làm bằng đường
   khác, không phải việc thứ hai phải làm tiếp. */
.auth-or{display:flex;align-items:center;gap:.6rem;margin:.9rem 0 .2rem;color:var(--muted);font-size:.85em}
.auth-or::before,.auth-or::after{content:"";flex:1;height:1px;background:var(--line)}
/* Dòng chuyển giữa hai màn hình nằm NGOÀI thẻ trắng: trong thẻ là những ô đang phải điền, ngoài
   thẻ là câu hỏi "tôi có đang ở đúng chỗ không". Chữ "Tạo tài khoản" gạch chân, to bằng chữ
   thường, đủ rộng để ngón cái bấm trúng mà không giành chỗ với nút xanh. */
/* Câu nhắc sau khi bấm (thư xác nhận vừa gửi, thư đặt lại mật khẩu vừa gửi) đứng cuối cùng, sau
   cả dòng chuyển màn hình — mà chữ nhỏ màu xám ở cuối trang thì mắt lướt qua mất. Cho nó một
   khung vàng nhạt để thành câu trả lời cho cái nút vừa bấm, không phải một dòng ghi chú. */
#auth-note:not(:empty),#pw-note:not(:empty){background:var(--gold-soft);border:1px solid var(--gold);border-radius:12px;
  padding:.6rem .8rem;margin-top:.9rem;color:var(--ink)}
.auth-switch{text-align:center;margin:.9rem 0 0}
.auth-switch button{border:0;background:none;color:var(--pine);text-decoration:underline;
  font:inherit;font-weight:600;padding:.35rem .25rem;margin-left:.15rem}
.account-settings{margin-top:1.2rem}
/* Trang đăng nhập: ba nút, mỗi nút một hàng, rộng hết bề ngang. Để chúng tự xuống dòng theo
   bề rộng chữ thì được ba nút dài ngắn khác nhau xếp so le — mà đây là việc đầu tiên người mới
   làm trong app, và nhiều người trong hội thánh này bấm bằng ngón cái ở tuổi bảy mươi. */
#authbox .actions{display:grid;gap:.45rem;margin-top:.5rem}
/* Ô mật khẩu mới đứng cạnh nút thì ở 390px chỉ còn 170px, cụt mất chữ "(ít nhất 6 ký tự)" —
   đúng cái điều kiện mà người ta cần đọc trước khi gõ. Cho ô nguyên hàng, nút xuống dưới. */
.account-settings .auth{display:grid;gap:.5rem}
.account-settings .auth .btn{justify-self:start}
/* Ô bẫy của thẻ góp ý: đưa ra khỏi màn hình chứ không display:none — vài máy gửi rác bỏ qua
   những ô bị ẩn hẳn, mà đúng mấy máy ấy là thứ ô này sinh ra để bắt. */
.hp{position:absolute;left:-9999px;width:1px;height:1px;opacity:0}
#fb-kinds{margin:.6rem 0 .2rem}
.tag{display:inline-block;font-size:.75em;padding:.1rem .5rem;border-radius:999px;background:var(--pine-soft);color:var(--pine);margin-left:.4rem}
.draft{border-color:var(--gold)}
.live{border-color:var(--pine)}
.sermon > summary{cursor:pointer;font-family:var(--serif);font-weight:600;font-size:1.05em}
.sermon[open] > summary{margin-bottom:.4rem;padding-bottom:.4rem;border-bottom:1px solid var(--line)}
nav.tabs button.hidden{display:none}
.admin-mat{margin:.5rem 0}
.admin-mat summary{cursor:pointer;font-weight:600}
/* Mục sư đọc và sửa thẳng trên nội dung đã dựng, không phải trên HTML thô. */
.mat-edit{border:1px solid var(--line);border-radius:12px;padding:.2rem .8rem;margin-top:.4rem;background:var(--card)}
.mat-edit:focus{outline:2px solid var(--pine);outline-offset:1px}
.mat-edit .card{border:0;background:none;padding:.6rem 0;margin:0}
.mat-edit .card + .card{border-top:1px solid var(--line)}
/* Thanh định dạng ngay trên ô sửa; dính ở đầu khi cuộn một bài dài. */
.fmt-bar{position:sticky;top:calc(env(safe-area-inset-top,0px) + var(--head-h,3.4rem));z-index:3;display:flex;flex-wrap:wrap;gap:.25rem;margin-top:.4rem;padding:.3rem;border:1px solid var(--line);border-radius:10px;background:var(--card)}
.fmt-bar button{min-width:2.1rem;height:2.1rem;padding:0 .4rem;border:1px solid var(--line);border-radius:8px;background:none;color:inherit;font:inherit;cursor:pointer}
.fmt-bar button:hover{border-color:var(--pine)}
.mat-edit blockquote,.dev-body blockquote,#who-content blockquote{margin:.6rem 0;padding:.5rem .9rem;border-left:3px solid var(--gold);background:var(--gold-soft);border-radius:0 10px 10px 0;font-style:italic}

/* ---- Quản trị: danh sách hội thánh ---- */
/* Mỗi người một dòng trong một khối liền, không phải mỗi người một cái thẻ rời: quản trị mở
   trang này ra để tìm một người trong hai trăm, nên cái nhìn đầu tiên phải là càng nhiều tên
   càng tốt — không phải ba cái thẻ đầu tiên và mép của cái thứ tư. */
.rlist{border:1px solid var(--line);border-radius:16px;background:var(--card);overflow:hidden;margin:.65rem 0}
.ruser + .ruser{border-top:1px solid var(--line)}
.ruser > summary{cursor:pointer;display:grid;gap:.1rem;padding:.55rem 2.2rem .55rem .9rem;position:relative}
.ruser > summary::-webkit-details-marker{display:none}
.ruser > summary::marker{content:""}
/* Mũi tên tự vẽ: summary ở display:grid thì trình duyệt bỏ cái tam giác mặc định, mà một hàng
   bấm được thì phải nói ra là nó bấm được. */
.ruser > summary::after{content:"\203A";position:absolute;right:1rem;top:50%;color:var(--muted);
  transform:translateY(-50%) rotate(90deg);transition:transform .15s}
.ruser[open] > summary{background:var(--pine-soft)}
.ruser[open] > summary::after{transform:translateY(-50%) rotate(-90deg)}
.ruser .rname{display:flex;align-items:baseline;flex-wrap:wrap}
.ruser .note{overflow-wrap:anywhere}
.rbody{padding:.1rem .9rem .8rem}
.rbody select{width:auto;min-width:9rem;max-width:100%}
.rbody .actions{margin-top:.5rem;align-items:center}
.rbody a.btn{text-decoration:none;color:var(--pine);white-space:nowrap}
/* Bốn vai bốn màu: ở một danh sách dài, mắt bắt được màu trước khi đọc xong cái tên. Vàng cho
   quản trị vì đó là vai hiếm nhất và nặng nhất — thấy hai chấm vàng là biết phải hỏi lại. */
.tag.rt-admin{background:var(--gold-soft);color:var(--ink)}
.tag.rt-pastor{background:var(--pine);color:var(--paper)}
.tag.rt-care{background:var(--pine-soft);color:var(--pine)}
.tag.rt-member{background:transparent;color:var(--muted);border:1px solid var(--line)}
.tag.rt-external{background:transparent;color:var(--muted);border:1px solid var(--line)}
.tag.rt-new_member{background:transparent;color:var(--muted);border:1px solid var(--line)}
/* Khách: cùng một viên thuốc trống như thành viên, chỉ khác viền — nét đứt thay cho nét liền.
   Nét đứt đọc ra được ở cỡ này mà không cần thêm một màu nữa vào bảng, và nó nói đúng điều cần
   nói: một cái vòng chưa khép. */
.tag.rt-guest{background:transparent;color:var(--muted);border:1px dashed var(--line)}
/* Viên thuốc chỉ còn một ký hiệu: bóp hẹp lại cho vừa một glyph, và bỏ baseline của emoji đi
   (line-height:1) để nó không đội viên thuốc cao hơn dòng chữ bên cạnh. */
.tag.ico{padding:.15rem .4rem;line-height:1;margin-left:.35rem}
/* Chữ dành riêng cho trình đọc màn hình: không ai thấy, nhưng vẫn được đọc ra. Mắt đọc ký hiệu
   và màu; tai đọc cái này. Dùng clip chứ không dùng display:none hay visibility:hidden — hai
   cách đó giấu luôn khỏi trình đọc, tức là giấu khỏi đúng người cần nó nhất. */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
/* Hàng nút lọc xuống dòng chứ không cuộn ngang như .who ở thẻ Bài giảng: mấy con số trên đó
   là bản kiểm kê — "hai quản trị, một mục sư, bốn chăm sóc" — và một bản kiểm kê phải thấy hết
   trong một cái liếc, chứ không phải vuốt sang mới biết còn gì ở bên kia mép màn hình. */
#rfilter,#cb-filter{flex-wrap:wrap;overflow-x:visible}
#rfilter button,#cb-filter button{display:inline-flex;align-items:center;gap:.4rem;padding:.4rem .8rem;font-size:.92em}
/* Hàng lọc của cuốn danh bạ mượn nguyên bộ mặt của hàng lọc ở thẻ Quản trị — cùng một việc
   ("cho tôi xem riêng mấy người vai này"), nên cùng một hình. Khe trên dưới hẹp hơn một chút vì
   ở đây nó nằm giữa ô tìm và một hàng nút, chứ không đứng riêng dưới một cái tiêu đề. */
#cb-filter{margin:.15rem 0 .45rem}
.btn.danger{border-color:#b04a3a;color:#b04a3a}

/* ---- tĩnh nguyện mỗi sáng ---- */
/* Phần này người ta đọc lúc sáu giờ sáng, thường là thứ duy nhất họ đọc trong ngày. Nên nó được
   dựng như một trang đọc: chữ serif, dòng thưa hơn, không có gì nhấp nháy đòi bấm. */
.dev-h{font-family:var(--serif);font-weight:600;font-size:1.45em;line-height:1.25;margin:.2rem 0 .4rem}
/* ✍️ sau tựa: sáng nay mục sư tự viết (103). Nhỏ hơn chữ tựa để nó là một ghi chú, không là một phần tựa. */
.dev-by{font-size:.7em;vertical-align:.15em;white-space:nowrap}
.dev-body{font-family:var(--serif);line-height:1.7}
.dev-body p{margin:.7rem 0}
.dev-audio{width:100%;margin:.6rem 0}
/* Video buổi sáng: cùng lề với thanh tiếng, vì nó thay chỗ của thanh đó. Nền đen để khung không
   nhảy màu trong khoảnh khắc trước khi frame đầu tiên về, và max-height để một đoạn quay dọc
   không đẩy cả bài suy gẫm xuống dưới màn hình. */
.dev-video{width:100%;max-height:70vh;margin:.6rem 0;border-radius:16px;display:block;background:#000}
/* Hai thẻ cuối bài là hai việc khác nhau — một việc để làm, một lời để đọc — nên bắt màu khác
   nhau, đủ để mắt biết mình đã hết phần suy gẫm. */
.dev-do{border-left:4px solid var(--gold)}
.dev-pray{border-left:4px solid var(--pine);background:var(--pine-soft)}
.dev-pray p{font-family:var(--serif)}
#dev-amen[aria-pressed="true"]{background:var(--pine);border-color:var(--pine);color:#fff}
/* Chấm trên thẻ Kết nối khi sáng nay chưa đọc: lời mời, không phải con số nợ. */
nav.tabs button.newdot .ic{position:relative}
nav.tabs button.newdot .ic::after{content:"";position:absolute;top:-.1em;right:-.35em;width:.42em;height:.42em;border-radius:50%;background:var(--gold)}
.dev-row{display:grid;gap:.15rem}
.dev-row .tag.wait{background:var(--gold-soft);color:var(--ink)}
/* Thẻ tuần của mục sư: đang hiện là màu đặc, mạnh nhất, vì đó là bài hội thánh đang đọc; chưa
   xếp lịch là viền đứt vàng — trống, đang chờ — hợp với viền vàng của thẻ nháp, và không lẫn
   với số Amen nền vàng ngay bên cạnh. */
#dev-list .tag.onair{background:var(--pine);color:var(--paper)}
#dev-list .tag.wait{background:transparent;color:var(--ink);border:1px dashed var(--gold)}
/* Số Amen trong danh sách của mục sư: vàng, để tách khỏi nhãn ngày/trạng thái màu xanh. */
.tag.amen{background:var(--gold-soft);color:var(--ink)}

/* ---- thẻ Mục sư chia ba mục ---- */
.subtabs{display:flex;gap:.3rem;margin:.6rem 0 1rem;border-bottom:1px solid var(--line)}
.subtabs button{flex:1;background:none;border:0;border-bottom:3px solid transparent;padding:.55rem .2rem;
  color:var(--muted);font-weight:500;font-size:.95em;display:flex;align-items:center;justify-content:center;gap:.35rem}
.subtabs button[aria-pressed="true"]{color:var(--pine);border-bottom-color:var(--pine);font-weight:700}
/* Số 0 thì không hiện gì: một cái chấm ghi "0" chỉ làm người ta dừng lại rồi nhận ra không có việc gì. */
.cnt:empty{display:none}
.cnt{background:var(--gold);color:#1F2A33;border-radius:999px;min-width:1.4em;height:1.4em;
  display:inline-flex;align-items:center;justify-content:center;font-size:.78em;font-weight:700;padding:0 .35em}
.pane{display:none}
.pane.on{display:block}
/* Việc vài lần một năm (nhập bài giảng bằng tay) thì gập lại, đừng chiếm chỗ ngang việc hằng tuần. */
.fold > summary{cursor:pointer;font-family:var(--serif);font-weight:600;font-size:1.05em}
.fold[open] > summary{margin-bottom:.6rem;padding-bottom:.4rem;border-bottom:1px solid var(--line)}
/* Thẻ bài giảng nằm trong một tháng đã gập: thụt vào cho thấy nó thuộc về dòng tháng ở trên. */
.month-cards{margin:.5rem 0 0 .6rem}

/* ---- lưới một tuần của mục sư ---- */
.weeknav{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;margin:1rem 0 .2rem}
.weeknav .wk{flex:1;min-width:8rem;text-align:center;display:grid;gap:.05rem}
.weeknav .wk b{font-family:var(--serif);font-weight:600}
.weeknav .iconbtn{flex:none}
.weeknav .btn{flex:0 0 100%;padding:.4rem 1rem;font-size:.9em}
.dev-day{margin:.7rem 0}
/* Nhãn ngày đứng ngoài thẻ, chữ nhỏ và nhạt: nó chỉ để định vị, không phải để đọc. Thứ đáng
   đọc là cái tựa ngay bên dưới nó. */
.dev-day-h{margin:0 0 .25rem;font-size:.85em;font-weight:700;color:var(--muted)}
.dev-day .card,.dev-day details.card{margin:0}
/* Hôm nay: một vạch xanh dọc bên trái cả ô. Không đổi màu viền thẻ, vì viền thẻ đang mang một
   tin khác — vàng là bản nháp, xanh là đã xếp lịch — và ghi đè lên nó thì mất tin đó. */
.dev-day.today{border-left:3px solid var(--pine);padding-left:.7rem}
.dev-day.today .dev-day-h{color:var(--pine)}
/* Viền đứt cho sáng còn trống: lướt mắt qua bảy ô là thấy ngay chỗ thiếu, không cần đọc chữ. */
#dev-list .dev-day > .card{padding:.7rem .85rem}
.dev-blank{border-style:dashed;background:none}
.dev-blank .note{margin:0}

/* ---- lịch hội thánh ---- */
/* Thanh tháng: hai mũi tên ôm lấy tên tháng, và nút "Tháng này" chỉ mọc ra khi đã đi lạc khỏi
   nó. Cùng hình dạng với thanh tuần của mục sư ở trên, vì làm cùng một việc. */
.calnav{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem;margin:.45rem 0 .25rem}
.calnav .mo{flex:1;min-width:8rem;text-align:center;font-family:var(--serif);font-weight:600}
.calnav .iconbtn{flex:none}
.calnav .btn{flex:0 0 100%;padding:.4rem 1rem;font-size:.9em}
.calgrid{display:grid;grid-template-columns:repeat(7,1fr);gap:.2rem}
/* Đầu cột thứ trong tuần: chữ nhỏ và nhạt, vì đọc một lần rồi thôi. */
.calgrid .wd{text-align:center;font-size:.75em;color:var(--muted);padding:.2rem 0}
/* Ô ngày là một cái nút vuông. aspect-ratio thay cho chiều cao cố định: bật chữ lớn thì ô to
   theo bề ngang của màn hình chứ không phải theo rem, nên lưới không bao giờ bị méo thành hình
   chữ nhật dẹt trên máy hẹp. */
.calgrid button{aspect-ratio:1;border:1px solid transparent;background:none;border-radius:12px;
  display:grid;place-items:center;align-content:center;gap:.12rem;padding:0;font-size:.9em;line-height:1}
.calgrid button:disabled{opacity:.3}     /* ngày của tháng bên cạnh: có mặt để giữ lưới thẳng hàng */
/* Ngày có việc: tô cả ô. Bản đầu đeo một cái chấm 5px dưới con số — đúng, nhưng phải đi tìm
   mới thấy, mà cả cái lưới này sinh ra để trả lời một câu liếc mắt. */
.calgrid button.has{background:var(--gold-soft);border-color:var(--gold);font-weight:700}
/* Hôm nay: một vòng viền phía TRONG, không phải màu nền. Nền đã có người dùng rồi (ô vàng ở
   trên), mà hôm nay lại rất hay trùng với một ngày có việc — hai tin trên cùng một ô thì mỗi
   tin phải nói bằng một thứ tiếng khác nhau. */
.calgrid button.today{box-shadow:inset 0 0 0 2px var(--pine);color:var(--pine);font-weight:700}
/* Ngày đang chọn đè lên cả hai. Ba quy tắc này cùng một mức, nên cái viết sau cùng thắng —
   thứ tự ở đây là một phần của thiết kế, đừng xếp lại cho gọn mắt. */
/* Ngày một chuỗi NGHỈ (082): viền đứt, số gạch ngang — vẫn bấm vào đọc được lời báo nghỉ,
   mà không ai nhìn lướt lại tưởng hôm ấy có buổi. Đứng TRƯỚC quy tắc ngày đang chọn. */
.calgrid button.off{border:1px dashed #b04a3a;color:#b04a3a;text-decoration:line-through}
.calgrid button[aria-pressed="true"]{background:var(--pine);border-color:var(--pine);color:#fff}
/* Hàng tên ngày khi đang lọc theo một ngày. Không dùng nhờ .calnav: ở đó nút "Tháng này"
   chiếm trọn một hàng (đúng, vì nó hiếm khi hiện), còn nút bỏ lọc ở đây thì hiện suốt
   mỗi lần chạm vào lưới — một hàng riêng cho nó là đẩy danh sách xuống vô cớ. */
.calday{display:flex;flex-wrap:wrap;align-items:baseline;gap:.5rem;margin:1rem 0 .3rem}
.calday .mo{flex:1;min-width:9rem;font-family:var(--serif);font-weight:600}
.calday .btn{flex:none;padding:.35rem .8rem;font-size:.85em}
/* Ngày và giờ đứng cạnh nhau: hai ô ngắn, mà mỗi ô một hàng thì biểu mẫu dài thêm vô cớ. */
.calrow{display:flex;flex-wrap:wrap;gap:.6rem}
.calrow .field{flex:1 1 8rem;margin:.4rem 0}
.subfold > summary{cursor:pointer;margin:.5rem 0 0}
/* Tên trang nhỏ hơn h1 của các trang khác. Ở đây nó phải nhường chỗ: ngay dưới nó là khúc Sắp
   tới, thứ người ta mở trang này để đọc. Một cái tựa 1.7em cộng dòng dẫn cộng lề chiếm gần một
   phần tư màn hình điện thoại trước khi tới dòng chữ đầu tiên đáng đọc. Chỉ thu ở trang này —
   Bài giảng và Tĩnh nguyện mở ra là để ĐỌC, cái tựa lớn ở đó đứng đúng chỗ của nó. */
#v-calendar #cal-lead{margin:0 0 .2rem}
/* Nút (?) cạnh "Đăng sự kiện": mở/đóng lời giới thiệu trang. Đang mở thì tô đặc. */
.cal-qbtn{flex:none;color:var(--muted);font-weight:700}
/* ‹ · 📅 · Lịch hội thánh · (?) · Đăng — tựa cùng cỡ với mọi trang khác. Ở màn hình hẹp nút Đăng
   chỉ còn icôn lịch-có-dấu-cộng (chữ nằm trong aria-label), để cả hàng vừa 375px. */
#v-calendar .phead h1{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@media (max-width:350px){ #v-calendar .phead h1{white-space:normal} }
@media (max-width:480px){
  #v-calendar #cal-post > span{display:none}
  #v-calendar #cal-post{width:2.2rem;height:2.2rem;padding:0;border-radius:999px;flex:none}
}
.cal-qbtn[aria-expanded="true"]{background:var(--muted);border-color:var(--muted);color:var(--card)}
/* Đầu khúc "Sắp tới". Nhỏ hơn tên trang nhưng vẫn là chữ serif, để nó là một cái mốc chứ
   không phải một dòng chữ đậm lạc giữa mấy cái thẻ. */
.cal-h{font-family:var(--serif);font-weight:600;font-size:1.05em;margin:.7rem 0 .1rem}
/* Tờ thông báo: tràn hết bề ngang thẻ, tràn cả ra phần đệm của thẻ — một tờ giấy dán tường thì
   không có lề trắng quanh nó. max-height để một tấm chụp dọc bằng điện thoại không đẩy cái tựa
   xuống dưới màn hình; object-fit giữ khung hình, cắt chứ không bóp méo. */
.ev-poster{display:block;width:calc(100% + 2rem);margin:-.85rem -1rem .7rem;border-radius:16px 16px 0 0;
  max-height:60vh;object-fit:cover;background:var(--bot)}
/* Khung xem trước trong biểu mẫu thì ngược lại: nhỏ thôi, nó chỉ để xác nhận "đúng tấm này". */
.ev-prev{display:block;max-width:100%;max-height:11rem;border-radius:12px;margin:.5rem 0 0;object-fit:contain}
#ev-pic-row{margin-top:.25rem}
/* Hàng nút của thẻ sự kiện: icôn, dồn về bên phải. Dồn phải vì chúng là công cụ, không phải
   nội dung — cột chữ bên trái giữ nguyên một mép thẳng, và mắt không phải nhảy qua năm cái nút
   để xuống thẻ sau. */
.evact{display:flex;flex-wrap:wrap;gap:.35rem;margin-top:.6rem;justify-content:flex-end}
/* 2.6rem ≈ 44px, đúng mức tối thiểu cho một mục tiêu chạm. To hơn .iconbtn của đầu trang (2.2rem)
   là có lý do: mấy nút này đứng sát nhau, một trong số đó là nút xóa, và nhiều bàn tay ở hội
   thánh này không còn nhắm chính xác như xưa. Bấm nhầm ở đầu trang là đổi thẻ; bấm nhầm ở đây
   là mất một bảng tin. Ở cỡ chữ lớn nó nở theo rem lên chừng 55px.
   Nhiều nhất bốn nút một hàng (xong · sửa · gỡ-hoặc-đặt-lại · xóa), nên 4×44 vẫn thừa chỗ ở 360px. */
.eb{border:1px solid var(--line);background:var(--card);color:var(--muted);border-radius:999px;
  width:2.6rem;height:2.6rem;padding:0;flex:none;display:inline-flex;align-items:center;justify-content:center}
.eb svg{width:1.1rem;height:1.1rem;display:block}
.eb.danger{color:#b04a3a;border-color:#b04a3a}
.evlike{margin-right:auto;gap:.3rem;width:auto;min-width:2.6rem;padding:0 .75rem}
.evlike span{font-size:.85rem;font-variant-numeric:tabular-nums}
.evlike.on{color:#c0392b;border-color:#c0392b}
@media (hover:hover){.eb:hover{border-color:var(--pine);color:var(--pine)}
  .eb.danger:hover{border-color:#b04a3a;color:#b04a3a;background:var(--gold-soft)}}
/* Việc đã xong: nhạt đi, KHÔNG gạch ngang như .done của lời cầu nguyện. Gạch ngang nói "bỏ cái
   này đi", mà một việc đã xong thì vẫn là một việc đã làm — nó chỉ thôi đòi chỗ trên đầu trang. */
.ev-card.fin{opacity:.6}
/* Thẻ gập: chỉ ngày và tên, gọn lại. Việc đã qua thì nhạt đi thêm. Thẻ đã xong (.fin) mở ra cũng
   nhạt như cũ. */
.ev-card.shut{padding-top:.7rem;padding-bottom:.7rem}
.ev-card.shut h3{margin:0;font-size:1em}
.ev-card.shut,.ev-card .ev-top{cursor:pointer}
@media (hover:hover){.ev-card.shut:hover{border-color:var(--pine)}}
.ev-card.past{opacity:.75}
/* Mũi tên gập/mở ở góc phải trên của thẻ. Nền đặc, vì thẻ có tờ thông báo thì nó nằm đè lên ảnh. */
.ev-card{position:relative}
/* Thẻ vừa mở bằng link ?e= (evFocus): dừng dưới đầu trang dính, và chớp một vòng viền vàng. */
#cal-list .ev-card{scroll-margin-top:calc(env(safe-area-inset-top,0px) + var(--head-h,3.4rem) + .6rem)}
.ev-card.flash{animation:evflash 2.4s ease-out}
@keyframes evflash{0%,35%{box-shadow:0 0 0 3px var(--gold)}100%{box-shadow:0 0 0 3px transparent}}
/* Mở bằng link 🎧 Nghe (listenCue): nháy cái cần chạm để nghe, hai lần, cùng ánh vàng với thẻ sự kiện. */
.cue{animation:evflash 2.4s ease-out 2}
@media (prefers-reduced-motion:reduce){.ev-card.flash{animation:none;box-shadow:0 0 0 3px var(--gold)}}
.ev-card .ev-when{padding-right:2.3rem}
.ev-fold{position:absolute;top:.45rem;right:.45rem;z-index:1;width:2.1rem;height:2.1rem;padding:0;
  border:1px solid var(--line);border-radius:50%;background:var(--paper);color:var(--pine);
  display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
.ev-fold svg{width:1.15rem;height:1.15rem}
/* Gập/mở thẻ tự giữ chỗ bằng evKeepPlace() trong app.js; tắt cách giữ chỗ riêng của Chrome để hai
   cách không cùng bù một lần. */
#v-calendar{overflow-anchor:none}
.cal-hrow{display:flex;align-items:baseline;justify-content:space-between;gap:.6rem}
/* Ba tab của trang Lịch: một thanh chia ba ô bằng nhau, ô đang chọn tô màu thông. */
.cal-tabs{display:grid;grid-template-columns:repeat(3,1fr);gap:0;margin:.35rem 0 0;padding:2px;
  border:1px solid var(--line);border-radius:999px;background:var(--card)}
.cal-tabs button{border:0;background:none;color:var(--muted);font:inherit;font-weight:600;font-size:.92em;
  padding:.3rem .4rem;border-radius:999px;cursor:pointer;min-height:2.1rem;
  display:inline-flex;align-items:center;justify-content:center;gap:.35rem;white-space:nowrap}
.cal-tabs button svg{width:1.1em;height:1.1em;flex:none}
.cal-tabs button[aria-selected="true"]{background:var(--pine);color:var(--card)}
#v-calendar[data-tab="week"] .cal-sec:not([data-sec="week"]),
#v-calendar[data-tab="later"] .cal-sec:not([data-sec="later"]),
#v-calendar[data-tab="month"] .cal-sec:not([data-sec="month"]){display:none}
.cw-acts{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center;justify-content:flex-end;margin:.5rem 0 .2rem}
/* Hai nút (Chia sẻ bản tin · Báo cả hội thánh) luôn cùng một hàng: không cho rớt hàng, mỗi nút co
   được tới nửa hàng; chật quá (320px, chữ lớn) thì chữ trong nút xuống dòng chứ nút không rớt. */
.cw-acts{flex-wrap:nowrap}
.cw-ib{display:inline-flex;align-items:center;justify-content:center;gap:.35rem;flex:0 1 auto;min-width:0;
  white-space:normal;text-align:left;line-height:1.2;padding-left:.75rem;padding-right:.75rem}
.cw-ib svg{width:1.05em;height:1.05em;flex:none}
.cw-ib.rung,#wk-ring.rung,#dev-ring.rung{opacity:.55}
.cal-hrow .ev-more,.cal-subrow .ev-more{padding:0;display:inline-flex;align-items:center;gap:.3rem}
.cal-hrow .ev-more svg,.cal-subrow .ev-more svg{width:1.1em;height:1.1em;flex:none}
/* Nút gập/mở tất cả của khúc Tuần này đứng riêng một dòng dưới tựa: tựa đã có khoảng ngày và hai
   mũi tên lật tuần, thêm nút nữa thì không vừa 390px. */
.cal-subrow{display:flex;justify-content:flex-end;margin:-.15rem 0 .1rem}
.ev-more{border:0;background:none;padding:.3rem 0 0;color:var(--pine);font:inherit;font-size:.88em;
  font-weight:600;cursor:pointer;text-decoration:underline;text-underline-offset:2px}
/* "đã báo" là một ghi chú nội bộ cho ban chăm sóc và người đăng, không phải một lời cảnh
   báo — nên nó lặng như dấu vai "thành viên", chứ không vàng lên như dấu bài đã gỡ. */
.tag.rung{background:transparent;color:var(--muted);border:1px solid var(--line)}
/* Một buổi đi qua nhiều chỗ. Giờ đứng thành một cột riêng bề ngang cố định để các dòng thẳng
   hàng nhau — mắt dò xuống một cột số thì nhanh hơn dò qua một đoạn chữ. Chỗ chưa biết mấy giờ
   vẫn giữ chỗ trống ấy, nên tên chỗ của nó không thụt ra trước mấy chỗ kia. */
.ev-stops{list-style:none;margin:.1rem 0 .45rem;padding:0;display:grid;gap:.45rem}
.ev-stops li{display:flex;gap:.55rem;align-items:baseline}
.ev-stops b{flex:none;min-width:3.6rem;color:var(--pine);font-size:.9em}
.ev-stops span{min-width:0;overflow-wrap:anywhere;display:block}
/* Địa chỉ nhạt hơn và xuống dòng riêng: tên chỗ là thứ nhận ra, địa chỉ là thứ tra khi cần.
   Nó là một đường dẫn mở bản đồ, nên phải TRÔNG như bấm được — gạch chân mờ, và đủ cao để ngón
   tay chạm trúng (mấy dòng địa chỉ nằm sát nhau trong danh sách chỗ gặp). Giữ màu nhạt chứ
   không tô xanh như mọi link khác: tô lên thì ba dòng địa chỉ hút hết mắt khỏi ba cái tên chỗ,
   mà tên chỗ mới là thứ người ta dò tìm trước. */
.ev-addr{display:block;font-style:normal;color:var(--muted);font-size:.9em;
  text-decoration:underline;text-decoration-color:var(--line);text-underline-offset:.18em;
  padding:.15rem 0}
.ev-addr.indent{margin-left:1.5rem}
/* Địa chỉ đứng một mình (không có tên chỗ) thì nó LÀ chỗ gặp: đứng cạnh 📍, cỡ chữ thường. */
.ev-addr.solo{display:inline;font-size:1em;color:inherit;padding:0}
@media (hover:hover){.ev-addr:hover{color:var(--pine);text-decoration-color:var(--pine)}}

/* Một dòng nhập chỗ gặp: ngày và giờ chung hàng trên, tên chỗ rồi địa chỉ mỗi thứ một hàng
   trọn bề ngang. Địa chỉ ở đây là địa chỉ thật — "880 Sanford Nicholson Rd, Nicholson, GA
   30565" — nên nhét nó chung hàng với hai ô kia là bắt gõ mù.
   Vạch dọc bên trái gom cả khối lại thành MỘT chỗ gặp; không có nó thì bốn ô của hai chỗ liền
   nhau đọc ra thành tám thứ rời rạc. */
.ev-stoplist{display:grid;gap:.15rem}
.ev-stoprow{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center;margin:.55rem 0;
  border-left:2px solid var(--line);padding-left:.6rem}
/* KHÔNG đặt bề ngang cứng cho ô ngày và ô giờ: chúng rộng bao nhiêu là do trình duyệt và ngôn
   ngữ của MÁY quyết định — Chrome ở lối 12 giờ còn phải chứa thêm "AM/PM" và một cái đồng hồ
   bấm được, mà một con số rem đoán bừa thì cắt cụt đúng cái chữ ấy (đo bằng ảnh chụp thật ở
   390px: 7.5rem cắt mất "AM"). Để trình duyệt tự đo, chỉ cấm nó co lại. */
.ev-stoprow input[type=date],.ev-stoprow input[type=time]{flex:none;width:auto;min-width:0}
.ev-stoprow .s-name,.ev-stoprow .s-addr{flex:1 1 100%;min-width:0}
.ev-stoprow .eb{flex:none;width:2.2rem;height:2.2rem;margin-left:auto}
/* Dòng đầu không có nút xoá — nó mang ngày của chính sự kiện. */
.ev-stoprow.first{border-left-color:var(--pine)}
/* Thẻ sự kiện: ngày giờ ở trên bằng chữ nhỏ, tên sự kiện là thứ đọc trước. */
.ev-when{font-size:.85em;color:var(--pine);font-weight:700;margin:0 0 .15rem}
/* Đầu thẻ: nhãn ngày kiểu tờ lịch bên trái (evBadge), bên phải là dòng icôn · giờ rồi tới tên việc. */
.ev-top{display:flex;gap:.75rem;align-items:flex-start}
.ev-tb{flex:1;min-width:0}
.ev-tb h3{margin-bottom:0}
.ev-card:not(.shut) .ev-top{margin-bottom:.4rem}
.ev-dt{font-weight:700}
.ev-badge{flex:none;width:3.4rem;display:flex;flex-direction:column;align-items:center;text-align:center;
  border:1px solid var(--line);border-radius:12px;background:var(--paper);padding:.25rem .15rem .3rem;line-height:1.1}
.ev-badge.span{width:4.8rem}
.ev-badge .bd-wd{font-size:.72em;font-weight:700;color:var(--wd,var(--pine));white-space:nowrap}
.ev-badge .bd-d{font-size:1.55em;font-weight:600;color:var(--ink);letter-spacing:-.02em;white-space:nowrap}
.ev-badge.span .bd-d{font-size:1.2em;padding:.12rem 0}
.ev-badge.span .bd-wd{font-size:.62em;letter-spacing:-.01em}
.ev-badge .bd-m{font-size:.68em;color:var(--muted);white-space:nowrap}
.ev-badge.off{opacity:.55}
.ev-badge.off .bd-d{text-decoration:line-through}
/* Dấu buổi trong ngày và online/trực tiếp, đứng đầu dòng ngày giờ (evMetaHtml). */
.ev-meta{display:inline-flex;gap:.25rem;margin-right:.4rem;vertical-align:-.2em}
.ev-mi{width:1.15em;height:1.15em;flex:none}
.ev-mi.morning,.ev-mi.afternoon{color:#C98A0B}
.ev-mi.evening{color:#4F5BB0}
.ev-mi.online{color:#2563EB}
.ev-mi.inperson{color:var(--pine)}
.ev-mi.repeat{color:#8B5CF6}
.ev-mi.once{color:var(--muted)}
/* Màu theo thứ: vạch ở mép trái thẻ. box-shadow chứ không border-left, để vạch đi theo góc bo. */
.ev-card[data-wd]{box-shadow:inset 5px 0 0 var(--wd)}
[data-wd="0"]{--wd:var(--wd0)} [data-wd="1"]{--wd:var(--wd1)} [data-wd="2"]{--wd:var(--wd2)}
[data-wd="3"]{--wd:var(--wd3)} [data-wd="4"]{--wd:var(--wd4)} [data-wd="5"]{--wd:var(--wd5)}
[data-wd="6"]{--wd:var(--wd6)}
/* Tựa Bài giảng và Tĩnh nguyện có nhãn ngày tờ lịch bên trái (dayBadge). */
.ttl-row{display:flex;gap:.75rem;align-items:center}
.ttl-row > h1,.ttl-row > h2{flex:1;min-width:0;margin:0}
.ttl-row > div:empty{display:none}
.ttl-row{margin:.2rem 0 .5rem}
.ev-card h3{margin:0 0 .2rem}
/* Dòng "mỗi Chủ nhật · buổi 3 trên 12" (migration 051). Nhạt hơn dòng ngày ở trên và KHÔNG đậm:
   dòng ngày là thứ phải đọc, dòng này là thứ để yên tâm — đậm cả hai thì không dòng nào còn đậm.
   Dấu ↻ không tách riêng bằng thẻ nào, nên nó thừa hưởng luôn màu và cỡ chữ ở đây. */
.ev-rep{font-size:.82em;color:var(--muted);margin:0 0 .3rem}
.ev-body{white-space:pre-wrap;margin:.4rem 0 0;overflow-wrap:anywhere}
/* "Ai mang gì" (091): danh sách món cần mang, ngay dưới bảng tin. Món đã đủ thì nhạt đi và có dấu ✓,
   món còn thiếu giữ màu chữ đậm — mắt người lướt qua phải dừng ở chỗ còn thiếu. */
.ev-bring{margin:.7rem 0 0;padding:.6rem .7rem;border:1px solid var(--line);border-radius:12px;background:var(--paper)}
.ev-bring.empty{padding:0;border:0;background:none}
.ev-bring.empty .btn{font-size:.9em;padding:.4rem .8rem}
.ev-bh{margin:0 0 .35rem;font-weight:600}
.ev-bh span{font-weight:400;color:var(--muted);font-size:.9em}
.ev-bring ul{list-style:none;margin:0;padding:0}
.ev-bring li{padding:.45rem 0;border-top:1px solid var(--line)}
.ev-bring li:first-child{border-top:0}
.ev-bl{display:flex;align-items:center;gap:.45rem;flex-wrap:wrap}
.ev-bl b{font-weight:600;overflow-wrap:anywhere;flex:1 1 8rem;min-width:0}
.ev-bq{flex:none}
.ev-bq{font-size:.85em;color:var(--muted);font-variant-numeric:tabular-nums}
.ev-bring li.full .ev-bl b{color:var(--muted);font-weight:500}
.ev-bme{width:auto;height:auto;min-height:2.4rem;padding:0 .85rem;margin-left:auto;white-space:nowrap;font-size:.9em;color:var(--pine);border-color:var(--pine)}
.ev-bme.on{background:var(--pine-soft)}
.ev-bfull{margin-left:auto;font-size:.88em;color:var(--pine);white-space:nowrap}
.ev-bdel{flex:none;border:0;background:none;color:var(--muted);font-size:1.15em;line-height:1;padding:.2rem .35rem;min-width:1.8rem;min-height:1.8rem}
.ev-bw{margin:.2rem 0 0;font-size:.86em;color:var(--muted);overflow-wrap:anywhere}
/* Số lượng hẹp bên trái, ghi chú lấp phần còn lại; ô thêm món thì ngược lại (tên món trước). */
.ev-bform{display:grid;grid-template-columns:4.5rem minmax(0,1fr);gap:.4rem;margin-top:.45rem}
.ev-bform.add{grid-template-columns:minmax(0,1fr) 4.5rem}
.ev-bform input.wide{grid-column:1/-1}
/* Công tắc nội bộ/công khai (092) và dấu người ngoài. */
.ev-bpub{display:flex;align-items:center;gap:.4rem;width:100%;text-align:left;border:1px dashed var(--line);background:var(--card);
  border-radius:10px;padding:.4rem .6rem;margin:0 0 .45rem;font:inherit;font-size:.86em;color:var(--muted)}
.ev-bpub.on{border-style:solid;border-color:var(--gold);background:var(--gold-soft);color:var(--ink)}
.ev-bpub span{margin-left:auto;color:var(--pine);font-weight:500;white-space:nowrap}
.ev-bp{overflow-wrap:anywhere}
.ev-bp a{color:var(--pine)}
.ev-bg{font-size:.85em;border:1px solid var(--line);border-radius:999px;padding:0 .4rem}
.ev-bdel.sm{min-width:1.6rem;min-height:1.6rem;font-size:1em;padding:0 .2rem;vertical-align:middle}
.ev-bform input{width:100%;min-width:0;padding:.45rem .6rem}
.ev-bbtns{grid-column:1/-1;display:flex;gap:.4rem;justify-content:flex-end}
.ev-bform .btn{padding:.4rem .8rem;font-size:.9em}
.ev-bform.add{margin-top:.6rem}
.ev-badd{display:block;border:0;background:none;color:var(--pine);font:inherit;font-size:.9em;padding:.4rem 0 0;font-weight:500}
/* Bảng tin của một lớp (052): cái nhãn "Lớp học" rồi tới tên người dạy. Nhãn và tên đứng CÙNG một
   dòng, không phải hai: dòng ngày ở trên và cái tựa đã chiếm hai dòng đầu của thẻ, và một thẻ lịch
   phải đọc hết được bằng một cái liếc mắt. */
.ev-class{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;margin:.1rem 0 .35rem}
.ev-class .note{font-size:.86em}
/* Nút "Xem thêm" KHÔNG nằm trong hàng icôn ở đáy thẻ: hàng ấy là những việc LÀM VỚI bảng tin (báo
   cho hội thánh, sửa, gỡ, xoá), còn cái này chỉ mở/gập phần giới thiệu lớp. Để lẫn vào đó thì nó
   thành cái nút thứ bảy trong một hàng icôn không nhãn. */
.ev-classgo{margin:.5rem 0 0}
.ev-cfacts{margin:.5rem 0 0}
/* Lời giới thiệu lớp mở ra trong thẻ lịch: cùng .art-body với trang Lớp học, nhưng sát lên dòng
   "Lớp học" hơn — trong một thẻ, lề .9rem của một trang đọc là một khoảng hở. */
.ev-about{margin:.4rem 0 0}
.ev-classgo .btn{padding:.3rem .65rem;font-size:.88em}
/* Dòng "ai đăng": khuôn mặt rồi tới tên. Một cái tên chữ là một cái tên; một khuôn mặt là một
   người mình đã ngồi cạnh hôm Chủ nhật. */
.ev-who{display:flex;align-items:center;gap:.45rem;margin:.55rem 0 0}
.ev-ava{flex:none;width:1.6rem;height:1.6rem;border-radius:999px;display:grid;place-items:center;
  background:var(--c-accent);color:#fff;font:600 .6em/1 var(--sans);user-select:none}
/* Bài của người khác: CẢ DÒNG là cái nút mở cuộc trò chuyện riêng, không riêng ô hình — ô hình
   rộng 1.6rem, chừng 27px, dưới mức ngón tay với tới được. Lề âm kéo vùng bấm ra sát mép chữ
   để nó không trông như bị thụt vào so với mấy dòng trên. */
.whobtn{display:inline-flex;align-items:center;gap:.45rem;border:0;background:none;text-align:left;
  padding:.4rem .6rem .4rem .25rem;margin:-.4rem 0 -.4rem -.25rem;border-radius:999px}
.whobtn .note{text-decoration:underline;text-decoration-color:var(--line);text-underline-offset:2px}
@media (hover:hover){.whobtn:hover{background:var(--c-accent-soft)}
  .whobtn:hover .note{text-decoration-color:var(--c-accent)}}
/* Bài đã gỡ: nền vàng nhạt, không phải gạch ngang như .done — nó chưa xong, nó bị dừng lại. */
.ev-card.down{background:var(--gold-soft);border-color:var(--gold)}
.ev-down{margin:.4rem 0 0;font-size:.85em}
/* Việc đã hủy (057): ngày gạch ngang, dấu "đã hủy" đỏ gạch như nút Xóa, và lời hủy đứng đậm —
   nó là câu duy nhất trên thẻ người ta cần đọc. */
.ev-card.cx .ev-when s{color:var(--muted)}
.cxmark{display:inline-flex;vertical-align:-.2em;margin-left:.4rem;color:#b04a3a}
.cxmark svg{width:1.3em;height:1.3em}
/* Lời hủy: chữ thường, không in đậm — đậm cả đoạn sáu dòng thì không còn chỗ nào nổi lên nữa.
   Cái làm nó nổi là khung: viền đỏ bên trái và nền đỏ rất nhạt, kiểu một lời báo dán lên. */
.ev-cx{margin:.35rem 0 .55rem;padding:.55rem .8rem;border-left:3px solid #b04a3a;border-radius:0 10px 10px 0;
  background:color-mix(in srgb,#b04a3a 7%,var(--card));white-space:pre-line;line-height:1.5}
/* Ô hủy trong thẻ (058): một đường kẻ tách nó khỏi phần đọc ở trên, để không ai tưởng hai ô
   chữ là một phần của bảng tin. */
.ev-cxform{border-top:1px solid var(--line);margin-top:.6rem;padding-top:.5rem}
.ev-cxh{margin:0 0 .2rem;font-weight:600;color:#b04a3a}
/* Hủy một buổi hay cả chuỗi (082): hai dòng chọn, chữ to bằng chữ thường để ngón tay trúng. */
.ev-cxpick{display:grid;gap:.35rem;margin:.3rem 0 .5rem}
.ev-cxpick label{display:flex;gap:.5rem;align-items:flex-start;justify-content:flex-start;text-align:left;line-height:1.35;margin:0}
.ev-cxpick input{width:auto;flex:none;margin:.2em 0 0;padding:0;accent-color:#b04a3a}
.ev-cxpick span{flex:1;min-width:0}
/* Dòng "nghỉ 10/10" trên thẻ của buổi kế tiếp: cùng màu lời hủy, nhỏ hơn — nó giải thích vì sao
   buổi này rơi vào ngày này, không phải tin chính của thẻ. */
.ev-skip{margin:.1rem 0 .4rem;font-size:.86em;color:#b04a3a;white-space:pre-line}
.ev-cxform .btn.danger{background:#b04a3a;color:#fff}

/* ---- thu tiếng / quay video ngay trong ô của buổi sáng ---- */
/* Khung xem trước lúc đang quay, và khung nghe lại sau đó. Thấp hơn khung của bài đã đăng: ở đây
   mục sư cần thấy CẢ nút "Xong" trong cùng một tầm mắt, đây không phải lúc để ngắm khung hình. */
.dev-prev{width:100%;max-height:44vh;margin:0 0 .5rem;border-radius:14px;display:block;background:#000}
/* Dòng "Đang thu · 1:12". Cùng lối với thanh ghi âm bên Kết nối, nhưng thanh đó có CSS gắn chặt
   vào .chatbar nên không dùng nhờ được — và nó nằm đáy màn hình, còn dòng này nằm trong một ô
   của lưới bảy buổi sáng. */
.dev-recline{display:flex;align-items:center;gap:.4rem;margin:.2rem 0 .5rem}
.dev-recline .dot{width:.6rem;height:.6rem;border-radius:50%;background:#B04A3A;flex:none}
.dev-recline b{font-weight:600;color:#B04A3A}
/* Tạm dừng thì đỏ tắt đi. Màu là thứ duy nhất nói được "đang thu" hay "đã dừng" từ khoảng cách
   một cánh tay, khi chữ còn chưa đọc tới. */
.dev-blank.paused .dev-recline .dot{background:var(--muted)}
.dev-blank.paused .dev-recline b{color:var(--muted)}
/* Hai nút thu đứng ngang nhau và rộng bằng nhau: không cái nào là lựa chọn phụ. */
.dev-take{margin-top:.2rem}
.dev-take .btn{flex:1 1 auto}

/* Ba cách soạn một buổi sáng. Mỗi cách là một ô bấm được cả khối, có tựa và một dòng nói nó
   làm gì — chứ không phải ba cái nút xếp hàng ngang. Ba cái nút cạnh nhau bắt người đọc suy
   ra sự khác nhau từ hai chữ trên mặt nút, mà khác nhau ở đây là thật: một cái tiêu tiền và
   mất nửa phút, một cái đòi cầm điện thoại lên nói, một cái mở ra trang trắng. */
.ways{display:grid;gap:.4rem;margin-top:.5rem}
.way{text-align:left;border:1px solid var(--line);background:var(--card);border-radius:14px;
  padding:.6rem .8rem;display:grid;gap:.1rem;font:inherit;color:inherit;width:100%}
.way b{font-weight:600}
.way .note{margin:0}
.way:disabled{opacity:.55;cursor:default}

/* ---- hồ sơ và sinh nhật ---- */
/* Số điện thoại đứng riêng bên phải, cách tên một khoảng: nó là cái duy nhất trong hàng này
   bấm được, nên đừng để nó lẫn vào dòng chữ ngày tháng ngay cạnh. */
.bday{display:flex;align-items:center;justify-content:space-between;gap:.8rem}
.bday .t{min-width:0;display:grid;gap:.1rem}
.bday .t b{font-family:var(--serif);font-weight:600}
.bday a.btn{flex:none;text-decoration:none;color:var(--pine);white-space:nowrap}

/* ---- nhóm trò chuyện ---- */
/* Phần này khác hẳn phần còn lại của app về nhịp: bài giảng và tĩnh nguyện là để ĐỌC, cuộn
   chậm, chữ serif, dòng thưa. Ở đây người ta liếc, gõ, gửi, rồi cất máy đi. Nên chữ sans, bong
   bóng sát nhau, và mọi thứ đáng chạm đều nằm trong tầm ngón cái. */

/* Chỗ cho thanh soạn tin nằm đè ở đáy — cùng cách tính với #v-ask và --composer-h. */
#v-chat.on{padding-bottom:var(--chatbar-h,1rem)}

/* ---- màu của nhóm ---- */
/* Người quản nhóm chọn một trong sáu màu cho nhóm mình. Không phải để trang trí: một người ở
   trong năm sáu nhóm mà nhóm nào cũng một màu thì nhắn lộn nhóm là chuyện xảy ra thật — xem đầu
   migration 018. Màu là thứ mắt nhận ra xong trước khi đọc kịp tên nhóm ở đầu phòng.

   Mỗi màu khai đúng MỘT dòng: góc màu, độ tươi, độ đậm. Bốn biến thật ở dưới dựng ra từ ba số
   đó, nên chế độ tối chỉ phải viết một lần cho cả sáu màu thay vì sáu lần. Khai thẳng mã màu
   cho từng cái thì mỗi màu mới là sáu dòng, và sẽ có ngày ba trong sáu dòng đó bị quên.

   'pine' KHÔNG có trong danh sách này, và đó là chủ ý: nó là bộ màu app, tức là "không tô gì
   cả". App bỏ hẳn thuộc tính data-chat-theme cho nhóm màu pine (paintChatTheme trong app.js),
   nên những nhóm chưa chọn màu đi đúng đường cũ, không qua một dòng nào ở đây.

   Sáu khoá này phải trùng khít với ràng buộc chat_groups_theme_ok của 018 và mảng THEMES trong
   app.js. Ba chỗ, một danh sách. */
[data-chat-theme="dusk"] {--th:214;--ts:42%;--tl:32%}   /* xanh trời chiều */
[data-chat-theme="plum"] {--th:288;--ts:26%;--tl:33%}   /* tím mận */
[data-chat-theme="clay"] {--th:14; --ts:46%;--tl:36%}   /* đất nung */
[data-chat-theme="olive"]{--th:92; --ts:34%;--tl:27%}   /* xanh ô liu */
[data-chat-theme="honey"]{--th:34; --ts:52%;--tl:30%}   /* nâu mật */

/* Bong bóng của mình giữ một màu đậm ở CẢ hai chế độ, y như --user hôm nay: chữ trong đó là #fff
   ghim cứng, nên cái nền mà sáng lên trong chế độ tối là chữ trắng trên nền nhạt. Đã đo: chữ
   trắng trên cả sáu nền đậm được 7.0–8.7, còn bản mặc định hôm nay là 7.45. Tính cả những chỗ
   khác mà chữ nằm lên màu — tên người nói, nút gửi, nền cảm xúc, hai chế độ sáng tối — thì chỗ
   thấp nhất của cả sáu màu là 5.4, còn mức đọc được của WCAG AA là 4.5. */
[data-chat-theme]{
  --c-mine:hsl(var(--th),var(--ts),var(--tl));
  --c-them:hsl(var(--th),32%,93%);
  --c-accent:hsl(var(--th),var(--ts),var(--tl));
  --c-accent-soft:hsl(var(--th),36%,92%);
}
/* Chế độ tối: bong bóng người khác tối lại và nhạt màu đi (cùng họ với --bot tối, #232B31), còn
   màu nhấn thì phải SÁNG lên mới đọc được trên nền than — đúng cách --pine đang đổi từ #1F5F52
   sang #7FC3AE. Hai khối cùng một nội dung, cùng khuôn với bảng màu ở đầu file. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) [data-chat-theme]{
    --c-them:hsl(var(--th),14%,18%);
    --c-accent:hsl(var(--th),42%,68%);
    --c-accent-soft:hsl(var(--th),26%,16%);
  }
}
:root[data-theme="dark"] [data-chat-theme]{
  --c-them:hsl(var(--th),14%,18%);
  --c-accent:hsl(var(--th),42%,68%);
  --c-accent-soft:hsl(var(--th),26%,16%);
}

/* Ô chọn màu ở trang cài đặt nhóm. Mỗi ô tự mang màu của chính nó (data-chat-theme trên từng cái
   nút), nên hạt màu bên trong là màu THẬT sẽ thấy trong phòng, không phải một mã màu chép tay
   lần thứ hai. Hạt chia hai nửa: nửa trên là bong bóng của mình, nửa dưới là bong bóng người
   khác — đúng hai thứ mà màu này đổi. */
.themes{display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.2rem}
.swatch{border:1px solid var(--line);background:var(--card);color:inherit;border-radius:999px;
  padding:.25rem .7rem .25rem .3rem;display:inline-flex;align-items:center;gap:.4rem;font-size:.88em}
.swatch .bead{flex:none;width:1.5rem;height:1.5rem;border-radius:999px;border:1px solid var(--line);
  background:linear-gradient(135deg,var(--c-mine) 50%,var(--c-them) 50%)}
/* Đang chọn: viền vàng và nền vàng nhạt, cùng lối với nút chữ lớn ở đầu trang. */
.swatch[aria-pressed="true"]{border-color:var(--gold);background:var(--gold-soft);font-weight:600}
/* "Xanh thông" là bộ màu app nên ô mẫu của nó không mang data-chat-theme — mà lúc body đang mang
   màu của một nhóm thì cả trang thừa hưởng màu đó, kể cả ô mẫu này. Trỏ thẳng về bộ màu gốc để
   hạt màu của nó luôn nói thật. */
.swatch[data-pick="pine"]{--c-mine:var(--user);--c-them:var(--bot)}

/* Chữ bấm được mà không phải là nút: đường quay lại, "xem những tin trước đó". Cùng lối với
   "Quên mật khẩu?" ở trang đăng nhập — một lối đi, không phải một lời mời. */
.linkish{border:0;background:none;color:var(--pine);text-decoration:underline;font:inherit;padding:.2rem 0}

/* Một nhóm một hàng, cả hàng bấm được. Không phải cái thẻ có nút bên trong: ngón tay cái cầm
   điện thoại một tay thì cả hàng là đích ngắm dễ nhất. */
#cg-list,.glist{display:grid;gap:.5rem;margin:.65rem 0}
/* Tiêu đề của một khúc trong hộp thư (Lớp học · Nhóm · Nhắn riêng). Nhỏ và nhạt hơn một cái tựa
   thật: nó chia danh sách ra chứ không xin ai chú ý, và cái phải đọc vẫn là tên cuộc trò chuyện
   ngay dưới nó. Lề trên lớn hơn lề dưới để mỗi tiêu đề dính vào khúc của mình, không lửng lơ
   giữa hai khúc. Khúc đầu tiên không cần khoảng trống ở trên. */
.seclab{display:flex;align-items:center;gap:.4rem;margin:.75rem 0 -.1rem;font:600 .82em/1.2 var(--sans);
  text-transform:uppercase;letter-spacing:.06em;color:var(--pine)}
.seclab:first-child{margin-top:.1rem}
.seclab .kind{height:.95em;opacity:.9}
.seclab .note{font-size:1em;letter-spacing:0;opacity:.75}
/* Việc đứng cuối hàng tiêu đề khúc ("＋ Nhóm mới", "Tìm trong danh bạ"): đẩy sang phải, và
   không mặc chữ in hoa của tiêu đề. */
.seclab .secact{margin-left:auto;text-transform:none;letter-spacing:0;font-weight:500;font-size:1.05em}
.seclab .secact .linkish{padding:0;text-align:right}
.seclab .secact .btn{font-size:.9em}
/* Dòng "Tìm một người trong danh bạ" đứng cuối khúc Nhắn riêng. */
.secmore{margin:.15rem 0 .2rem}
/* Hai cột: hình đại diện, rồi mới tới chữ. Hình chiếm cả hai hàng bên phải nó (tên, và dòng
   phụ), nên nó đứng giữa hàng dù dòng phụ có dài ra. */
.grow{text-align:left;border:1px solid var(--line);background:var(--card);border-radius:14px;
  padding:.7rem .9rem;display:grid;grid-template-columns:2.5rem 1fr auto;column-gap:.7rem;row-gap:.15rem;
  align-items:center;font:inherit;color:inherit}
.grow b{font-family:var(--serif);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.grow .gtop{grid-area:1/2;display:flex;align-items:center;gap:.5rem;min-width:0}
/* Dòng phụ KHÔNG BAO GIỜ được rớt xuống dòng thứ hai. Hàng này là một lưới hai cột, ô hình
   chiếm cả hai dòng bên trái; dòng chữ mà xuống hàng thì hàng thứ hai của nó thò ra dưới ô hình
   và cả cái thẻ trông như bị vỡ. Dài quá thì CẮT bằng dấu ba chấm — một hàng trong hộp thư là
   để lướt, ai cần đủ chữ thì bấm vào.
   Xếp ngang thay vì để chữ chảy tự do: mấy cái mặt người quản phải đứng giữa dòng chứ không
   ngồi theo đường chân chữ (vertical-align), và chỉ phần CHỮ mới co lại rồi cắt — cắt trúng
   một khuôn mặt thì thành nửa cái đầu. min-width:0 ở cả hai chỗ, nếu không thì theo mặc định
   của flexbox không ô nào chịu co, và dòng chữ lại đẩy cả cột ra. */
.grow > .note:not(.when){grid-area:2/2;min-width:0}
.grow .gsub{display:flex;align-items:center;min-width:0}
.grow .gsub .gtxt{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Giờ và số tin chưa đọc xếp thành một cột thứ ba, giờ trên số dưới — lối của mọi hộp thư, và
   là chỗ duy nhất chúng không đè lên nhau. Trước đây giờ nằm tuyệt đối ở góc phải còn con số
   thì chừa chỗ cho nó bằng một khoảng lề đo bằng tay; thêm cột hình đại diện vào là khoảng lề
   đó hụt đúng bằng bề ngang cái hình, và "9:07 AM" chồng lên con số 3. */
.grow .when{grid-area:1/3;justify-self:end;white-space:nowrap}
.grow .cnt{grid-area:2/3;justify-self:end}

/* Tròn là một người, vuông bo góc là một nhóm. Hình dáng làm việc phân biệt, không phải màu:
   màu ở đây là màu của chính cuộc trò chuyện đó (018), nên nó còn bận nói một chuyện khác —
   nhóm nào là nhóm nào. Ai không phân biệt được màu vẫn đọc được cái danh sách này.
   Chữ trắng trên --c-mine: đúng cặp màu của bong bóng "của mình" trong phòng, đã đo 7.0–8.7
   ở cả sáu màu lẫn hai chế độ sáng tối (xem ghi chú ở phần màu phòng chat). */
.grow .ava{grid-column:1;grid-row:1/3;width:2.5rem;height:2.5rem;display:grid;place-items:center;
  background:var(--c-mine);color:#fff;font:600 .82em/1 var(--sans);letter-spacing:.02em;
  border-radius:11px;user-select:none}
.grow .ava.one{border-radius:999px}
.grow .when{font-size:.78em}
/* Có tin chưa đọc: viền xanh và tên đậm. Không đổi nền cả hàng — một danh sách mà nửa số hàng
   đổi màu thì không còn cái nào nổi lên nữa. */
.grow.unread{border-color:var(--pine)}
/* Hàng nút lọc Tất cả · Nhắn riêng · Nhóm. Cùng khuôn với .swatch ở trang cài đặt nhóm: viên
   thuốc, viền vàng khi đang chọn. Một lối cho mọi hàng nút chọn trong app. */
.filters{display:flex;flex-wrap:wrap;gap:.45rem;margin:.65rem 0 0}
.chip{border:1px solid var(--line);background:var(--card);color:inherit;border-radius:999px;
  padding:.3rem .8rem;font:inherit;font-size:.88em}
.chip .note{font-size:.9em}
.chip[aria-pressed="true"]{border-color:var(--gold);background:var(--gold-soft);font-weight:600}
/* Hàng lọc của hộp thư: MỘT hàng, cuộn ngang khi chật. Năm cái chip không vừa bề ngang 390px, mà
   bọc xuống hàng thứ hai thì chúng ăn mất hai hàng màn hình của một danh sách vốn đã phải cuộn.
   Cùng lối với hàng chọn nhóm người đọc ở thẻ Bài giảng (.who): thanh cuộn giấu đi, và cái chip
   bị cắt ở mép phải tự nói rằng còn nữa. Lề dưới chừa chỗ cho viền vàng của chip đang chọn. */
.filters.oneline{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;padding-bottom:.15rem}
.filters.oneline::-webkit-scrollbar{display:none}
.filters.oneline .chip{flex:0 0 auto}
/* Ngôi sao trên một hàng trong hộp thư. Vàng của app, và đứng ngay sau cái tên — cùng chỗ mắt
   vừa dừng lại. Nhỏ hơn chữ tên: nó là một cái dấu, không phải một chữ phải đọc. */
.gstar{color:var(--gold);font-size:.85em;line-height:1;flex:none}
.grow.unread b{font-weight:700}


.room-head{display:flex;align-items:flex-start;justify-content:space-between;gap:.8rem;margin-bottom:.3rem}
.room-head h1{margin:0}
.room-t{min-width:0}
.room-t .note{margin:.1rem 0 0;overflow-wrap:anywhere}
/* Hai cái chip Bài học / Trò chuyện của một lớp. Lề hẹp hơn .filters ở mọi chỗ khác, và hẹp có
   lý do: phòng chat sống bằng chiều dọc (xem ghi chú ở .thread ngay dưới), mỗi rem ở đầu màn hình
   là một dòng tin nhắn bị đẩy xuống dưới thanh soạn tin. */
.cr-panes-row{display:flex;align-items:center;flex-wrap:wrap;gap:.45rem .8rem;margin:.2rem 0 .5rem}
/* Nhóm thường: không chip, không lối về — cả hàng biến đi, không để lại một khoảng lề trống. */
.cr-panes-row:not(:has(> :not([hidden]))){display:none}
#cr-panes{margin:0}
#cr-lesson-back{font-size:.9em;white-space:nowrap}
/* Màn điện thoại thường (360–400px): chip gầy bớt một chút để lối "← Tất cả" vừa cùng hàng. Màn
   hẹp hơn nữa thì chip rớt xuống dòng dưới. */
@media (max-width:400px){#cr-panes .chip{padding:.3rem .62rem}.cr-panes-row{column-gap:.5rem}}
#cr-lesson-back[hidden]{display:none}
/* Hàng ảnh trong ô soạn bài học: mỗi tấm một ô nhỏ mang số của nó và một nút Bỏ. Số phải đọc được
   vì nó chính là cái dấu [[img:N]] đang nằm trong ô chữ — không thấy số thì người dạy không biết
   tấm nào đi với dấu nào. Cuộn ngang khi có nhiều tấm, để nó không đẩy nút Lưu xuống khỏi màn hình. */
/* Ba ô soạn dùng chung hàng ảnh này: bài học (#cl-img-list), ô mở lớp và ô cài đặt lớp (.clpics,
   xem migration 052). Một luật cho cả ba — mấy tấm hình ấy là cùng một thứ, và cùng một thứ trên
   màn hình thì phải nhìn ra là cùng một thứ. */
#cl-img-list,.clpics{display:flex;gap:.5rem;overflow-x:auto;padding:.4rem 0;scrollbar-width:none}
#cl-img-list::-webkit-scrollbar,.clpics::-webkit-scrollbar{display:none}
.clpic{flex:0 0 auto;display:grid;gap:.25rem;justify-items:center;width:5.2rem}
.clpic img{width:5.2rem;height:5.2rem;object-fit:cover;border-radius:10px;border:1px solid var(--line)}
.clpic b{font-size:.78em;color:var(--muted);font-weight:700}
.clpic .btn{padding:.2rem .45rem;font-size:.76em}
/* Tấm đã gửi lên mà dấu của nó không còn trong bài: lưu bài là nó rời đi. Viền vàng của app nói
   "còn một việc chưa xong ở đây", cùng thứ tiếng mà bài bị gỡ và nhóm tạm ngưng đang nói. */
.clpic.loose img{border-color:var(--gold);opacity:.6}
.cr-more{text-align:center}
.closed-note{border:1px dashed var(--line);border-radius:12px;padding:.6rem .8rem;text-align:center}

/* "Máy này đang nhắc cho ai" — một dòng dưới công tắc nhắc mỗi sáng. Bình thường nó là một câu
   xác nhận, nên nó đi cùng màu chữ phụ và không xin ai chú ý. Khi máy còn dính tài khoản người
   khác thì nó đổi sang màu vàng của app và dày lên: đó là lúc tin nhắn riêng của một người đang
   rơi xuống màn hình khóa của người khác, và câu đó phải được nhìn thấy chứ không phải được đọc
   nếu tình cờ đọc tới. Vàng chứ không đỏ — chưa có gì hỏng, chỉ có một việc cần một cú chạm. */
.whopush{font-size:.85em;color:var(--muted);margin:.5rem 0 0}

.whopush.warn{color:var(--ink);font-weight:600;background:var(--gold-soft);
  border:1px solid var(--gold);border-radius:12px;padding:.55rem .75rem}

/* Mạch tin nhắn. min-height để nhóm mới lập không hiện ra như một trang hỏng. */
.thread{display:flex;flex-direction:column;gap:.35rem;min-height:35vh;padding-bottom:.5rem}

/* Phòng chat sống bằng chiều dọc: mỗi rem ở đầu màn hình là một dòng tin nhắn bị đẩy xuống dưới
   thanh soạn tin. Nên đầu trang ở đây nhỏ hơn mọi trang khác — tên nhóm đọc một lần rồi thôi,
   còn tin nhắn thì đọc suốt buổi.
   Hai thứ KHÔNG bóp: cỡ chữ trong bong bóng tin nhắn, và nút bánh răng. Chữ tin nhắn là thứ
   người ta đọc lâu nhất, mà nhiều người ở hội thánh đọc màn hình điện thoại không còn dễ. Còn
   bánh răng thì nằm ngang hàng với tên nhóm, thu nhỏ nó không lấy lại được dòng nào, chỉ làm
   khó bấm. */
/* Hàng đầu phòng dính lại dưới đầu trang khi cuộn. Cuộn lên đọc lại tin cũ là chuyện làm suốt
   ngày, mà cuộn một cái là tên nhóm trôi mất — đang ở nhóm nào, và lối ra (mũi ‹) ở đâu, đều
   phải cuộn ngược lên trên cùng mới thấy. Bây giờ nó ở nguyên đó.
   top = lề an toàn + chiều cao đầu trang thật, do sizeHead() trong app.js đo và ghi vào
   --head-h: đầu trang cao bao nhiêu còn tuỳ chữ lớn hay màn hình hẹp (cụm nút rớt xuống hàng
   dưới). Đoán một con số cố định là hoặc chừa khe cho chữ lọt qua giữa hai thanh, hoặc đè mất
   dòng tên nhóm. z-index 4: dưới đầu trang (5), trên dòng tin nhắn.
   Lề âm hai bên kéo nền ra sát mép để không còn hai khe hở ở rìa cho tin nhắn trôi qua. Lề âm
   TRÊN (-1rem, đúng bằng lề trên của main) là để hai tư thế của nó trùng khít nhau: dính thì nó
   nằm sát đáy đầu trang, mà đứng yên ở đầu trang thì trước đây nó tụt xuống dưới cái lề 1rem
   kia — nên vừa chạm tay cuộn là cả thanh nhảy lên một nấc và thấp đi trông thấy. Giờ đứng đâu
   cũng sát đáy đầu trang, không nhảy, và đỡ được một nấc chiều cao trên màn hình điện thoại. */
#chat-room .room-head{align-items:center;gap:.35rem;margin:-1rem -1rem .25rem;
  position:sticky;top:calc(env(safe-area-inset-top,0px) + var(--head-h,3.4rem));z-index:4;
  background:var(--paper);border-bottom:1px solid var(--line);padding:.15rem 1rem}
/* Lề trên dưới của hàng này bóp xuống .15rem, nhưng hai cái nút vẫn 2.2rem như cũ: chiều cao
   hàng do NÚT quyết định chứ không do lề, nên bóp lề lấy lại được 6px mà không ai phải nhắm
   ngón tay khó hơn. Thu nút lại thì được thêm chừng ba pixel nữa, đổi bằng một mũi quay lại khó
   bấm cho người lớn tuổi — không đáng. */
#chat-room .backbtn{flex:none;border:0;background:none;color:var(--c-accent);width:2rem;height:2.2rem;
  padding:0;margin-left:-.4rem;display:inline-flex;align-items:center;justify-content:center}
#chat-room .backbtn svg{width:1.35rem;height:1.35rem;display:block}
/* Tên nhóm và số người nằm cạnh nhau trên cùng một dòng, cùng cắt bớt bằng dấu ba chấm khi
   chật. Nhóm nào có câu mục đích dài thì câu đó cụt ở đây — không sao, chỗ đọc nó là màn hình
   cài đặt nhóm, còn ở đây thứ phải luôn thấy là tên. */
#chat-room .room-t{flex:1;min-width:0;display:flex;align-items:baseline;gap:.35rem}
#chat-room .room-t h1{font-size:1.05em;margin:0;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#chat-room .room-t .note{font-size:.78em;margin:0;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#chat-room .room-t .note::before{content:"· "}
#chat-room .cr-more{margin:.15rem 0}
#chat-room .linkish{color:var(--c-accent)}
#chat-room .thread{gap:.3rem}
/* Khung cuộc trò chuyện chạy từ đáy thanh tên nhóm tới đỉnh thanh soạn tin, không tính hai
   thanh đó. Trước đây dưới luồng tin có 164px trống trong khi hai thanh chỉ chiếm 118px, mà
   lệnh cuộn của app (scrollIntoView block:"end") lại canh đáy tin cuối vào đáy MÀN HÌNH — tức
   là đẩy nguyên tin cuối xuống dưới hai thanh. Mở một nhóm ra là câu vừa nói không thấy đâu,
   phải tự vuốt ngược lên mới đọc được.
   scroll-margin-bottom dạy đúng lệnh cuộn ấy rằng đáy khung nằm ở đỉnh thanh soạn tin. Và
   trong phòng thì lề dưới của main nhường lại cho một con số đúng bằng hai thanh: không thừa
   một khoảng trắng để vuốt hụt, không thiếu một pixel để che mất chữ. */
body.in-room main{padding-bottom:0}
body.in-room #v-chat.on{padding-bottom:calc(var(--tabs-h,3.6rem) + var(--bar-h,0px))}
#chat-room .line{scroll-margin-bottom:calc(var(--tabs-h,3.6rem) + var(--bar-h,0px) + .4rem)}
.daysep{text-align:center;font-size:.78em;color:var(--muted);margin:.9rem 0 .3rem}

/* Một dòng tin = bong bóng + giờ đứng ngoài nó. Người nhận: bong bóng bên trái, giờ bên phải.
   Mình gửi: ngược lại. Trước đây giờ nằm trong bong bóng và chiếm trọn một hàng dưới câu chữ —
   một cuộc trò chuyện toàn câu ngắn ("hi", "em") thì gần nửa chiều cao là giờ. */
.line{display:flex;align-items:flex-end;gap:.35rem;max-width:92%;align-self:flex-start}
.line.mine{align-self:flex-end;flex-direction:row-reverse}
.line > .bub{flex:0 1 auto;min-width:0;max-width:100%}
.line > .stamp{flex:none;font-size:.66em;color:var(--muted);white-space:nowrap;padding-bottom:.25rem}

/* Chữ trong bong bóng nhỏ hơn chữ chung của app và dòng sát hơn, để một màn hình chứa được
   nhiều câu hơn. Ai đọc không nổi thì nút "A" trên đầu trang vẫn kéo cả app lên 21px. */
.bub{position:relative;background:var(--c-them);font-size:.9em;line-height:1.35;
  border-radius:16px;border-bottom-left-radius:4px;padding:.35rem .65rem;
  word-wrap:break-word;overflow-wrap:anywhere}
.bub.mine{background:var(--c-mine);color:#fff;border-bottom-left-radius:16px;border-bottom-right-radius:4px}
.bub.mine a{color:#fff;text-decoration:underline}
.bub .who{display:block;font-size:.78em;font-weight:700;color:var(--c-accent);margin-bottom:.1rem}
.bub .say{white-space:pre-wrap}
.bub .gone{opacity:.6}
/* Câu đang trả lời, dán ngay trên câu trả lời: không có nó thì một cuộc bàn việc nhiều nhánh
   đọc lại chẳng hiểu ai đang nói với ai. */
.bub .quote{display:block;border-left:3px solid var(--gold);padding-left:.5rem;margin-bottom:.3rem;
  font-size:.82em;opacity:.8}
.bub .quote b{display:block;font-weight:600}

/* Ảnh: bong bóng thu về đúng tấm ảnh, không có nền quanh nó.
   Khổ ảnh vừa phải. Một tấm chụp dọc bằng điện thoại để nguyên chiều cao thì chiếm gần trọn màn
   hình và đẩy hết những câu vừa nói ra khỏi tầm mắt — cuộc trò chuyện đứt làm đôi vì một tấm
   ảnh. Chừng này vẫn đủ nhận ra ảnh gì, còn muốn xem kỹ thì bấm vào là nó mở lớn hết màn hình. */
.bub.shotbub{padding:.25rem .25rem .2rem;background:var(--c-them)}
.bub.shotbub.mine{background:var(--c-mine)}
.shot{display:block;padding:0;border:0;background:none;line-height:0;cursor:zoom-in}
.shot img{display:block;max-width:min(100%,15rem);width:auto;max-height:40vh;border-radius:12px}
.bub audio{width:min(15rem,60vw);margin:.2rem 0;display:block}
.filechip{display:inline-flex;align-items:center;gap:.4rem;text-decoration:none;color:inherit;
  border:1px solid var(--line);border-radius:12px;padding:.4rem .7rem;background:var(--card);max-width:100%}
.bub.mine .filechip{background:rgb(255 255 255 / .12);border-color:rgb(255 255 255 / .3)}

/* Nút nhỏ trên bong bóng (cảm xúc, trả lời, thu hồi). Luôn hiện trên điện thoại — di chuột để
   hiện ra là một ý niệm của máy tính bàn, mà ở đây gần như không ai dùng máy tính bàn. */
/* Ba nút ☺ ↩ 🗑 chỉ hiện khi rê chuột lên bong bóng, hoặc khi chạm vào nó trên điện thoại.
   Bày sẵn trên MỌI bong bóng thì cột tin nhắn đầy những nút không ai bấm, mà chúng lại nổi lên
   trên và đè vào câu phía trên. pointer-events:none lúc ẩn: không thì cái nút vô hình vẫn nuốt
   cú chạm của người đang định chạm câu chữ. */
.bub .tools{position:absolute;top:-.55rem;right:.4rem;display:flex;gap:.15rem;
  opacity:0;pointer-events:none;transition:opacity .12s}
.bub.mine .tools{right:auto;left:.4rem}
@media (hover:hover){.bub:hover .tools{opacity:1;pointer-events:auto}}
.bub.show .tools{opacity:1;pointer-events:auto}
/* Trên bong bóng ẢNH thì hàng nút ở sẵn, không đợi rê chuột cũng không đợi chạm: chạm vào ảnh
   giờ là mở ảnh lớn, nên nó không còn là cách bật hàng nút như ở bong bóng chữ — nếu nút vẫn
   trốn thì trên điện thoại không còn đường nào thả tim hay lưu tấm ảnh về máy.
   Nút nằm ĐÈ lên góc ảnh chứ không nhô lên trên bong bóng như ở tin chữ: nhô lên thì nó che mất
   câu phía trên, mà hàng nút luôn hiện thì che suốt. */
.bub.shotbub .tools{top:.45rem;right:.45rem;left:auto;opacity:1;pointer-events:auto;gap:.2rem}
.tiny{border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:999px;
  width:1.55rem;height:1.55rem;font-size:.8em;line-height:1;padding:0;display:inline-flex;
  align-items:center;justify-content:center}
/* Nút đứng trên ảnh thì bỏ viền và thay bằng một bóng đổ: ảnh nào cũng có thể trắng đúng ngay
   cái góc ấy, và một cái viền xám nhạt trên nền trắng thì coi như không có viền. */
.bub.shotbub .tiny{border-color:transparent;background:rgb(255 255 255 / .94);color:#111;
  box-shadow:0 1px 5px rgb(0 0 0 / .35)}
.bub .reacts{display:flex;flex-wrap:wrap;gap:.2rem;margin-top:.25rem}
.react{border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:999px;
  padding:.05rem .4rem;font-size:.8em;display:inline-flex;align-items:center;gap:.15rem}
.react.on{border-color:var(--c-accent);background:var(--c-accent-soft)}
.react span{font-size:.9em;color:var(--muted)}
/* Bảng chọn cảm xúc mở ngay dưới bong bóng vừa chạm. Nổi lên trên, vì nó chỉ sống vài giây.
   Nó MỞ RA PHÍA TRONG MÀN HÌNH, không mở về phía mép gần nhất. Bong bóng của người khác nằm
   sát mép trái, nên nó neo mép trái của mình vào đó rồi dài sang phải; bong bóng của mình nằm
   sát mép phải thì ngược lại. Neo ngược — như lúc trước — thì một câu ngắn ("Hi") cho ra một
   bảng dài gấp ba bề ngang bong bóng, thò hẳn ra ngoài mép màn hình: 👍 và ❤️ bị cắt mất, còn
   người dùng thì không có cách nào kéo màn hình sang để với tới chúng.
   max-width là cái lưới đỡ cuối cùng cho cỡ chữ lớn: thà bảng xuống hai hàng còn hơn lọt ra
   ngoài mép. 4.5rem chừa lại đúng phần ô hình nhỏ đứng cạnh bong bóng.
   width:max-content LÀ BẮT BUỘC, không phải để cho đẹp. Bảng này position:absolute nên khung
   chứa nó là cái bong bóng, mà bề ngang một hộp absolute tự co theo khung chứa: dưới một bong
   bóng "Hi" rộng 70px thì bảng cũng chỉ được 70px. Trước đây không ai thấy vì không có
   flex-wrap — sáu cái emoji cứ thế tràn ra ngoài. Thêm flex-wrap vào mà quên chỗ này thì cái
   tràn ngang biến thành cái xuống hàng: sáu emoji xếp thành một cột dọc, mỗi cái một hàng.
   max-content bảo nó rộng đúng bằng sáu emoji nằm ngang, rồi max-width mới kẹp lại. */
.picker{position:absolute;z-index:6;top:100%;left:0;margin-top:.2rem;display:flex;gap:.1rem;
  flex-wrap:wrap;width:max-content;max-width:calc(100vw - 4.5rem);
  background:var(--card);border:1px solid var(--line);border-radius:999px;padding:.2rem .3rem;
  box-shadow:0 2px 12px rgb(0 0 0 / .18)}
.bub.mine .picker{left:auto;right:0}
.picker button{border:0;background:none;font-size:1.15em;padding:.15rem .25rem;border-radius:999px;line-height:1}
/* Ai đã thả cảm xúc. Cùng cái hộp nổi với bảng chọn cảm xúc, nhưng xếp dọc và là CHỮ chứ không
   phải nút — ở đây không có gì để bấm. Phải đặt lại màu chữ: hộp này nằm trong bong bóng, mà
   bong bóng của mình có chữ trắng, nên không đặt thì danh sách tên trắng trên nền trắng. */
/* Lớp tên là "names", KHÔNG phải "who": .bub .who đã là tên người nói trên đầu bong bóng, và
   dùng lại chữ đó ở đây thì bảng danh sách ăn nguyên display:block + chữ đậm + màu xanh của nó
   — hộp nổi xếp ngang hóa ra một cục chữ đậm. */
.picker.names{flex-direction:column;align-items:flex-start;gap:.15rem;border-radius:12px;
  padding:.35rem .6rem;font-size:.8em;line-height:1.3;white-space:normal;color:var(--ink);
  max-width:min(16rem,72vw)}
.picker.names b{font-weight:400;margin-right:.15rem}

/* Khung ảnh phóng lớn, do app tự dựng (openShot trong app.js). Không phải một tab mới: tab mới
   là đường dẫn có ký tên của kho riêng hiện nguyên trên thanh địa chỉ, và đường về thì phải mò
   nút lùi của trình duyệt. Ở đây chạm ra nền là đóng.
   z-index 8: trên đầu trang và thanh thẻ (5), dưới toast (9) — dòng "đã lưu ảnh" phải đọc được
   ngay cả khi tấm ảnh còn đang mở. */
.shotbox{position:fixed;inset:0;z-index:8;display:none;align-items:center;justify-content:center;
  background:rgb(0 0 0 / .92);
  padding:calc(env(safe-area-inset-top,0px) + 3.2rem) 1rem calc(env(safe-area-inset-bottom,0px) + 1rem)}
.shotbox.on{display:flex}
.shotbox img{max-width:100%;max-height:100%;object-fit:contain;border-radius:8px}
.shotbox-bar{position:absolute;top:calc(env(safe-area-inset-top,0px) + .7rem);right:.9rem;display:flex;gap:.5rem}
.shotbox-btn{border:0;border-radius:999px;background:rgb(255 255 255 / .92);color:#111;
  font:inherit;font-size:.85em;font-weight:500;padding:.45rem .85rem;line-height:1}
/* Ảnh mở lớn rồi thì trang phía sau đứng yên: không thì vuốt trên tấm ảnh lại kéo cuộc trò
   chuyện chạy mất, và đóng khung ra là đã lạc khỏi chỗ đang đọc. */
body.shot-open{overflow:hidden}

/* Ảnh & video (069, 071). Hàng chọn album: một dải viên cuộn ngang, không xuống hàng — hội thánh
   có ba chục album thì nó vẫn chỉ cao một dòng. */
.ph-bar{display:flex;gap:.4rem;overflow-x:auto;padding:0;margin:.35rem 0 0;scrollbar-width:none}
.ph-bar::-webkit-scrollbar{display:none}
.ph-chip{flex:none;border:1px solid var(--line);background:var(--card);color:inherit;border-radius:999px;
  padding:.4rem .8rem;font:inherit;font-size:.9em;white-space:nowrap;cursor:pointer}
.ph-chip[aria-pressed="true"]{background:var(--pine);border-color:var(--pine);color:#fff}
.ph-chip.add{color:var(--pine);border-style:dashed}
.ph-n{opacity:.7;font-size:.85em}
#ph-form{margin-top:.4rem}
.ph-h{margin:.25rem 0 0;font-size:1.15em}
.ph-sub{margin:0;font-size:.85em}
.ph-where{margin:.4rem 0 0;font-size:.85em}
.ph-day{margin:.8rem 0 .35rem;font-size:.85em;font-weight:600;color:var(--muted)}
.ph-day + .al-grid{margin-top:0}
.ph-older{display:block;margin:1rem auto 0}
/* Hàng nút icôn của thư viện: cùng nút tròn .eb của thẻ sự kiện. Nút chính (thêm ảnh, tải về) tô
   màu; nút đang mở (chia sẻ, ?) viền màu. Nút ? đứng mép phải, tách khỏi những nút làm việc. */
.ph-tools{display:flex;flex-wrap:wrap;gap:.35rem;align-items:center;margin-top:.5rem}
.ph-tools-gap{flex:1}
.eb.pri{background:var(--pine);border-color:var(--pine);color:#fff}
.eb.on{border-color:var(--pine);color:var(--pine);background:var(--pine-soft)}
.eb:disabled{opacity:.4;cursor:default}
/* Nút ? sát sau tựa "Ảnh & video": tựa thôi giãn hết hàng để nút đứng ngay cạnh chữ. */
#v-photos .phead h1{flex:none}
.eb.ph-headq{width:2rem;height:2rem}
.eb[hidden]{display:none}
.eb.ph-headq svg{width:1rem;height:1rem}
.ph-lead{margin:.2rem 0 .4rem;padding:.6rem .75rem;border:1px solid var(--line);border-radius:12px;background:var(--card);font-size:.95em}
.ph-help{margin:.6rem 0 0;padding:.6rem .75rem;border:1px solid var(--line);border-radius:12px;background:var(--card)}
.ph-help .ph-where{margin:.5rem 0 0}
/* Chú giải nút: icôn thu nhỏ đúng hình nút thật, rồi một câu. */
.ph-legend{list-style:none;margin:0;padding:0;display:grid;gap:.5rem}
.ph-legend li{display:flex;gap:.6rem;align-items:flex-start;font-size:.9em;line-height:1.4}
.ph-legend b{font-weight:600}
.eb.ph-li{width:1.9rem;height:1.9rem;margin-top:.05rem}
.eb.ph-li svg{width:.95rem;height:.95rem}
.ph-pickbar .eb,.ph-movebar .eb{flex:none}
.al-tile video{display:block;width:100%;height:100%;object-fit:cover;pointer-events:none}
/* Lọc lưới Tất cả / Ảnh / Video: ba nút liền một khối, nút đang chọn tô màu. */
.ph-kinds{display:inline-flex;flex:none;margin:0;border:1px solid var(--line);border-radius:999px;overflow:hidden;background:var(--card)}
#ph-body > .ph-kinds{margin-top:.6rem}
.ph-kind{border:0;background:none;color:inherit;font:inherit;font-size:.84em;padding:.3rem .7rem;cursor:pointer;white-space:nowrap}
.ph-kind + .ph-kind{border-left:1px solid var(--line)}
.ph-kind[aria-pressed="true"]{background:var(--pine);color:#fff}
.ph-kind[aria-pressed="true"] .ph-n{opacity:.85}
/* Chia sẻ album (077): hai lựa chọn dạng thẻ chọn, rồi ô link chỉ đọc và hàng nút. */
.ph-sharerow{margin-top:.3rem}
.ph-share{margin:.6rem 0 .2rem}
.ph-share h3{margin:0 0 .5rem;font-size:1em}
.ph-opt{display:flex;gap:.6rem;align-items:flex-start;padding:.55rem .65rem;margin:.35rem 0;
  border:1px solid var(--line);border-radius:12px;cursor:pointer}
.ph-opt.on{border-color:var(--pine);background:var(--pine-soft)}
.ph-opt input{width:auto;margin:.25rem 0 0;flex:none;accent-color:var(--pine)}
.ph-opt b{display:block;font-weight:600}
.ph-opt .note{display:block;margin:.1rem 0 0;font-size:.85em}
.ph-linkbox{margin-top:.5rem;font-size:.85em;color:var(--muted)}
.ph-pub{color:var(--pine);font-weight:600}
/* Chọn ảnh (để tải về hoặc xếp vào album): viền xanh và dấu ✓ trên mục đã chọn. */
.ph-pickbar{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center;margin-top:.5rem}
.ph-pickn{margin-right:auto;font-weight:600;font-size:.92em}
.al-check{position:absolute;top:.35rem;right:.35rem;width:1.5rem;height:1.5rem;border-radius:999px;
  border:2px solid #fff;background:rgb(0 0 0 / .25);color:#fff;font-size:.85em;font-weight:700;
  display:flex;align-items:center;justify-content:center;box-shadow:0 1px 3px rgb(0 0 0 / .4)}
.al-tile.picked{outline:3px solid var(--pine);outline-offset:-3px}
.al-tile.picked img{opacity:.85}
.al-tile.picked .al-check{background:var(--pine);border-color:var(--pine)}
/* Xếp những mục đang chọn vào album (075): ô chọn album giãn hết chỗ, nút đứng cuối hàng. */
.ph-movebar{display:flex;gap:.4rem;align-items:center;margin-top:.45rem}
.ph-movebar select{flex:1;min-width:0;width:auto}
.ph-drop{margin-top:1.4rem;justify-content:flex-end}
.al-info{margin:.4rem 0 0;font-size:.85em}
/* Dòng thông tin dưới tấm ảnh đang mở lớn: ai đăng, lúc nào, album nào. */
.shotbox-cap{position:absolute;left:1rem;right:1rem;bottom:calc(env(safe-area-inset-bottom,0px) + .9rem);
  margin:0;text-align:center;color:#fff;font-size:.85em;text-shadow:0 1px 4px rgb(0 0 0 / .8)}
.shotbox-cap[hidden]{display:none}
.shotbox:has(.shotbox-cap:not([hidden])){padding-bottom:calc(env(safe-area-inset-bottom,0px) + 3rem)}
/* Lưới trong một album: ba cột ô vuông, chỉ tải ảnh nhỏ; chạm mới mở ảnh lớn. */
.al-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;margin-top:.7rem}
@media (min-width:600px){.al-grid{grid-template-columns:repeat(4,1fr)}}
.al-tile{position:relative;aspect-ratio:1;padding:0;border:0;border-radius:8px;overflow:hidden;
  background:var(--bot);cursor:pointer;font:inherit}
.al-tile img{display:block;width:100%;height:100%;object-fit:cover}
.al-tile.wait{animation:al-wait 1.2s ease-in-out infinite alternate}
@keyframes al-wait{to{opacity:.5}}
.al-play{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  color:#fff;font-size:1.6em;text-shadow:0 1px 6px rgb(0 0 0 / .6)}
.al-acts{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:.5rem}
.al-busy{margin:.5rem 0 0}
.al-link{margin-top:.5rem}
.al-link .note{margin:.4rem 0 0;font-size:.85em}
.al-player{margin:.7rem 0 0}
.al-player iframe{display:block;width:100%;aspect-ratio:16/9;border:0;border-radius:10px;background:#000}
/* Video tải thẳng lên (076): giữ đúng tỉ lệ của nó (video dọc từ điện thoại), không ép 16:9. */
.al-player video{display:block;width:100%;max-height:75vh;border-radius:10px;background:#000}
.al-dur{position:absolute;right:.3rem;bottom:.3rem;padding:.05rem .35rem;border-radius:6px;
  background:rgb(0 0 0 / .6);color:#fff;font-size:.72em;font-weight:600;line-height:1.4}
.al-pbar{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center;justify-content:flex-end;margin-top:.4rem}
.al-pbar a{margin-right:auto;font-size:.9em;color:var(--pine)}
/* Mũi tên lướt qua album (069). Cạnh trái/phải màn hình, đủ to cho ngón cái. */
.shotbox-nav{position:absolute;top:50%;transform:translateY(-50%);width:2.8rem;height:2.8rem;border:0;
  border-radius:999px;background:rgb(255 255 255 / .18);color:#fff;font-size:1.8em;line-height:1;padding:0}
.shotbox-nav.prev{left:.5rem}
.shotbox-nav.next{right:.5rem}
.shotbox-nav[hidden],.shotbox-btn[hidden]{display:none}

/* Thanh soạn tin của nhóm. Ngồi ngay trên thanh thẻ, như thanh của mục Hỏi. */
.chatbar{position:fixed;left:0;right:0;bottom:var(--tabs-h,calc(3.6rem + env(safe-area-inset-bottom,0px)));
  background:var(--paper);border-top:1px solid var(--line);padding:.35rem 1rem;display:none;z-index:4}
.chatbar.on{display:block}
.chatbar .row{max-width:720px;margin:0 auto;display:flex;gap:.4rem;align-items:flex-end}
/* Thanh soạn tin đi theo lối Messenger, và đó là lý do thật chứ không phải chuộng hình thức:
   gần như cả hội thánh dùng Messenger mỗi ngày. Cái gì đặt đúng chỗ họ đã quen thì không ai
   phải học lại, mà người ngại học lại nhất lại chính là những người ta muốn kéo vào đây.
   Ba thay đổi so với bản trước: nút bỏ viền (bốn cái hộp xếp cạnh nhau biến mất, chỉ còn hình),
   ô nhập thành viên thuốc xám không viền, và nút gửi tròn bằng đúng các nút kia thay cho một
   thanh chữ "Gửi" chiếm gần một phần ba bề ngang — chỗ đó trả lại cho câu đang gõ. */
.chatbar textarea{flex:1;resize:none;border:0;border-radius:1.2rem;
  padding:.4rem .95rem;font:inherit;background:var(--c-them);color:var(--ink);max-height:120px;min-height:2.4rem}
.cbtn{flex:none;border:0;background:none;border-radius:999px;color:var(--c-accent);
  width:2.4rem;height:2.4rem;padding:0;display:inline-flex;align-items:center;justify-content:center}
/* 2.4rem ≈ 38px. Messenger còn nhỏ hơn, nhưng dưới mức này thì ngón tay người lớn tuổi bắt đầu
   trượt — giống nhau tới đó là đủ, không giống tới mức khó bấm.
   Đây cũng là thứ cao nhất trong hàng, nên nó định luôn chiều cao thanh soạn tin: bóp lề của ô
   nhập lại thì được ngay mấy pixel, còn bóp nút thì mất chỗ bấm mà chẳng được gì. */
.cbtn svg{width:1.45rem;height:1.45rem;display:block}
.cbtn.send{background:var(--c-accent);color:var(--paper)}
/* Chưa gõ gì thì nút gửi mờ đi, y như Messenger. Không ẩn hẳn: ẩn rồi hiện lại làm cả hàng nút
   nhảy ngang đúng lúc người ta đang nhắm ngón tay vào đó. */
.cbtn.send[disabled]{background:none;color:var(--muted);opacity:.5}
.chatbar .replying{max-width:720px;margin:0 auto .35rem;display:flex;align-items:center;gap:.5rem;
  font-size:.82em;color:var(--muted);border-left:3px solid var(--gold);padding-left:.5rem}
.chatbar .replying span{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chatbar .replying .x{border:0;background:none;color:var(--muted);font-size:1em;padding:.1rem .3rem}
.chatbar .note{max-width:720px;margin:.3rem auto 0}
/* Đang ghi âm: một chấm đỏ đập và cái đồng hồ. Phải nhìn ra ngay là máy ĐANG nghe. */
/* Một hàng, không rớt dòng. Thanh này nằm ngay trên thanh soạn tin, nên rớt xuống hai dòng là
   đẩy cả bàn phím và ô nhập xuống theo, đúng lúc người ta đang nói vào micro.
   Ba nút giữ nguyên kích thước (flex:none) và chữ trạng thái là thứ co lại — ở 320px thì
   "Recording" cụt thành "Recordi…", còn chấm và đồng hồ vẫn đủ nói máy đang nghe. Ngược lại,
   bóp nút cho vừa thì có ngày bấm "Bỏ" trúng "Gửi". */
.chatbar .rec{max-width:720px;margin:0 auto .35rem;display:flex;align-items:center;gap:.35rem;
  font-size:.9em;flex-wrap:nowrap}
/* Chấm đứng yên, không nhấp nháy. Một chấm đỏ chớp tắt cạnh "0:00" không nói được nó đang ghi
   hay đang đứng, hay vừa hỏng — người ta nhìn cái chớp rồi đoán, mà đoán sai thì mất lời nhắn.
   Chữ "Đang ghi" nói thẳng việc đang xảy ra, còn đồng hồ nhảy từng giây mới là thứ chứng minh
   máy còn nghe. Cái chớp không thêm được gì mà lại làm người ta phải luận. */
.chatbar .rec .dot{width:.6rem;height:.6rem;border-radius:50%;background:#B04A3A;flex:none;margin-right:.15rem}
.chatbar .rec b{font-weight:600;color:#B04A3A;flex:0 1 auto;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chatbar .rec #cr-rec-t{flex:none;margin-right:.2rem}
.chatbar .rec .btn{flex:none;padding:.3rem .5rem;font-size:.82em;white-space:nowrap}
/* Tạm dừng thì chấm và chữ cùng xám đi. Chỉ đổi mỗi chữ thì người ta vẫn thấy chấm đỏ và tưởng
   máy còn nghe — mà tưởng nhầm chiều đó là nói vào chỗ không ai ghi. */
.chatbar .rec.paused .dot{background:var(--muted)}
.chatbar .rec.paused b{color:var(--muted)}

/* Người trong nhóm: dùng lại .rlist/.ruser của thẻ Quản trị, chỉ đổi thành một hàng ngang vì ở
   đây không có gì gập ra. */
.ruser.prow{display:flex;align-items:center;justify-content:space-between;gap:.6rem;padding:.55rem .9rem}
.ruser.prow .rname{flex:1;min-width:0}
/* ---- Danh mục lớp ----
   Một thẻ lớp XẾP DỌC, không phải một hàng ngang như .prow. Nó phải nói năm điều mà cái tên lớp
   không nói được — ai dạy, tối thứ mấy ở đâu, mấy học viên, mấy bài học, vào được không — và năm
   điều nối nhau bằng dấu chấm giữa thì ở bề ngang điện thoại thành ba dòng chữ không chỗ nào bám
   mắt. Nên: dòng đầu là hình + tên, rồi mấy dòng nhãn-giá trị, rồi cái nút hết bề ngang ở dưới.
   Cái nút xuống dưới là điều kiện để phần trên thở được: đứng cạnh tên như trước thì "Nhắn giảng
   viên" cắn mất một phần ba bề ngang, đúng phần mà hai cột nhãn-giá trị cần. */
.ruser.crow{display:flex;flex-direction:column;gap:.3rem;padding:.7rem .9rem;
  width:100%;text-align:left;background:var(--card);border:0}
/* Trả lại đường kẻ giữa hai thẻ. `border:0` ở trên là để gỡ cái vỏ <button>, nhưng nó đứng SAU
   `.ruser + .ruser` trong file nên nó xoá luôn cả đường kẻ ấy — và mấy cái thẻ cao mới này thì
   cần đường kẻ hơn hẳn mấy hàng một dòng ngày trước: không có nó, năm cái thẻ đọc thành một khối
   chữ liền. Đặt lại ở đây, sau và hẹp hơn, nên nó thắng. */
.ruser + .ruser.crow{border-top:1px solid var(--line)}
.crow-top{display:flex;align-items:center;gap:.6rem}
.crow-top .rname{flex:1;min-width:0;display:block}
.crow-top .rname b{overflow-wrap:anywhere}
.crow-sub{display:block;line-height:1.35}
/* Hai cột thẳng hàng qua cả mấy dòng — đó là cả lý do dùng lưới thay vì mỗi dòng một flex: mắt
   chạy dọc cột nhãn để tìm "Sinh hoạt", và nó chỉ chạy được khi mấy cái nhãn bắt đầu cùng một
   chỗ. minmax(0,1fr) cho cột giá trị: không có nó thì một cái tên dài đẩy toang cả lưới. */
.cfacts{display:grid;grid-template-columns:auto minmax(0,1fr);gap:.15rem .6rem;
  font-size:.92em;align-items:baseline}
.cfk{color:var(--muted)}
.cfv{min-width:0;overflow-wrap:anywhere}
/* Hàng phụ của một lớp trong danh mục: cắt ở dòng thứ hai. App đã cắt chữ ở 120 ký tự trước khi
   vẽ (oneLine), nên luật này chỉ lo phần còn lại — một cái tên lớp dài cộng một câu mô tả vừa đúng
   120 ký tự vẫn có thể ăn ba dòng trên màn hình 375px, và ba dòng thì năm cái thẻ đọc thành một
   khối chữ liền. */
.crow-sub{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.cact{display:flex;justify-content:flex-end}
.cact .btn{padding:.35rem .7rem;font-size:.9em}
/* Lớp của mình: cả cái thẻ là một <button>, nên phải gỡ cái vỏ nút mà trình duyệt tự đắp vào —
   nền, viền, bề ngang, lề chữ — để nó trông y hệt cái thẻ <div> nằm ngay trên và ngay dưới nó.
   (Chữ với con trỏ thì bảng kiểu đã gỡ một lần cho cả app ở `button{font:inherit…}`.) */
.ruser.crow.crow-go{cursor:pointer}
@media (hover:hover){.ruser.crow.crow-go:hover,.ruser.crow.crow-tap:hover{background:var(--pine-soft)}}
/* Lớp CHƯA vào: cả thẻ cũng bấm được, nhưng nó ở lại <div> vì cái nút "Vào lớp" / "Nhắn giảng
   viên" còn nằm bên trong. Nên chỉ mượn con trỏ và cái nền lúc rê chuột — KHÔNG mượn mũi tên ›:
   cái nút bên dưới đã nói ra việc sắp xảy ra bằng cả một câu, mà một câu thì rõ hơn một mũi tên. */
.ruser.crow.crow-tap{cursor:pointer}
/* Mũi › của thẻ lớp mình: cùng cái mũi của .ruser > summary ở thẻ Quản trị, vì nó nói cùng một
   điều — thẻ này bấm được. Không quay: ở kia hàng gập RA tại chỗ nên mũi chỉ xuống, còn thẻ này
   đi SANG một trang khác. Thẻ thật chứ không ::after, vì trong một thẻ xếp dọc thì một phần tử
   giả tự rơi xuống cuối cột, xa hẳn cái tên lớp mà nó đang nói về. */
.cgo{flex:none;color:var(--muted);line-height:1;font-size:1.15em}
.pacts{display:flex;gap:.35rem;flex-wrap:wrap;justify-content:flex-end}
.pacts .btn{padding:.35rem .6rem;font-size:.82em}
/* ---- Danh bạ hội thánh ---- */
/* Dùng lại .rlist/.ruser của thẻ Quản trị: hai trang này trả lời cùng một câu hỏi ("tìm một
   người trong hai trăm"), nên chúng nên trông giống nhau — kể cả cái mũi tên › gập hàng ra, và
   kể cả chuyện hàng đóng lại là MẶC ĐỊNH. Khác đúng một chỗ: hàng ở đây có BA vùng chạm, vì
   chạm vào tên là nhắn riêng chứ không phải gập hàng. */
.ruser.cbrow{display:flex;flex-wrap:wrap;align-items:center;gap:.3rem .6rem;padding:.55rem .9rem}
/* Vùng bên trái là một cái nút, nhưng nó không được TRÔNG như một cái nút: cả hàng là một con
   người, mà viền với nền sẽ vẽ ra một cái hộp quanh đúng một nửa hàng. */
.cbwho{flex:1 1 10rem;min-width:0;display:flex;align-items:center;gap:.6rem;
  border:0;background:none;padding:.2rem 0;color:inherit;font:inherit;text-align:left;cursor:pointer}
.cbwho .rname{min-width:0;display:flex;align-items:baseline;flex-wrap:wrap}
.cbwho b{font-family:var(--serif);font-weight:600;overflow-wrap:anywhere}
.cbwho[aria-pressed="true"] b{font-weight:700}
/* Nhãn "Nhắn" cuối vùng tên: đẩy sang phải, sát mũi ›, và không bao giờ bị cái tên dài ép xuống
   hàng hay bóp méo. Màu thông, nhạt — nó chỉ chỗ bấm, không giành chỗ với cái tên. */
.cbwho .cbmsg{margin-left:auto;flex:none;color:var(--pine);background:var(--pine-soft);border-color:transparent;
  padding:.28rem .6rem;font-size:.82em;pointer-events:none}
/* Mũi tên gập hàng, vẽ y như mũi tên của .ruser > summary ở thẻ Quản trị và quay cùng một
   kiểu, vì nó nói đúng một điều đó. Ô chạm 2rem: nó đứng sát cái tên, mà chạm nhầm ở đây là mở
   ra một cuộc trò chuyện với người khác. */
.cbmore{flex:none;width:2rem;height:2rem;border:0;background:none;cursor:pointer;
  color:var(--muted);font-size:1.25em;line-height:1;display:inline-flex;
  align-items:center;justify-content:center;transition:transform .12s}
.cbmore.on{transform:rotate(90deg);color:var(--ink)}
/* Nhóm trong danh bạ: dòng phụ (số người, ai quản) nằm dưới tên; bánh răng thay cho mũi ›. */
#cb-groups{margin:.4rem 0 .9rem}
#cb-groups .rname{flex-direction:column;align-items:flex-start}
/* Tiêu đề khúc gập được: cả hàng là nút, mũi ˄/˅ đứng ngay sau con số. */
.sectog{display:inline-flex;align-items:center;gap:.4rem;border:0;background:none;padding:.15rem 0;
  font:inherit;color:inherit;text-transform:inherit;letter-spacing:inherit;cursor:pointer}
.secchev{width:1.15em;height:1.15em;flex:none;opacity:.8}
.cbsub{font-size:.85em;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cbset{font-size:1.05em}
/* Số điện thoại và email xuống HÀNG RIÊNG, không nép bên phải cái tên: chúng chỉ hiện khi có
   người hỏi tới, mà lúc đã hỏi thì chúng là thứ chính của hàng ấy. Thụt vào bằng đúng bề ngang
   ô hình (2.1rem) cộng khe (.6rem) để thẳng lề với cái tên ngay trên. */
.cbacts{flex:0 0 100%;display:flex;gap:.35rem;flex-wrap:wrap;justify-content:flex-start;
  padding-left:2.7rem}
/* Lúc đang chọn người lập nhóm thì hàng có thêm ô tích trước ô hình, nên lề thụt phải dài thêm
   đúng bề ngang ô tích (1.15rem) cộng khe (.6rem). Đánh dấu bằng một cái lớp do JS đặt, không
   dùng :has(): cả tệp này không có chỗ nào khác cần :has(), và một cái lề thụt thì không đáng
   để đem theo một bộ chọn mà iPhone đời cũ hơn iOS 16.4 không hiểu. */
.cbrow.picking .cbacts{padding-left:4.45rem}
/* Số điện thoại và email nhỏ hơn chữ thường: chúng là thứ để BẤM lúc cần, không phải thứ đọc
   hàng nào cũng đọc. Email dài thì cho ngắt giữa chữ — thà một cái nút hai dòng còn hơn một
   thanh cuộn ngang cho cả trang. */
.cbacts .btn{padding:.3rem .55rem;font-size:.8em;overflow-wrap:anywhere}
/* Chữ f trong ô vuông bo góc, đứng trước đường dẫn Facebook của một người (danh bạ, và bảng
   Quản trị). Cùng một hình với #fblink ở đầu trang, và cùng một lẽ: quả địa cầu trong app này
   đã có nghĩa "trang web hội thánh", nên Facebook phải mang hình khác, còn chữ f đặc màu xanh
   thương hiệu thì kéo mắt mạnh hơn cả cái tên nó đứng cạnh.
   Cỡ bằng em chứ không bằng rem: nó đi trong .btn, mà .btn ở danh bạ nhỏ hơn (.8em) .btn ở bảng
   Quản trị — một cỡ cứng thì to đúng một chỗ. Và PHẢI cỡ ở đây: cả tệp này không có luật nào
   cỡ svg bên trong .btn, thiếu thì trình duyệt vẽ nó 300×150 và đen sì. */
.btn .fbmark{width:1em;height:1em;vertical-align:-.15em;fill:none;stroke:currentColor;
  stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
/* Ô tích: một vòng tròn rỗng, tô xanh khi đã chọn. Không dùng <input type="checkbox"> vì cả vùng
   bên trái đã là một cái nút, mà một checkbox thật nằm trong một <button> thì không bấm tới. */
.tick{flex:none;width:1.15rem;height:1.15rem;border-radius:999px;border:2px solid var(--line);position:relative}
.tick.on{border-color:var(--pine);background:var(--pine)}
.tick.on::after{content:"";position:absolute;left:.26rem;top:.08rem;width:.3rem;height:.55rem;
  border:solid var(--paper);border-width:0 2px 2px 0;transform:rotate(45deg)}

/* ---- Quản trị: ẩn một hồ sơ khỏi danh bạ ---- */
/* Khối riêng, có một vạch phía trên: nút này KHÔNG được đứng cạnh nút Xóa. Hai việc trông giống
   nhau từ xa mà hậu quả khác hẳn nhau, nên chúng phải cách nhau bằng một cái gì mắt thấy được. */
.hidebox{border-top:1px solid var(--line);margin-top:.6rem;padding-top:.5rem}
.tag.hid{background:var(--gold-soft);color:var(--ink)}

@media (max-width:420px){
  .chatbar{padding:.35rem .8rem}
  /* Lề của main ở khổ này là .8rem, nên lề âm của hàng đầu phòng phải bằng đúng .8rem: lệch
     hai bên là nền thò ra ngoài mép màn hình, đẩy ra một thanh cuộn ngang. */
  #chat-room .room-head{margin:-1rem -.8rem .25rem;padding:.15rem .8rem}
  .ruser.prow{flex-wrap:wrap}
  .pacts{justify-content:flex-start}
}

/* ---------- Bài viết ---------- */
/* Tên chuyên mục đứng TRÊN cái tựa, chữ nhỏ và in hoa: đó là câu trả lời cho "đây là cái gì"
   mà mắt đọc trước khi kịp đọc cái tựa. Cùng chỗ và cùng vai với dòng ngày tháng của một bảng
   tin (.ev-when) — nên cùng màu thông, cùng cỡ chữ. */
.art-kicker{font-size:.8em;color:var(--pine);font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;margin:0 0 .2rem}
.art-kicker:empty{display:none}
/* Thân bài: chữ có chân và dòng thưa, y như thân bài tĩnh nguyện (.dev-body) — đây cũng là chữ
   để ĐỌC HẾT, không phải để liếc qua.
   Bài lưu ở dạng chữ thường (035), nhưng app dựng mỗi đoạn thành một thẻ <p> khi vẽ (xem
   paintArtBody) chứ KHÔNG đổ cả khối rồi nhờ pre-wrap. Lý do: một dòng trống thật cao bằng một
   dòng chữ, mà khoảng cách giữa hai đoạn văn phải nhỏ hơn thế — và phải bằng nhau ở mọi chỗ,
   không phụ thuộc người viết gõ mấy lần Enter. pre-wrap vẫn còn, nhưng ở TRONG một đoạn, để
   giữ những lần xuống dòng đơn mà người ta cố ý gõ. */
.art-body{font-family:var(--serif);line-height:1.75;overflow-wrap:anywhere;margin:.9rem 0 0}
.art-body p{white-space:pre-wrap;margin:0 0 .9rem}
.art-body p:last-child{margin-bottom:0}
/* Định dạng nhẹ (paintPara ở app.js): tiêu đề, danh sách, trích dẫn. Cùng nhịp lề .9rem với đoạn
   văn, để một bài trộn đủ thứ vẫn đều tay. Tiêu đề cách đoạn TRƯỚC nhiều hơn đoạn SAU: nó thuộc
   về phần nó mở ra, không phải phần vừa hết. */
.art-body h3{font-family:var(--serif);font-size:1.15em;font-weight:700;line-height:1.35;color:var(--pine);
  margin:1.6rem 0 .55rem;padding-bottom:.25rem;border-bottom:1px solid var(--line)}
.art-body h4{font-family:var(--serif);font-size:1.02em;font-weight:700;line-height:1.35;margin:1.15rem 0 .4rem}
.art-body :is(h3,h4):first-child{margin-top:0}
/* Bố cục bấm được (linkToc): một liên kết trông như liên kết, và đầu mục được nhảy tới đứng chừa
   chỗ cho thanh đầu trang dính ở trên, rồi loé nhẹ một nhịp để mắt biết mình đã tới đâu. */
.art-body a.toc-link{color:var(--pine);text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:.2em}
.art-body :is(h3,h4){scroll-margin-top:calc(env(safe-area-inset-top,0px) + var(--head-h,3.4rem) + .6rem)}
.art-body .toc-hit{animation:tocHit 1.4s ease-out}
@keyframes tocHit{0%{background:var(--gold-soft)}100%{background:transparent}}
@media (prefers-reduced-motion:reduce){.art-body .toc-hit{animation:none}}
/* Ô nổi bật (>!): điều người đọc phải nhớ — ghi nhớ, bài tập, định nghĩa then chốt. Nền vàng nhạt
   cùng màu với chip đang chọn, để nó nổi lên giữa trang mà không la hét. */
.art-body .callout{white-space:pre-wrap;margin:0 0 .9rem;padding:.65rem .85rem;border-radius:10px;
  background:var(--gold-soft);border-left:4px solid var(--gold)}
.art-body mark{background:var(--gold-soft);color:inherit;padding:0 .15em;border-radius:3px;box-shadow:inset 0 -.45em 0 color-mix(in srgb, var(--gold) 35%, transparent)}
.art-body .callout mark{background:none}
.art-body strong{font-weight:700}
.art-body ul,.art-body ol{margin:0 0 .9rem;padding-left:1.4rem}
.art-body li{margin:0 0 .3rem;padding-left:.15rem}
.art-body li:last-child{margin-bottom:0}
.art-body blockquote{white-space:pre-wrap;margin:0 0 .9rem;padding:.15rem 0 .15rem .9rem;border-left:3px solid var(--gold);font-style:italic}
.art-body > :last-child{margin-bottom:0}
/* Thanh định dạng trên ô soạn: một hàng chip nhỏ, cuộn ngang khi chật thay vì rớt ba hàng. */
.txtfmt{display:flex;gap:.35rem;overflow-x:auto;scrollbar-width:none;margin:.2rem 0 .4rem;padding-bottom:.1rem}
.txtfmt::-webkit-scrollbar{display:none}
.txtfmt .chip{flex:0 0 auto;font-size:.85em;padding:.25rem .65rem}
.txtfmt [data-tfmt="b"]{font-weight:700}
.txtfmt [data-tfmt="i"]{font-style:italic}
.txtfmt [data-tfmt="m"]{background:var(--gold-soft)}
.fmthint{margin:.3rem 0 0;font-size:.8em}
.fmtsay{margin:.35rem 0 0}
.fmtsay:empty{display:none}
/* Tấm ảnh xen giữa hai đoạn: lề trên bằng lề dưới, và bằng đúng lề giữa hai đoạn văn — nếu không
   thì ảnh trông như đang dính vào đoạn dưới nó. */
#art-one h1{margin-bottom:.25rem}
#art-one .evact{justify-content:flex-start}
/* Bài đã gỡ, nhìn từ danh sách: cùng màu vàng cảnh báo với thẻ sự kiện đã gỡ (.ev-card.down),
   để một dấu hiệu chỉ phải học một lần. */
.archive button.down{border-color:var(--gold);background:var(--gold-soft)}
/* Hàng chuyên mục trong ô quản lý: tên bên trái, mấy cái nút icôn bên phải. */
.ac-row{display:flex;align-items:center;gap:.5rem;padding:.45rem 0;border-bottom:1px solid var(--line)}
.ac-row:last-of-type{border-bottom:0}
.ac-row .nm{flex:1;min-width:0}
.ac-row .nm b{font-family:var(--serif);font-weight:600}
.ac-row.off .nm{opacity:.55}
.ac-row .evact{margin:0;flex:none}
/* Ảnh đầu bài. Không tràn lề như .ev-poster của bảng tin: tấm kia nằm trong một .card nên phải
   bò ra tới mép thẻ, còn bài viết mở hẳn ra một trang, không có thẻ nào để bò. */
.art-cover{display:block;width:100%;max-height:13rem;object-fit:cover;border-radius:14px;margin:.1rem 0 .7rem}
/* Thẻ một bài viết trong danh sách. Bốn tầng, bốn vai rõ ràng — chứ không phải bốn dòng chữ xám
   bằng nhau như bản trước, nơi mắt không biết bám vào đâu. */
.art-row{grid-template-columns:auto minmax(0,1fr);row-gap:.1rem;column-gap:.75rem}
.art-row > *{grid-column:2}
.art-row .k{font-size:.72em;color:var(--pine);font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;line-height:1.3}
.art-row b{font-size:1.06em;line-height:1.3;margin:.1rem 0 .15rem;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
.art-row .by{font-size:.82em;color:var(--muted)}
.art-row .by .tag{margin-left:.3rem}
/* Mấy dòng đầu cắt ở hai dòng: dài hơn thì nó thôi là một lời mời đọc và thành chính bài đọc,
   mà cả danh sách thì cuộn mãi không hết. */
.art-row .ex{font-size:.9em;color:var(--muted);margin-top:.2rem;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
/* Ảnh nhỏ bên phải, neo lên đầu cho thẳng hàng với dòng chuyên mục.
   `grid-row: 1 / span 4` chứ KHÔNG phải `1 / -1`, và đây đúng là cái làm hỏng bản trước: số âm
   đếm từ mép LƯỚI TƯỜNG MINH, mà ở đây chỉ khai cột chứ không khai hàng nào — nên mọi hàng đều
   là hàng ngầm, `-1` rơi về chính vạch 1, và ô ảnh chỉ chiếm HÀNG MỘT rồi kéo dòng chuyên mục
   cao bằng 4.6rem. Đo được: dòng chuyên mục cao 74px cho một dòng chữ 12px, và cái khoảng trống
   ấy chính là chỗ hội thánh nhìn thấy. `span` thì đếm theo hàng thật, nên nó đúng. */
.art-row.haspic{grid-template-columns:auto minmax(0,1fr) 4.6rem}
.art-row.haspic > :not(.thumb){grid-column:2}
/* Nhãn ngày tờ lịch (smallBadge) ở cột đầu, cao suốt bốn tầng chữ. */
.art-row > .ev-badge,.art-row.haspic > .ev-badge{grid-column:1;grid-row:1/span 4;align-self:start}
.art-row .by:empty{display:none}
.art-row .thumb{grid-column:3;grid-row:1/span 4;align-self:start;width:4.6rem;height:4.6rem;
  object-fit:cover;border-radius:10px;background:var(--pine-soft)}
/* Mấy tấm ảnh máy bóc ra từ file Word, bày thành một hàng nhỏ trong ô soạn: người viết thấy ngay
   máy lấy được những gì, trước khi bấm Đăng. */
.artsrc-pics{display:flex;flex-wrap:wrap;gap:.4rem;margin:.5rem 0 0}
.artsrc-pics figure{margin:0;width:4rem}
.artsrc-pics img{display:block;width:4rem;height:4rem;object-fit:cover;border-radius:8px;background:var(--pine-soft)}
.artsrc-pics figcaption{font-size:.7em;color:var(--muted);text-align:center;margin-top:.15rem}
/* ---- Ô viết bài: ba bước (wizard) ----
   Người dùng chính là mục sư và ban chăm sóc lớn tuổi, nên mọi thứ ở đây to hơn phần còn lại của
   app một nấc: nút chọn đường cao cỡ một ngón tay cái, chữ hỏi ở chữ có chân, nút đi tiếp to và
   nằm bên phải — đúng chỗ ngón tay đang chờ. */
.card .wz-steps{display:flex;gap:.3rem;list-style:none;padding:0;margin:.2rem 0 .9rem;counter-reset:wz}
.card .wz-steps li{flex:1;min-width:0;margin:0;text-decoration:none;opacity:1;counter-increment:wz;font-size:.78em;color:var(--muted);
  padding-top:.45rem;border-top:4px solid var(--line);line-height:1.3}
.card .wz-steps li::before{content:counter(wz) ". ";font-weight:700}
.card .wz-steps li.done{border-top-color:var(--pine);color:var(--ink)}
.card .wz-steps li.on{border-top-color:var(--pine);color:var(--pine);font-weight:700}
.wz-q{font-family:var(--serif);font-weight:600;font-size:1.15em;margin:.2rem 0 .7rem}
.wz-q.small{font-size:1em;margin-top:1.3rem;padding-top:.9rem;border-top:1px solid var(--line)}
.wz-pick{display:grid;gap:.6rem}
.wz-pick button{display:grid;grid-template-columns:2.6rem minmax(0,1fr);column-gap:.7rem;align-items:center;
  text-align:left;padding:.95rem 1rem;border:1.5px solid var(--line);border-radius:14px;
  background:var(--card);color:var(--ink);font:inherit;cursor:pointer;min-height:4.4rem}
.wz-pick button:hover,.wz-pick button:focus-visible{border-color:var(--pine);background:var(--pine-soft)}
.wz-pick .ic{grid-row:1/span 2;font-size:1.8em;line-height:1;text-align:center}
.wz-pick b{font-size:1.08em}
.wz-pick small{color:var(--muted);font-size:.86em;line-height:1.4;margin-top:.1rem}
.wz-resume{background:var(--gold-soft);border:1px solid var(--gold);border-radius:14px;padding:.8rem .95rem}
.wz-resume p{margin:0}
.wz-resume .note{display:block;margin-top:.25rem;overflow-wrap:anywhere}
.wz-box{background:var(--pine-soft);border-radius:14px;padding:.8rem .95rem;margin:0 0 .8rem}
.wz-box .actions{margin-top:.5rem}
.wz-status{margin:0;font-weight:500}
.wz-status:empty{display:none}
.wz-nav{justify-content:space-between;margin-top:1rem}
.btn.big{padding:.8rem 1.2rem;font-size:1.05em;font-weight:600}
.note.warn{color:#B04A3A;font-weight:500}
.note.warn:empty,#arw-saved:empty{display:none}
#arw-saved{margin:.2rem 0 0;font-size:.85em}
/* Khung xem trước: viền nét đứt để nói "đây chưa phải bài thật", nhưng bên trong là đúng khuôn
   chữ của trang đọc bài (.art-kicker, .art-body) — không dựng một khuôn thứ hai cho giống. */
.wz-preview{border:1.5px dashed var(--line);border-radius:14px;padding:.9rem 1rem;background:var(--paper)}
.wz-preview h2{font-family:var(--serif);margin:.1rem 0 .25rem;font-size:1.3em;line-height:1.3}
.wz-preview .note{margin:0}
.wz-preview .art-body{max-height:24rem;overflow:auto}
#arw-rec .dev-recline{font-size:1.1em}
/* Bước 3 của tĩnh nguyện: lời nhắc việc còn lại, nằm ngay dưới dòng tựa của thẻ vừa dựng. */
.wz-hint{background:var(--gold-soft);border:1px solid var(--gold);border-radius:12px;padding:.6rem .8rem;margin:.2rem 0 .7rem}
/* Cách chép bản ghi chữ YouTube: các bước đánh số, chữ to bằng chữ thường chứ không mờ như .note. */
.card .wz-howto{margin:.3rem 0 .4rem;padding-left:1.4rem}
.card .wz-howto li{margin:.25rem 0}
.wz-box p{margin:0 0 .3rem}
/* Biểu mẫu ba bước dựng trên MỘT biểu mẫu có sẵn (đăng sự kiện, mở lớp): mỗi ô mang
   data-wstep, và khung mang data-step của bước đang đứng. Không có data-step (lúc sửa một sự
   kiện cũ) thì mọi ô hiện đủ và mấy thứ riêng của wizard ẩn đi. */
#cg-mem-list{display:flex;flex-wrap:wrap;gap:.35rem;margin:.5rem 0 .2rem}
#cg-mem-list:empty{display:none}
.wzf[data-step="1"] [data-wstep]:not([data-wstep="1"]),
.wzf[data-step="2"] [data-wstep]:not([data-wstep="2"]),
.wzf[data-step="3"] [data-wstep]:not([data-wstep="3"]){display:none !important}
.wzf:not([data-step]) .wz-only{display:none !important}
.ev-review .ev-card{margin:0;border-style:dashed;border-width:1.5px}
.wz-review{border:1.5px dashed var(--line);border-radius:14px;padding:.8rem 1rem;background:var(--paper)}
.wz-review h3{font-family:var(--serif);margin:.1rem 0 .4rem;font-size:1.2em}
.wz-review dl{margin:0;display:grid;grid-template-columns:auto minmax(0,1fr);gap:.3rem .7rem}
.wz-review dt{color:var(--muted);font-size:.88em}
.wz-review dd{margin:0;overflow-wrap:anywhere}
/* Ảnh xen trong thân bài. KHÔNG đặt width:100% — chặn theo CẢ HAI chiều rồi canh giữa.
   width:100% trơn thì một tấm ảnh dọc (điện thoại chụp đứng, hay một tờ thông báo scan) cao gấp
   rưỡi màn hình, và người đọc phải cuộn qua nó như cuộn qua một bức tường. max-height giữ cho
   tấm ảnh luôn nằm gọn trong một màn; tấm nhỏ thì để nguyên cỡ thật, phóng lên chỉ ra một khối mờ.
   Chặn theo MÀN (vh), không theo một con số cố định: 22rem cũ bóp một tấm minh hoạ dọc ghép ba
   khung (bài "Carry Me", 26/10) còn bằng một phần ba màn máy tính, chữ trong hình không đọc nổi. */
.art-body img{display:block;max-width:100%;width:auto;height:auto;max-height:85vh;max-height:85svh;
  border-radius:12px;margin:0 auto .9rem}

/* ===== Một lớp, mở ra để đọc (#class-one, migration 052) ===== */
/* Ô hình đứng CẠNH cái tên, không đứng trên nó: tên lớp là thứ phải đọc trước, mà một tấm hình
   ngang hết bề rộng thì đẩy cái tên xuống dưới mép màn hình trên điện thoại. */
.cone-top{display:flex;align-items:center;gap:.8rem;margin:0 0 .6rem}
.cone-top>div{min-width:0}
.cone-top h1{margin:0;font-size:1.3em;overflow-wrap:anywhere}
.cone-top .note{margin:.15rem 0 0}
.cone .cfacts{margin:.7rem 0 0}
/* Lời giới thiệu: cùng một khuôn chữ với bài viết và bài học (.art-body) — cùng một việc thì cùng
   một dáng, nên người đọc không phải học lại cách đọc ở trang thứ ba. */
.cone .art-body{margin:.9rem 0 0}
.cone .actions{margin:.9rem 0 0}

/* ===== Dâng hiến (056) =====
   Dòng để chép là cả một nút rộng hết thẻ: ngón cái chạm đâu cũng trúng. Chữ dùng font thường
   chứ không monospace — người đọc là ông bà, không phải lập trình viên — nhưng được ngắt ở bất
   kỳ chỗ nào, vì một địa chỉ email dài không có dấu cách nào để xuống dòng. */
.give-copy{display:flex;align-items:center;gap:.6rem;width:100%;text-align:left;font:inherit;color:var(--ink);
  background:var(--pine-soft);border:1px dashed var(--line);border-radius:12px;padding:.6rem .75rem;margin:.2rem 0 .35rem;cursor:pointer}
.give-val{flex:1;font-weight:700;font-size:1.05em;overflow-wrap:anywhere}
.give-cp{flex:none;font-size:.85em;color:var(--pine);font-weight:500}
.give-copy:active{border-style:solid;border-color:var(--pine)}
/* Icôn vẽ tay trong lưới trang chủ và đầu trang (HEART_CROSS): đứng như một chữ, không như ảnh
   nội dòng — display:block bỏ khe hở của đường chân chữ dưới hình. */
.htile .ic svg,.phead-ic svg{display:block;width:1em;height:1em}

/* Lời giải thích gấp sau nút (?): một dòng khi đóng, bấm thì mở ra các câu .note bên dưới. */
.help{margin:0 0 .6rem}
.help > summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:.45rem;
  font-size:.85em;color:var(--muted);padding:.25rem 0;-webkit-tap-highlight-color:transparent}
.help > summary::-webkit-details-marker{display:none}
.help > summary::marker{content:""}
.help .qmark{display:inline-grid;place-items:center;width:1.5rem;height:1.5rem;border-radius:50%;
  border:1.5px solid currentColor;font-weight:600;font-size:.9em;line-height:1}
.help[open] .qmark{background:var(--muted);border-color:var(--muted);color:var(--card)}
.help > .note{margin:.35rem 0 0}

/* Ô hồ sơ còn thiếu, sau một lần đăng nhập — cùng màu vàng với dòng nhắc #prof-nudge ở đầu thẻ. */
.field input.need{border-color:var(--gold);box-shadow:0 0 0 3px var(--gold-soft)}
/* Đầu trang dính trên cùng, nên cuộn tới thẻ hồ sơ phải chừa chỗ cho nó, không thì nó che dòng nhắc. */
#profile-card{scroll-margin-top:calc(env(safe-area-inset-top,0px) + 5rem)}

/* Nút Nghe ở đầu bài (bài giảng, tĩnh nguyện, bài viết). Hàng nút cuối bài mang lề trên .7rem để
   tách khỏi thân bài; ở đầu bài thì nó cần tách khỏi phần bên DƯỚI nữa. */
.listen-top{margin:.7rem 0 .4rem}
/* Nút Dừng nổi khi đang đọc mà nút Nghe đã cuộn khuất. Đứng ngay dưới đầu trang, bên phải — không
   ở dưới đáy: dưới đó là chỗ của toast ("Giọng đọc thật đang thu…" hiện đúng lúc vừa bấm Nghe),
   của dòng mời cập nhật và của ô gõ tin nhắn, và đặt ở đó thì đè lên cả ba. */
.say-float{position:fixed;right:1rem;top:calc(env(safe-area-inset-top,0px) + var(--head-h,3.4rem) + .6rem);
  background:var(--ink);color:var(--paper);border:0;border-radius:999px;padding:.6rem 1rem;font:inherit;font-size:.9em;font-weight:500;
  box-shadow:0 2px 12px rgb(0 0 0 / .22);z-index:10}
.say-float[hidden]{display:none}
/* Thanh phần (088): viên nổi giữa cạnh dưới, ngay trên thanh thẻ — chỗ ngón cái với tới khi một
   tay cầm máy nghe, và không chồng lên nút Dừng nổi (cái ấy ẩn đi khi viên này hiện). */
.say-chap{position:fixed;left:0;right:0;margin:0 auto;width:max-content;z-index:10;
  bottom:calc(var(--tabs-h,calc(3.6rem + env(safe-area-inset-bottom,0px))) + .6rem);
  display:flex;align-items:center;gap:.15rem;max-width:calc(100vw - 2rem);padding:.25rem;
  background:var(--ink);color:var(--paper);border-radius:999px;box-shadow:0 2px 12px rgb(0 0 0 / .22)}
.say-chap[hidden]{display:none}
.say-chap button{flex:none;width:2.5rem;height:2.5rem;border:0;border-radius:999px;background:none;color:inherit;font:inherit;font-size:1.05em;cursor:pointer}
.say-chap button:disabled{opacity:.35;cursor:default}
.say-chap button:not(:disabled):active{background:rgb(255 255 255 / .15)}
.say-chap-t{min-width:0;flex:1;padding:0 .35rem;font-size:.85em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:15rem}
/* "tới" giữa ô giờ bắt đầu và ô giờ xong (066). */
.ev-stoprow .ev-until{flex:none;display:inline-flex;align-items:center;gap:.4rem}
.ev-stoprow .ev-to{color:var(--muted);font-size:.9em}
/* Mở ô đăng bằng nút góc phải thì trang cuộn tới đầu ô — chừa chỗ cho thanh đầu trang dính ở
   trên, không thì chính dòng "Đăng một sự kiện" (cũng là chỗ bấm để đóng) nằm khuất dưới nó. */
#cal-new{scroll-margin-top:calc(env(safe-area-inset-top,0px) + var(--head-h,3.4rem) + .75rem)}

/* Ô "Mời vào app": lời mời bày ra để sửa trước khi gửi (xem shareInvite). Trên điện thoại nó là
   một tấm trượt lên từ đáy, ngay tầm ngón cái; màn hình rộng thì là một khung giữa trang. */
.invite-box{border:1px solid var(--line);border-radius:18px;background:var(--card);color:var(--ink);
  padding:1rem 1rem calc(1rem + env(safe-area-inset-bottom,0px));width:min(34rem,100vw - 2rem);max-height:90dvh;overflow:auto}
.invite-box::backdrop{background:rgba(0,0,0,.45)}
.invite-box h2{margin:0 0 .2rem;font-size:1.15em}
.invite-box .note{margin:0 0 .4rem}
.invite-box textarea{min-height:12em;font-size:.95em;line-height:1.45}
@media (max-width:600px){
  .invite-box{width:100vw;max-width:100vw;margin:auto 0 0;border-radius:18px 18px 0 0;border-bottom:0}
}
.invite-box:focus{outline:none}
.invite-box .filters{margin:.2rem 0 .3rem}

/* Bài học dạng trình chiếu (openSlides trong app.js). Phủ kín trang, cùng màu giấy và mực của app
   nên tự theo chế độ sáng/tối. Trang tựa có ảnh đầu bài thì ảnh làm nền, chữ trắng trên lớp tối.
   z-index 8: cùng tầng với khung xem ảnh, dưới toast (9). */
/* Dòng ai-khi-nào và ba nút tròn chung một hàng: nút đứng bên phải, chữ co lại bên trái. Màn hẹp
   quá thì nút rớt xuống hàng dưới chứ không bóp chữ. Tựa bài sát lên mũi tên quay lại. */
#v-lesson .back{margin-bottom:.15rem}
#cr-lesson-title,#pl-title{margin:.15rem 0 .1rem}
.lesson-meta{display:flex;align-items:center;flex-wrap:wrap;gap:.3rem .6rem}
.lesson-meta .note{margin:0;flex:1 1 9rem;min-width:0}
.lesson-tools{display:flex;gap:.4rem;align-items:center;margin:0}
.lt-btn{display:inline-flex;align-items:center;justify-content:center;gap:.15rem;height:2.2rem;min-width:2.2rem;padding:0;
  border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:999px;font:inherit;cursor:pointer}
.lt-btn svg{width:1.1rem;height:1.1rem;flex:none}
.lt-btn[hidden]{display:none}
.lt-reach{padding:0 .55rem 0 .65rem;font-size:.95em}
.lt-reach .chev{width:.9rem;height:.9rem;color:var(--muted);transition:transform .15s}
.lt-reach[aria-expanded="true"]{background:var(--gold-soft);border-color:var(--gold)}
.lt-reach[aria-expanded="true"] .chev{transform:rotate(180deg)}
@media (hover:hover){.lt-btn:hover{border-color:var(--pine)}}
.slides{position:fixed;inset:0;z-index:8;display:none;flex-direction:column;background:var(--paper);color:var(--ink);
  padding:env(safe-area-inset-top,0px) env(safe-area-inset-right,0px) env(safe-area-inset-bottom,0px) env(safe-area-inset-left,0px)}
.slides.on{display:flex}
.slides-stage{flex:1;min-height:0;overflow:auto;display:flex;padding:1.2rem 1rem;cursor:pointer;-webkit-tap-highlight-color:transparent}
.slide{margin:auto;width:100%;max-width:46rem;line-height:1.45;font-size:2em}
.slide .art-body{margin:0;line-height:1.5}
.slide .art-body p{margin:0 0 .7em}
.slide .art-body h3{margin:0 0 .5em;font-size:1.25em;border:0}
.slide .art-body h4{margin:0 0 .4em}
.slide .art-body .callout{margin:0 0 .7em}
.slide .art-body ul,.slide .art-body ol,.slide .art-body blockquote{margin:0 0 .7em}
.slide .art-body img{max-height:62vh;margin:.4em auto;border-radius:12px}
.slide.pic{max-width:none;height:100%;display:flex}
.slide.pic .art-body{margin:auto;display:flex;align-items:center;justify-content:center;width:100%;height:100%}
.slide.pic .art-body img{max-width:100%;max-height:100%;object-fit:contain;margin:0}
.slide.title{text-align:center}
.slide.title h2{font-family:var(--serif);font-size:1.5em;line-height:1.25;margin:.2em 0 .5em;overflow-wrap:anywhere}
.slide.title .k{margin:0;font-size:.55em;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--pine)}
.slide.title .by{margin:.2em 0 0;font-size:.5em;color:var(--muted)}
.slide.title.haspic{max-width:none;align-self:stretch;display:flex;flex-direction:column;justify-content:center;
  margin:-1.2rem -1rem;padding:2rem 1.2rem;color:#fff;
  background:linear-gradient(rgb(0 0 0 / .45),rgb(0 0 0 / .6)),var(--cover) center/cover no-repeat}
.slide.title.haspic .k{color:#F3DE97}
.slide.title.haspic .by{color:rgb(255 255 255 / .85)}
.slides-bar{display:flex;align-items:center;gap:.5rem;padding:.5rem .9rem;border-top:1px solid var(--line);background:var(--paper)}
.slides-gap{flex:1}
.slides-count{min-width:4.5em;text-align:center;font-variant-numeric:tabular-nums;color:var(--muted);font-size:.9em}
.slides-btn{border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:999px;font:inherit;font-size:.9em;
  font-weight:500;padding:.45rem .9rem;line-height:1.1;min-height:2.4rem}
.slides-btn[data-sl-prev],.slides-btn[data-sl-next]{font-size:1.4em;padding:0;width:2.6rem;height:2.6rem;min-height:0}
.slides-btn:disabled{opacity:.35}
.slides-prog{height:3px;background:var(--line)}
.slides-prog i{display:block;height:100%;width:0;background:var(--pine);transition:width .2s}
.slides:fullscreen{padding:0}
body.slides-open{overflow:hidden}
@media (max-width:480px){.slides-btn[data-sl-full]{display:none}}
#cr-lesson-reach{margin:.5rem 0 0}
#cr-lesson-body,#pl-body{margin-top:.7rem}

/* ===== Ảnh & video: gọn lại (11/10/2026) =====
   Nút tròn nhỏ hơn nút của thẻ sự kiện — trang này có hai hàng nút chồng nhau, và ở 390px mỗi
   rem dọc là một hàng ảnh bị đẩy xuống. */
#v-photos .phead{margin-bottom:0}
#v-photos .eb{width:2.15rem;height:2.15rem}
#v-photos .eb svg{width:1rem;height:1rem}
#v-photos .eb.ph-headq{width:1.8rem;height:1.8rem}
#v-photos .eb.ph-headq svg{width:.9rem;height:.9rem}
#v-photos .eb.ph-li{width:1.8rem;height:1.8rem}
/* Tháng cũ thu gọn: một dòng chạm được, mũi tên mở/gập ở mép phải. */
.ph-month{display:flex;align-items:center;gap:.4rem;width:100%;margin:.6rem 0 0;padding:.55rem .2rem;
  border:0;border-top:1px solid var(--line);background:none;color:inherit;font:inherit;font-weight:600;font-size:.92em;cursor:pointer;text-align:left}
.ph-month svg{width:1.05rem;height:1.05rem;margin-left:auto;color:var(--muted)}
.ph-mgrid{margin:.2rem 0 .3rem}
.ph-back{border:0;background:none;padding:.2rem 0;margin-top:.5rem;color:var(--pine);font:inherit;font-size:.88em;cursor:pointer}
/* Danh sách album (nút lọc Album): thẻ ảnh bìa, hai cột trên điện thoại. */
.ph-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(9rem,1fr));gap:.6rem;margin-top:.6rem}
.ph-card{display:flex;flex-direction:column;align-items:stretch;text-align:left;padding:0;overflow:hidden;
  border:1px solid var(--line);border-radius:14px;background:var(--card);color:inherit;font:inherit;cursor:pointer}
.ph-card .ph-cover{display:flex;align-items:center;justify-content:center;aspect-ratio:4/3;background:var(--bot);font-size:1.8rem}
.ph-card .ph-cover img{display:block;width:100%;height:100%;object-fit:cover}
.ph-card .ph-t{padding:.45rem .6rem 0;font-weight:600;font-size:.92em;line-height:1.3;overflow-wrap:anywhere}
.ph-card .ph-m{padding:.1rem .6rem .5rem;font-size:.78em;color:var(--muted)}
.ph-card.add{border-style:dashed;color:var(--pine)}
.ph-card.add .ph-cover{background:var(--pine-soft);color:var(--pine);font-size:2.2rem}
.ph-card.add .ph-t{padding-bottom:.6rem}
/* Mô tả album (078): giữ xuống dòng như người viết gõ. */
.ph-note{margin:.35rem 0 0;font-size:.92em;line-height:1.5;white-space:pre-line;overflow-wrap:anywhere}
/* Nhãn "Bìa" trên ô của tấm đang làm ảnh bìa — góc trái dưới, không đè dấu chọn hay thời lượng. */
.al-cover{position:absolute;left:.3rem;bottom:.3rem;padding:.05rem .4rem;border-radius:6px;
  background:var(--pine);color:#fff;font-size:.7em;font-weight:600;line-height:1.5}
.ph-coverline{margin:.2rem 0 .6rem}
.linkbtn{border:0;background:none;padding:0;color:var(--pine);font:inherit;text-decoration:underline;cursor:pointer}
/* ===== Trình chiếu album =====
   Phủ kín màn hình trên nền đen. Ảnh hiện dần rồi phóng rất chậm trong lúc đứng (đủ để ảnh không
   đứng im, không đủ để chóng mặt). Chạm vào ảnh: ẩn/hiện các nút. z-index 8 như .shotbox. */
.slideshow{position:fixed;inset:0;z-index:8;display:none;background:#000;color:#fff;user-select:none}
.slideshow.on{display:block}
.ss-stage{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;overflow:hidden}
.ss-img,.ss-vid{max-width:100%;max-height:100%;object-fit:contain}
.ss-img.in{animation:ss-in .7s ease-out both, ss-zoom 5.6s linear both}
@keyframes ss-in{from{opacity:0}to{opacity:1}}
@keyframes ss-zoom{from{transform:scale(1)}to{transform:scale(1.06)}}
@media (prefers-reduced-motion:reduce){.ss-img.in{animation:ss-in .3s ease-out both}}
.ss-top{position:absolute;top:0;left:0;right:0;display:flex;align-items:center;gap:.6rem;
  padding:calc(env(safe-area-inset-top,0px) + .6rem) .8rem .6rem;background:linear-gradient(rgb(0 0 0 / .55),transparent)}
.ss-count{font-size:.85em;font-variant-numeric:tabular-nums;opacity:.85}
.ss-title{flex:1;min-width:0;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ss-bar{position:absolute;left:0;right:0;bottom:0;height:3px;background:rgb(255 255 255 / .15)}
.ss-prog{display:block;height:100%;width:0;background:#fff}
.ss-ctl{position:absolute;left:0;right:0;bottom:0;display:flex;justify-content:center;align-items:center;gap:.9rem;
  padding:.8rem .8rem calc(env(safe-area-inset-bottom,0px) + 1rem);background:linear-gradient(transparent,rgb(0 0 0 / .55))}
.ss-btn{border:0;border-radius:999px;background:rgb(255 255 255 / .16);color:#fff;font:inherit;
  width:2.8rem;height:2.8rem;display:inline-flex;align-items:center;justify-content:center;font-size:1.25em;line-height:1;cursor:pointer}
.ss-btn.ss-play{width:3.4rem;height:3.4rem;background:rgb(255 255 255 / .9);color:#111;font-size:1.1em}
.ss-top .ss-btn{width:2.4rem;height:2.4rem;font-size:1em}
.ss-btn[hidden]{display:none}
.slideshow .ss-top,.slideshow .ss-ctl{transition:opacity .25s}
.slideshow.bare .ss-top,.slideshow.bare .ss-ctl{opacity:0;pointer-events:none}
/* Nhạc nền của trình chiếu: tên bài nhỏ trên thanh trên; bảng chọn bài nổi trên hàng nút. */
.ss-tune{flex:none;max-width:40%;font-size:.78em;opacity:.8;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ss-menu{position:absolute;left:50%;transform:translateX(-50%);bottom:calc(env(safe-area-inset-bottom,0px) + 5.4rem);
  width:min(22rem,calc(100% - 2rem));max-height:60vh;overflow-y:auto;padding:.4rem;border-radius:14px;
  background:rgb(28 28 30 / .96);box-shadow:0 6px 24px rgb(0 0 0 / .5)}
.ss-menu[hidden]{display:none}
.ss-mhead{margin:.3rem .5rem .4rem;font-size:.78em;letter-spacing:.04em;text-transform:uppercase;opacity:.6}
.ss-mitem{display:flex;align-items:center;gap:.5rem;width:100%;padding:.55rem .5rem;border:0;border-radius:9px;
  background:none;color:#fff;font:inherit;font-size:.95em;text-align:left;cursor:pointer}
.ss-mitem.on{background:rgb(255 255 255 / .1)}
.ss-mitem:disabled{opacity:.4;cursor:default}
.ss-check{flex:none;width:1.1rem;text-align:center}
.ss-save{margin-top:.3rem;border-top:1px solid rgb(255 255 255 / .12);border-radius:0 0 9px 9px;color:#9fd8c6}
.ss-mnote{margin:.4rem .5rem .3rem;font-size:.8em;opacity:.65}
/* Thích (♥) trong thư viện ảnh (081): nút ♥ dùng .evlike của thẻ sự kiện; trên ô chỉ một nhãn nhỏ. */
.ph-hrow{display:flex;align-items:center;gap:.6rem;margin-top:.25rem}
.ph-hrow .ph-h{margin:0;min-width:0;flex:1}
.ph-hrow .evlike{flex:none;margin-right:0}
.al-pbar .evlike{margin-right:0}
.al-pbar .ph-heart-l{margin-right:auto}
.al-likes{position:absolute;left:.3rem;top:.3rem;padding:.05rem .35rem;border-radius:6px;
  background:rgb(0 0 0 / .55);color:#fff;font-size:.72em;font-weight:600;line-height:1.5}
.al-likes.on{background:#c0392b}
.shotbox-btn.on{background:#fbe3e0;color:#c0392b}
/* Nút ♥ là viên thuốc (trái tim + con số): thoát khỏi bề rộng cố định của #v-photos .eb, và giữ màu
   đỏ của .evlike.on thay vì màu xanh của .eb.on (nút đang mở). */
#v-photos .eb.evlike{width:auto;min-width:2.15rem;padding:0 .65rem;gap:.3rem}
#v-photos .eb.evlike svg{flex:none;width:1.05rem;height:1.05rem}
#v-photos .eb.evlike.on{color:#c0392b;border-color:#c0392b;background:var(--card)}
.ss-tune.tap{cursor:pointer;opacity:1;padding:.25rem .6rem;border-radius:999px;background:#fff;color:#111;font-weight:600}
.ss-diag{margin:.5rem .5rem .2rem;font-size:.7em;line-height:1.4;opacity:.45;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}

/* ---- Quản trị: lượt đọc (095) ---- */
.vstats > summary{cursor:pointer;list-style:none}
.vstats > summary::-webkit-details-marker{display:none}
.vstats > summary h2{display:inline}
.vstats > summary h2::after{content:" \203A";color:var(--muted);display:inline-block;transition:transform .15s}
.vstats[open] > summary h2::after{transform:rotate(90deg)}
.vstats h3{margin:1rem 0 .2rem;font-size:1em}
.vrow{display:grid;grid-template-columns:1fr auto;gap:.1rem .8rem;padding:.55rem .9rem;align-items:center}
.vrow + .vrow{border-top:1px solid var(--line)}
.vrow .note{overflow-wrap:anywhere}
.vrow .vnum{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}
.vrow .vnum b{font-size:1.1em}
.vmore > summary{cursor:pointer;padding:.55rem .9rem;color:var(--pine)}
