/* lifelog. The chat follows the familiar messenger layout (list, bubbles, unread numbers);
   colours and shapes are this app's own. */
:root {
  /* Every colour, size, layer and duration the rules use is named here, and the rules use the names
     (AGENTS.md 「화면 배치」: no raw colour in a rule). The dark scheme below sets the same names again;
     a new look is a change to these lines. */
  /* surfaces */
  --bg: #ffffff;                            /* the page */
  --surface: #ffffff;                       /* bars and panels on it: composer, emoticon panel, a focused field */
  --surface-glass: rgba(255,255,255,.96);   /* a bar floating over content (new message, notice) */
  --sheet: #ffffff;                         /* a sheet, and the bars that stick inside one */
  --control: #ffffff;                       /* a button, a chip, a cell to pick: on the page and on a sheet alike */
  --soft: #f6f6f6;                          /* fields, chips, quiet fills */
  --paper: #f3efe3;                         /* the 지난 오늘 card */
  --veil: rgba(255,255,255,.6);             /* a light film over the room */
  --glass-dark: rgba(255,255,255,.12);      /* buttons on the photo viewer */
  /* text and lines */
  --fg: #191919;
  --fg-2: #555555;                          /* secondary text that is not faint (chips) */
  --mine-muted: #5f5f5f;                    /* faint text on my yellow bubble */
  --avatar-ink: #1f2a24;                    /* initials on the pastel faces (white was 1.6-2.5:1) */
  --muted: #6b6b6b;                         /* faint text (4.5:1 on --soft too; it was #8a8a8a) */
  --on-fg: #ffffff;                         /* text on --fg (a chosen segment or chip) */
  --on-accent: #ffffff;                     /* text and icons on --accent */
  --on-badge: #ffffff;                      /* the number on --badge (white in both schemes) */
  --on-photo: #ffffff;                      /* text on photos and dark pills (toast, viewer, cell badges) */
  --on-photo-2: rgba(255,255,255,.85);
  --line: #ececec;                          /* dividers */
  --line-strong: #8a8a8a;                   /* the edge a control is seen by (3:1 on --bg) */
  --hairline: rgba(0,0,0,.12);              /* a divider over a coloured fill */
  --press: rgba(0,0,0,.06);                 /* a pressed or hovered fill */
  --track: rgba(0,0,0,.1);                  /* the empty part of a progress bar */
  /* meaning */
  --accent: #2e7d5b;
  --accent-ink: #256a4c;                    /* the accent as small text, on --accent-tint too */
  --accent-tint: #e3f1ea;                   /* a chosen cell, a shared chip */
  --danger: #c9372c;                        /* red text: errors, 지우기, money out, Sunday; badges */
  --danger-bg: #fdecea;
  --danger-ink: #7a2a23;                    /* text in a --danger-bg box */
  --badge: var(--danger);                   /* unread counts (white on it) */
  --warn-bg: #fff4d6;
  --warn-ink: #6b5200;
  --warn-line: #f0d48a;
  --income: #2a62c9;                        /* money in; Saturday */
  --expense: var(--danger);
  --link: #1b5fb8;
  --hilite: #ffe48a;                        /* a word found by search */
  --hilite-soft: #ffe9a8;                   /* a chosen reaction or mood */
  --hilite-pale: #fff4cc;                   /* my reaction chip */
  --hilite-ring: #f2c230;
  --flash-rgb: 255, 196, 0;                 /* the ring a message found or jumped to flashes with */
  --heart: #ff5a6e;
  /* the chat room */
  --room-bg: #b9cad8;
  --room-ink: #3c4a56;                      /* text straight on --room-bg */
  --room-ink-2: #4a5866;                    /* a quote; a placeholder bubble's text */
  --room-ink-strong: #34424f;               /* a centred system line, on its grey pill */
  --room-line: #b9c3cc;
  --room-rule: rgba(0,0,0,.18);
  --mine: #fee36e;
  --mine-veil: rgba(254, 227, 110, .5);
  /* My bubble is yellow in both schemes, so what is on it does not follow the scheme: */
  --mine-ink: #191919;                      /* text on --mine; the send button's icon */
  --mine-ink-2: #4a5866;                    /* a quote in my bubble, the bar of photos going up */
  --mine-hairline: rgba(0,0,0,.12);
  --link-mine: #1b5fb8;
  --other: #ffffff;
  --unread: #e0a800;                        /* the 「1」: stays yellow (사용자 결정 2026-10-29), not a contrast fix */
  /* over photos, and what shows before they load */
  --pill-dark: rgba(0,0,0,.45);             /* a pill on the room (the day line, 최신 대화로) */
  --photo-pill: rgba(0,0,0,.5);             /* a pill over a photo (+3, ▶ 1:23, ♥ 2): dark in both schemes */
  --pill-strong: rgba(0,0,0,.6);
  --scrim: rgba(0,0,0,.35);                 /* behind a sheet */
  --toast-bg: rgba(30,30,30,.9);
  --ph: #dddddd;                            /* a picture not loaded yet */
  --ph-dark: #222222;                       /* a video without a cover */
  --stage: #000000;                         /* the photo viewer */
  /* type */
  --font: -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", sans-serif;
  --font-mono: ui-monospace, Menlo, Consolas, monospace;
  --fs-2xs: 11px;
  --fs-xs: 12px;
  --fs-sm: 13px;
  --fs-md: 14px;
  --fs-base: 15px;
  --fs-lg: 16px;                            /* inputs: iOS zooms into anything smaller */
  --fs-xl: 18px;
  --fs-title: 21px;
  --lh-tight: 1.25;
  --lh-base: 1.45;
  --lh-read: 1.6;                           /* reading text (hints, notes) -- not bubbles or entry bodies */
  /* spacing and shape */
  --sp-1: 2px;
  --sp-2: 4px;
  --sp-3: 6px;
  --sp-4: 8px;
  --sp-5: 10px;
  --sp-6: 12px;
  --sp-7: 14px;                             /* the page's side gutter */
  --sp-8: 16px;
  --sp-9: 18px;
  --sp-10: 24px;
  --tap: 44px;                              /* the smallest a thing to tap is (its hit area, not always its look) */
  --r-xs: 4px;
  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 12px;
  --r-xl: 14px;
  --r-2xl: 16px;
  --r-3xl: 20px;
  --r-pill: 999px;
  --r-avatar: 37%;                          /* a face's rounded square at any size */
  /* depth and motion */
  --shadow-float: 0 2px 10px rgba(0,0,0,.16);
  --z-raise: 1;
  --z-float: 2;
  --z-head: 5;
  --z-fab: 10;                              /* the floating create button: over the page, under every sheet and the viewer */
  --z-sheet: 30;
  --z-viewer: 40;
  --z-viewer-sheet: 45;
  --z-toast: 50;
  --dur: .2s;
  --disabled: .45;
  --off: .55;                               /* a paused or undone row */
  /* the screen */
  --tab-h: 58px;
  --fab-size: 56px;                         /* the floating create button (app.js fab()): its height, and its width when round */
  --fab-lift: 0px;                          /* how far the toasts go up: the button's height and a gap while one is shown */
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  /* The content column on a tablet or a PC (사용자 결정 2026-10-29): at most --content-max wide and
     centred. The scroll boxes stay the window's width (their scroll bar at its edge) and take this as
     side padding: half of what is left beside the column, or the notch's inset when that is more. A
     % here is of the box the rule is on: use these only on boxes as wide as the window (.view, the
     tab bar, the chat room's rows and bars). */
  --content-max: 760px;
  --col-l: max(var(--safe-l), calc((100% - var(--content-max)) / 2));
  --col-r: max(var(--safe-r), calc((100% - var(--content-max)) / 2));
  /* The largest a sticker cell gets in the emoticon panel (and the pack sheets): past it, a wider
     screen shows more per row instead. This one value is the knob (AGENTS.md 「화면 배치」). At 108:
     phones 352-436 px wide four to a row as before, 320 px three (was four of 73 px), wider more. */
  --emo-cell-max: 108px;
  color-scheme: light;
}
/* Dark: the system's setting decides (사용자 결정 2026-10-29 -- no switch in the app). Only colours
   change, by these same names; my bubble stays yellow with dark text (--mine*, --link-mine), photo
   pills, the viewer and the faces' pastels stay as they are. tests/pure.test.mjs 「contrast: …」 checks
   the key pairs in both schemes. */
@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --bg: #121212;
    --surface: #1c1c1e;
    --surface-glass: rgba(28,28,30,.96);
    --sheet: #1c1c1e;
    --control: #1c1c1e;
    --soft: #2c2c2e;
    --paper: #2a2620;
    --veil: rgba(255,255,255,.08);
    --fg: #ececec;
    --fg-2: #c7c7c7;
    --muted: #9b9b9b;
    --on-fg: #121212;
    --on-accent: #0f1f18;
    --line: #2f2f31;
    --line-strong: #7c7c80;
    --hairline: rgba(255,255,255,.14);
    --press: rgba(255,255,255,.08);
    --track: rgba(255,255,255,.14);
    --accent: #4fb38a;
    --accent-ink: #7fd0ac;
    --accent-tint: #183a2c;
    --danger: #ff6b5e;
    --danger-bg: #3b1e1b;
    --danger-ink: #ffb4ab;
    --badge: #c9372c;
    --warn-bg: #3a2f12;
    --warn-ink: #f3d27a;
    --warn-line: #6b5518;
    --income: #6d9eff;
    --link: #8ab4f8;
    --hilite: #6b5a14;
    --hilite-soft: #4d4216;
    --hilite-pale: #3d3412;
    --hilite-ring: #c9a227;
    --room-bg: #1f2a33;
    --room-ink: #9fb0bf;
    --room-ink-2: #9fb0bf;
    --room-ink-strong: #c3d0db;
    --room-line: #4a5a68;
    --room-rule: rgba(255,255,255,.18);
    --mine-veil: rgba(254, 227, 110, .9);
    --other: #2c2c2e;
    --unread: #e0b700;
    --pill-dark: rgba(255,255,255,.14);
    --scrim: rgba(0,0,0,.55);
    --toast-bg: rgba(58,58,60,.96);
    --ph: #3a3a3c;
    --shadow-float: 0 2px 10px rgba(0,0,0,.5);
  }
}

* { box-sizing: border-box; }
/* On html too: the spec takes the viewport's overscroll-behavior from the root element, not from
   body. No bounce or pull-to-refresh of the page itself -- only the boxes inside it scroll. Up and
   down only (-y here and on the boxes): a sideways swipe still reaches the browser's back. */
html { margin: 0; height: 100%; overscroll-behavior-y: none; }
body {
  margin: 0; height: 100%; overscroll-behavior-y: none;
  background: var(--bg);
  color: var(--fg);
  font: var(--fs-base)/var(--lh-base) var(--font);
  -webkit-text-size-adjust: 100%;
}
button, input, textarea, select { font: inherit; color: inherit; }
a { color: inherit; }
.hidden { display: none !important; }
.muted { color: var(--muted); }
.small { font-size: var(--fs-sm); }
.ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.icon { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* icon(name, 'em'): the size of the text it stands in (a pin by a name, ✓ before a word, › after one) */
.icon.em { width: 1.15em; height: 1.15em; vertical-align: -.2em; }
.icon.solid { fill: currentColor; }   /* icon(name, 'solid'): a given heart, ▶ */

/* ------------------------------------------------------------------ controls */
/* Hit areas: where a control looks smaller than a finger (--tap, 44px), an ::after a little bigger
   than it takes the taps around it -- what one sees stays the same size. Only into empty space
   beside it: over a neighbour it would steal that one's taps (so some get less than 44px). .btn is
   positioned for it here, early, so a later position (.trash-restore: absolute) still wins. */
.btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 44px; padding: 0 18px; border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--control); cursor: pointer; text-decoration: none; word-break: keep-all; overflow-wrap: break-word;
}
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
.btn.small { min-height: 34px; padding: 0 12px; border-radius: var(--r-md); font-size: var(--fs-md); }
.btn.small::after { content: ''; position: absolute; inset: -5px 0; }
.btn.wide { width: 100%; }
.btn.link { border: 0; background: none; color: var(--muted); text-decoration: underline; }
.btn.danger, .btn.link.danger { color: var(--danger); }
.btn:disabled { opacity: var(--disabled); cursor: default; }
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center; width: var(--tap); height: var(--tap);
  border: 0; border-radius: 50%; background: none; cursor: pointer; color: inherit; text-decoration: none;
}
.icon-btn:active { background: var(--press); }
.icon-btn.light { color: var(--on-photo); }
/* A ring for the keyboard only (a tap or a click shows none). A sheet takes the focus as it opens and
   needs none of its own. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sheet:focus { outline: none; }
/* With a mouse (PC), what a click would choose shows under the pointer. Not on touch screens, where
   a hover sticks after the tap. */
@media (hover: hover) {
  .icon-btn:hover, .room-row:hover, .menu-item:hover, .result:hover, .person:hover, .set-row:hover, .tx:hover,
  .ledger-label-row:hover, .ledger-due-row:hover, .past-row:hover, .talk-btn:hover, .react-all-btn:hover,
  .emo-sticker:hover, .emo-emoji:hover, .emo-tab:hover, .ledger-month-pick:hover, .routine-day-pick:hover { background: var(--press); }
  /* :where() keeps these weak (.btn:hover, .btn.link:hover): a button's own colours set later win --
     the viewer's dark buttons, the empty album's main one, a red 지우기 link. */
  .btn:where(:not(.primary, .link, :disabled)):hover { background: var(--soft); }
  .btn.link:where(:not(.danger, :disabled)):hover { color: var(--fg); }
  .tab:hover { color: var(--fg); }
  /* the floating create button keeps its accent: the press fill goes over it */
  .fab:hover { box-shadow: var(--shadow-float), inset 0 0 0 var(--fab-size) var(--press); }
}
/* min-width 0: in a row (flex or grid) an input would not shrink below its own default width. */
.field {
  display: block; width: 100%; min-width: 0; min-height: 46px; padding: 10px 12px; margin: 6px 0;
  border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--soft); font-size: var(--fs-lg);
  box-shadow: inset 0 -1px 0 var(--line-strong);   /* its edge at 3:1, which the light border is not */
}
.field:focus { outline: 2px solid var(--accent); outline-offset: -1px; background: var(--surface); }
.field.tall { min-height: 180px; resize: vertical; }
.field.amount { font-size: 22px; font-weight: 600; text-align: right; }
.label { display: block; margin: 10px 0 4px; font-size: var(--fs-sm); color: var(--muted); word-break: keep-all; overflow-wrap: break-word; }
.seg { display: flex; gap: 6px; margin: 8px 0; }
.seg button { flex: 1; min-height: 40px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--control); cursor: pointer; }
.seg button.on { background: var(--fg); border-color: var(--fg); color: var(--on-fg); }
.chip { display: inline-block; padding: 2px 8px; border-radius: var(--r-pill); background: var(--soft); color: var(--fg-2); font-size: var(--fs-xs); white-space: nowrap; }
.chip.shared { background: var(--accent-tint); color: var(--accent-ink); }
/* in a row of the routine manager the chip gives way first (its text ends in …) */
.routine-group-head .chip { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.check { display: flex; align-items: center; gap: 8px; margin: 8px 0; word-break: keep-all; overflow-wrap: break-word; }
.row-btns { display: flex; gap: 8px; }
.row-btns .btn { flex: 1; }
/* The line under a form or on a waiting screen. Red unless it says otherwise: most of them show an
   error (errText), and pages keep adding such lines. .wait (waiting, or a neutral word such as
   로그아웃했어요) is faint, .ok (approved) green; .err says red outright. */
.status { min-height: 1.4em; color: var(--danger); text-align: center; }
.status.wait { color: var(--muted); }
.status.ok { color: var(--accent-ink); }
.status.err { color: var(--danger); }
/* A warning to read before doing something (the login code): a box of its own, not an error line. */
.note-warn { margin: 6px 0; padding: 10px 12px; border-radius: var(--r-lg); background: var(--warn-bg); color: var(--warn-ink); line-height: var(--lh-read); }
.empty { text-align: center; padding: 40px 0; }

/* Centred by its margins inside the screen's width (left 50% would leave it half the screen to wrap in). */
#toast {
  position: fixed; left: 0; right: 0; bottom: calc(var(--tab-h) + var(--safe-b) + 20px + var(--fab-lift)); transform: translateY(20px);
  width: max-content; max-width: 86vw; margin: 0 auto; padding: 10px 16px; border-radius: var(--r-3xl); background: var(--toast-bg); color: var(--on-photo);
  opacity: 0; pointer-events: none; transition: opacity var(--dur), transform var(--dur); z-index: var(--z-toast); font-size: var(--fs-md);
  word-break: keep-all; overflow-wrap: break-word;
}
#toast.show { opacity: 1; transform: translateY(0); }
/* Asked for less motion (the system setting): nothing slides, grows or fills over time -- it is just
   there. A found message's ring shows still for the same time and goes (the .flash class stays on,
   the animation's end takes the ring away). Nothing in app.js waits for a transition's end. */
@media (prefers-reduced-motion: reduce) {
  #toast, .cell.on img, .cell.on .cell-file, .viewer-img.animate, .sending-meter, .upload-meter, .sheet.settling, .fab { transition: none; }
  .row.flash .bubble, .row.flash .jumbo, .row.flash .sticker-msg, .day-line.flash span, .entry.flash, .tx.flash { animation: flash-still 1.6s step-end; }
}
@keyframes flash-still { from { box-shadow: 0 0 0 4px rgba(var(--flash-rgb), .85); } to { box-shadow: none; } }

.avatar {
  flex: none; width: 46px; height: 46px; border-radius: var(--r-avatar); display: flex; align-items: center; justify-content: center;
  color: var(--avatar-ink); font-weight: 700; font-size: var(--fs-xl); overflow: hidden;
}
.avatar.small { width: 32px; height: 32px; font-size: var(--fs-md); }
.avatar.large { width: 64px; height: 64px; font-size: 26px; }
.avatar.family { background: var(--accent); }
.avatar.family .icon { width: 28px; height: 28px; stroke: var(--on-accent); }
.avatar.photo { background: var(--soft); }
.avatar.photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.room-edit-pic { display: flex; justify-content: center; padding: 8px 0 12px; }
.room-edit-pic .avatar { width: 88px; height: 88px; font-size: 34px; }
/* the faces of a group: 2 x 2, an empty cell (three people) the quiet fill */
.avatar.group { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--soft); }
.avatar.group span { display: flex; align-items: center; justify-content: center; font-size: var(--fs-2xs); }

/* ------------------------------------------------------------------ sign-in screens */
.auth { max-width: 380px; margin: 0 auto; padding: calc(48px + var(--safe-t)) 24px 32px; display: flex; flex-direction: column; gap: 10px; }
.brand { text-align: center; margin-bottom: 18px; }
.brand-mark { width: 72px; height: 72px; margin: 0 auto 10px; border-radius: 26px; background: var(--accent); display: flex; align-items: center; justify-content: center; }
.brand-mark .icon { width: 40px; height: 40px; stroke: var(--on-accent); }
.brand h1 { margin: 0; font-size: 28px; word-break: keep-all; overflow-wrap: anywhere; }
.brand p { margin: 4px 0 0; }
.hint { font-size: var(--fs-sm); text-align: center; line-height: var(--lh-read); word-break: keep-all; overflow-wrap: break-word; }
.match { text-align: center; padding: 16px; border-radius: var(--r-2xl); background: var(--soft); }
.match-device { margin: 4px 0; font-size: 20px; font-weight: 700; overflow-wrap: anywhere; }
.match-code { font-size: 44px; font-weight: 800; letter-spacing: .18em; font-variant-numeric: tabular-nums; }
.match-code.small { font-size: 26px; letter-spacing: .12em; }
.match-code.reg-code { font-family: var(--font-mono); letter-spacing: .2em; margin: 8px 0; }
.field.code { font-family: var(--font-mono); font-size: 20px; letter-spacing: .2em; }

/* ------------------------------------------------------------------ shell */
/* The shell is pinned to the screen and never scrolls; each page has one scroll box, .view (a chat
   room: .msgs). Fixed, it leaves the document nothing to scroll -- in flow at 100dvh it could come
   out taller than the screen (iOS home-screen apps, a toolbar, the keyboard), and a drag past the
   top of the messages could then move the whole app and take the composer off the screen (read
   from the code, not seen on a device). contain: a box's scroll stops at its own edge instead of
   passing on to the page. */
.shell { position: fixed; top: 0; right: 0; bottom: 0; left: 0; display: flex; flex-direction: column; }
/* Left and right: the content column (--col-l/-r: centred at most 760px, past a turned phone's notch).
   Padding, not a narrower box: the scroll bar stays at the window's edge. The page's own background
   fills the sides; the chat room pads its own rows and bars instead (its background is not the page's). */
.view {
  flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior-y: contain; -webkit-overflow-scrolling: touch;
  padding: 0 var(--col-r) 12px var(--col-l);
}
.tabs {
  flex: none; display: flex; height: calc(var(--tab-h) + var(--safe-b)); padding: 0 var(--col-r) var(--safe-b) var(--col-l);
  border-top: 1px solid var(--line); background: var(--surface-glass);
}
.tab { position: relative; flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; text-decoration: none; color: var(--muted); font-size: var(--fs-2xs); }
.tab.active { color: var(--fg); }
.tab .badge { position: absolute; top: 5px; left: 50%; margin-left: 6px; min-width: 18px; height: 18px; padding: 0 5px; border-radius: var(--r-pill); background: var(--badge); color: var(--on-badge); font-weight: 700; font-size: var(--fs-2xs); line-height: 18px; text-align: center; }
.shell.in-room .tabs { display: none; }
.shell.in-room .view { padding: 0; overflow: hidden; }
/* A tab page's main 「create」 action (app.js fab(); 사용자 결정 2026-10-29, AGENTS.md 「화면 배치」): round, at the
   bottom right where a thumb reaches -- over the tab bar (the home indicator is in its height) and at the content
   column's right edge (--col-r: the notch on a phone, the 760px column on a wide screen; fixed, a % in it is of the
   window, as on .view). It stays put while the page scrolls. A word that matters (이어 쓰기) makes it a pill, the
   icon and the word. Under every sheet and the photo viewer (--z-fab); a chat room makes none. */
.fab {
  position: fixed; right: calc(var(--col-r) + var(--sp-8)); bottom: calc(var(--tab-h) + var(--safe-b) + var(--sp-8)); z-index: var(--z-fab);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-4);
  min-width: var(--fab-size); height: var(--fab-size); padding: 0; border: 0; border-radius: var(--r-pill);
  background: var(--accent); color: var(--on-accent); box-shadow: var(--shadow-float); cursor: pointer;
  font-size: var(--fs-base); font-weight: 600; white-space: nowrap; word-break: keep-all; transition: transform var(--dur);
}
.fab.extended { padding: 0 var(--sp-9) 0 var(--sp-8); }
/* Pressed: a little smaller, the press fill over the accent (just there, not shrinking, with less motion). */
.fab:active { transform: scale(.94); box-shadow: var(--shadow-float), inset 0 0 0 var(--fab-size) var(--press); }
/* The keyboard's ring, on a ring of the page's colour: it shows over a photo too. */
.fab:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; box-shadow: var(--shadow-float), 0 0 0 3px var(--bg); }
/* The page leaves room under its last row (the button, a gap under it and one over it), so the end of a list
   never stays under the button; and the toasts show above it rather than over it. Without :has() (older
   browsers) every page leaves the room and the toasts stay where they are. */
.view:has(> .fab:not(.hidden)) { padding-bottom: calc(var(--fab-size) + 2 * var(--sp-8)); }
body:has(.view > .fab:not(.hidden)) { --fab-lift: calc(var(--fab-size) + var(--sp-8)); }
@supports not selector(:has(a)) {
  .view { padding-bottom: calc(var(--fab-size) + 2 * var(--sp-8)); }
}

/* The header is one row that wraps: ← (when there is one), the title taking what is left but never
   less than 6em, and the buttons on the right (.head-btns: margin-left auto). Buttons that do not fit
   beside the title go to a second row, still on the right, rather than past the edge of a narrow
   phone: an album's name, 「다음 20장 저장」 at 320 px. The album's sticky day headings sit under it
   at its measured height (--day-top, app.js drawAlbumHead). */
.top {
  position: sticky; top: 0; z-index: var(--z-head); display: flex; flex-wrap: wrap; align-items: center; column-gap: 8px; row-gap: 4px;
  min-height: 56px; padding: calc(4px + var(--safe-t)) 10px 4px 14px; background: var(--bg);
}
.top h2 { flex: 1 1 0; min-width: 6em; margin: 0; font-size: var(--fs-title); font-weight: 700; word-break: keep-all; overflow-wrap: anywhere; }
.month-nav { display: flex; align-items: center; justify-content: center; gap: 12px; padding: 4px 0 10px; }
.month-nav strong { min-width: 120px; text-align: center; }
.card { margin: 10px 14px; padding: 14px; border: 1px solid var(--line); border-radius: var(--r-2xl); }
/* 임시 로그인: the band on top of the app with the time left and 로그아웃 (app.js tempBand) */
.card.temp-band { display: flex; align-items: center; gap: 8px; margin: 8px calc(14px + var(--col-r)) 0 calc(14px + var(--col-l)); padding: 8px 12px; }
.card h3 { margin: 0 0 10px; font-size: var(--fs-base); word-break: keep-all; overflow-wrap: anywhere; }
/* reading text in a card or a sheet (what a setting does, how to install): looser lines, whole words */
.card p, .sheet-body > p { line-height: var(--lh-read); word-break: keep-all; overflow-wrap: break-word; }
.card.me { display: flex; align-items: center; gap: 14px; }

/* ------------------------------------------------------------------ chat list */
.room-list { list-style: none; margin: 0; padding: 0; }
/* A long press on a row opens the room's menu (pin, read), so no callout or selection. */
.room-row { display: flex; align-items: center; gap: 12px; padding: 9px 16px; text-decoration: none; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
.room-row:active { background: var(--soft); }
.room-main { flex: 1; min-width: 0; }
.room-title { display: flex; gap: 6px; align-items: baseline; font-weight: 600; }
.count { color: var(--muted); font-weight: 400; font-size: var(--fs-md); margin-left: 4px; }
.room-preview { color: var(--muted); font-size: var(--fs-md); margin-top: 2px; }
.room-side { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.room-time { color: var(--muted); font-size: var(--fs-xs); }
.unread-badge { min-width: 20px; height: 20px; padding: 0 6px; border-radius: var(--r-pill); background: var(--badge); color: var(--on-badge); font-size: var(--fs-xs); font-weight: 700; line-height: 20px; text-align: center; }
.pick-list { list-style: none; margin: 0; padding: 0; }
.pick { display: flex; align-items: center; gap: 10px; padding: 8px 0; }

/* ------------------------------------------------------------------ chat room */
/* The room is exactly the page's height and clips what does not fit, so nothing (a focus, a jump
   to a message) can scroll the page around it: only .msgs scrolls. clip where it is known. */
.room { height: 100%; display: flex; flex-direction: column; overflow: hidden; overflow: clip; background: var(--room-bg); }
/* The room's head is a grid, not the header's wrapping row: ← at the start of the left column, the
   title centred between two columns as wide as the two buttons on the right (2 × 44px, no gap). */
.room-head {
  display: grid; grid-template-columns: 88px minmax(0, 1fr) 88px; column-gap: 0; text-align: center; background: var(--room-bg);
  padding-left: calc(14px + var(--col-l)); padding-right: calc(10px + var(--col-r));
}
.room-head > :first-child { justify-self: start; }
.room-head h2 { min-width: 0; font-size: 17px; }
.room-head .head-btns { gap: 0; }
.msgs { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior-y: contain; padding: 6px calc(10px + var(--col-r)) 12px calc(10px + var(--col-l)); -webkit-overflow-scrolling: touch; }
.older { text-align: center; font-size: var(--fs-xs); padding: 6px 0; color: var(--room-ink); }   /* on the room: not --muted (3.2:1 there) */
.day-line { text-align: center; margin: 14px 0 8px; }
.day-line span { display: inline-block; padding: 4px 12px; border-radius: var(--r-pill); background: var(--pill-dark); color: var(--on-photo); font-size: var(--fs-xs); }
.row { display: flex; margin-top: 3px; }
.row.first { margin-top: 10px; }
.row.mine { justify-content: flex-end; }
.row.other { gap: 8px; }
.row .avatar, .avatar-space { flex: none; width: 38px; height: 38px; font-size: var(--fs-base); }
.col { min-width: 0; max-width: 76%; }
.name { font-size: var(--fs-sm); color: var(--room-ink); margin: 0 0 3px 2px; }
.line { display: flex; align-items: flex-end; gap: 5px; }
.mine-col { max-width: 78%; min-width: 0; display: flex; flex-direction: column; align-items: flex-end; }
.row.mine .line { max-width: 100%; }
.meta { flex: none; display: flex; flex-direction: column; font-size: var(--fs-2xs); color: var(--room-ink); line-height: var(--lh-tight); }
.row.mine .meta { align-items: flex-end; }
.meta .unread { color: var(--unread); font-weight: 700; font-size: var(--fs-2xs); }
.meta .edited { color: var(--room-ink); }
/* A text selection on a bubble would cancel the long press that opens the message menu
   (which has 복사 instead); the callout would cover it. */
.row .bubble, .row .sticker-msg { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
.bubble { position: relative; min-width: 0; padding: 8px 11px; border-radius: var(--r-xl); background: var(--other); word-break: break-word; overflow-wrap: anywhere; }
.bubble.text { white-space: pre-wrap; }
.row.mine .bubble { background: var(--mine); color: var(--mine-ink); }
.bubble.head::before { content: ""; position: absolute; top: 0; width: 10px; height: 12px; background: inherit; }
.row.other .bubble.head { border-top-left-radius: 4px; }
.row.mine .bubble.head { border-top-right-radius: 4px; }
.bubble.photo { padding: 0; border: 0; overflow: hidden; background: var(--ph); cursor: zoom-in; display: block; }
.bubble.photo img { display: block; width: min(240px, 60vw); height: auto; object-fit: cover; }
/* a video: its cover (or a dark box) with 「▶ 1:23」; the same badge sits on the album cell */
.bubble.photo.video { background: var(--ph-dark); }
.bubble.video .video-blank { display: flex; align-items: center; justify-content: center; width: min(240px, 60vw); aspect-ratio: 16 / 9; color: var(--on-photo-2); font-size: 34px; }
.bubble.video .video-blank .icon { width: 40px; height: 40px; }
.video-badge { position: absolute; right: 6px; bottom: 6px; display: flex; align-items: center; gap: 4px; padding: 1px 7px; border-radius: var(--r-pill); background: var(--pill-strong); color: var(--on-photo); font-size: var(--fs-2xs); line-height: 16px; pointer-events: none; }
.video-play { font-size: 9px; }
.bubble.deleted { color: var(--muted); font-style: italic; }
.row.mine .bubble.deleted { color: var(--mine-muted); }
.bubble.file { display: inline-block; text-decoration: underline; }
.bubble a { color: var(--link); }
.row.mine .bubble a { color: var(--link-mine); }
.composer { flex: none; display: flex; align-items: flex-end; gap: 6px; padding: 6px calc(8px + var(--col-r)) calc(6px + var(--safe-b)) calc(8px + var(--col-l)); background: var(--surface); }
.composer .plus { position: relative; flex: none; width: 40px; height: 40px; border: 0; border-radius: 50%; background: none; color: var(--muted); cursor: pointer; display: flex; align-items: center; justify-content: center; }
/* min-width 0: a textarea's own default width (its cols) would push the send button off a narrow phone */
.composer-input { flex: 1; min-width: 0; min-height: 40px; max-height: 120px; padding: 9px 12px; border: 0; border-radius: var(--r-3xl); background: var(--soft); resize: none; font-size: var(--fs-lg); line-height: 1.35; }
.composer-input:focus { outline: none; }
.send { position: relative; flex: none; width: 40px; height: 40px; border: 0; border-radius: 50%; background: var(--mine); color: var(--mine-ink); cursor: pointer; display: flex; align-items: center; justify-content: center; }
.send:disabled { background: var(--soft); color: var(--line-strong); }
/* the composer's 40px buttons take 2px more each way: 44px, in the 6px gaps between them */
.composer .plus::after, .composer .emo-btn::after, .send::after { content: ''; position: absolute; inset: -2px; border-radius: 50%; }
/* the menu a long press on a message opens */
.menu { display: flex; flex-direction: column; padding-bottom: 4px; }
.menu-item { min-height: 48px; padding: 0 6px; border: 0; border-top: 1px solid var(--line); background: none; text-align: left; font-size: var(--fs-lg); cursor: pointer; }
.menu-item:first-child { border-top: 0; }
.menu-item.danger { color: var(--danger); }
/* replies: the bar above the input and the quote at the top of a reply bubble */
.reply-bar { flex: none; display: flex; align-items: center; gap: 6px; padding: 6px calc(6px + var(--col-r)) 6px calc(14px + var(--col-l)); background: var(--surface); border-top: 1px solid var(--line); }
.reply-main { flex: 1; min-width: 0; }
.reply-who { font-size: var(--fs-sm); font-weight: 600; }
.reply-what { font-size: var(--fs-sm); color: var(--muted); }
.quote { display: flex; align-items: center; gap: 8px; width: 100%; min-width: 0; padding: 0 0 6px; margin: 0 0 6px; border: 0; border-bottom: 1px solid var(--hairline); background: none; text-align: left; cursor: pointer; white-space: nowrap; }
.quote-main { display: flex; flex-direction: column; min-width: 0; overflow: hidden; }
.quote-who { font-size: var(--fs-xs); font-weight: 600; }
.quote-what { font-size: var(--fs-xs); color: var(--room-ink-2); overflow: hidden; text-overflow: ellipsis; }
.row.mine .quote { border-bottom-color: var(--mine-hairline); }
.row.mine .quote-what { color: var(--mine-ink-2); }
.quote-thumb { flex: none; width: 36px; height: 36px; border-radius: var(--r-sm); object-fit: cover; }
.bubble.reply { min-width: 140px; }
/* reactions: the row in the menu and the chips under a bubble */
.react-pick { display: flex; justify-content: space-between; gap: 4px; padding: 4px 0 10px; }
/* padding and min-width 0 here and on the other emoji buttons in a row (.mood-btn, .emo-emoji): the
   browser's own button padding beside the emoji could make a row of them wider than a narrow phone */
.react-btn { flex: 1; min-width: 0; height: 46px; padding: 0; border: 0; border-radius: var(--r-xl); background: var(--soft); font-size: 24px; cursor: pointer; }
.react-btn.on { background: var(--hilite-soft); box-shadow: inset 0 0 0 2px var(--hilite-ring); }
.reacts { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 3px; }
.react-chip { position: relative; display: inline-flex; align-items: center; gap: 3px; height: 24px; padding: 0 8px; border: 1px solid var(--hairline); border-radius: var(--r-lg); background: var(--surface-glass); font-size: var(--fs-sm); cursor: pointer; }
.react-chip.mine { border-color: var(--hilite-ring); background: var(--hilite-pale); }
/* only into the gaps around it: more would reach the bubble above, whose long press opens its menu */
.react-chip::after { content: ''; position: absolute; inset: -3px -2px; }
.reactors { display: flex; gap: 10px; align-items: baseline; padding: 8px 0; border-top: 1px solid var(--line); }
.reactors:first-child { border-top: 0; }
.reactors-emoji { font-size: 20px; }
/* The reaction layer (the message menu, the emoji picker, who reacted, the chips): a long press there
   selects no text, calls up no callout and drags nothing (app.js reactLayer/guardLayer stop the rest). */
.react-layer, .react-layer * { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; -webkit-user-drag: none; }
.react-more { display: flex; align-items: center; justify-content: center; color: var(--muted); }
.react-more svg { width: 22px; height: 22px; }
.reacts .react-chip { max-width: 100%; }
.row.mine .reacts { justify-content: flex-end; }
/* every emoji, by category (the 「+」 of a reaction picker) */
/* sticks under the sheet's own head (--sheet-head-h, sheet() keeps it) */
.react-all-tabs { position: sticky; top: var(--sheet-head-h, 54px); z-index: var(--z-raise); display: flex; gap: 2px; overflow-x: auto; padding: 4px 0; background: var(--sheet); border-bottom: 1px solid var(--line); }
.react-all-tab { flex: none; width: 44px; height: 40px; border: 0; border-radius: var(--r-md); background: none; font-size: 20px; cursor: pointer; }
.react-all-cat { font-size: var(--fs-xs); margin: 10px 2px 4px; scroll-margin-top: calc(var(--sheet-head-h, 54px) + 52px); }
.react-all-grid { display: grid; gap: 2px; grid-template-columns: repeat(auto-fill, minmax(42px, 1fr)); }
.react-all-btn { height: 42px; border: 0; border-radius: var(--r-md); background: none; font-size: 26px; line-height: 1; cursor: pointer; }
.react-all-btn.on { background: var(--hilite-soft); box-shadow: inset 0 0 0 2px var(--hilite-ring); }
.react-all-btn:active, .react-all-tab:active { background: var(--soft); }
/* reading: the line where I stopped, and the bar for a new message while I read further up */
.msgs-wrap { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; }
.read-line { display: flex; align-items: center; gap: 8px; margin: 14px 4px 8px; color: var(--room-ink); font-size: var(--fs-xs); }
.read-line::before, .read-line::after { content: ""; flex: 1; height: 1px; background: var(--room-rule); }
.read-line span { padding: 2px 10px; border-radius: var(--r-pill); background: var(--veil); }
.new-bar { position: absolute; left: calc(10px + var(--col-l)); right: calc(10px + var(--col-r)); bottom: 8px; z-index: var(--z-float); display: flex; align-items: center; gap: 8px; min-height: 40px; padding: 6px 12px; border: 0; border-radius: var(--r-xl); background: var(--surface-glass); box-shadow: var(--shadow-float); text-align: left; cursor: pointer; }
.new-who { flex: none; font-weight: 600; font-size: var(--fs-sm); }
.new-what { flex: 1; font-size: var(--fs-sm); color: var(--fg-2); }
.new-down { flex: none; color: var(--muted); }
/* going to a day (room search) and the room drawer */
.day-jump { display: flex; align-items: center; gap: 8px; }
.day-jump .field { flex: 1; }
.drawer-head { margin: 4px 0; font-size: var(--fs-md); color: var(--muted); font-weight: 600; }
/* the notice over the top of the messages; folded, only its icon at the right */
.notice-bar { position: absolute; top: 6px; left: calc(10px + var(--col-l)); right: calc(10px + var(--col-r)); z-index: var(--z-float); display: flex; align-items: center; gap: 4px; padding: 0 4px 0 0; border-radius: var(--r-xl); background: var(--surface-glass); box-shadow: var(--shadow-float); }
.notice-bar.folded { left: auto; padding: 0; background: none; box-shadow: none; }
.msgs.under-notice { padding-top: 56px; }
.notice-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; min-height: 42px; padding: 6px 12px; border: 0; background: none; text-align: left; cursor: pointer; }
.notice-icon { flex: none; }
.notice-text { flex: 1; font-size: var(--fs-md); }
.notice-fold { flex: none; border: 0; background: none; color: var(--muted); font-size: var(--fs-sm); padding: 6px 8px; cursor: pointer; }
.notice-open { width: 40px; height: 40px; border: 0; border-radius: 50%; background: var(--surface-glass); box-shadow: var(--shadow-float); font-size: var(--fs-xl); cursor: pointer; }
/* profiles: my card in 더보기, a status under a name (drawer) and under a two-person room's title */
.me-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.me-status { font-size: var(--fs-md); overflow-wrap: anywhere; }
.person-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.person-status { font-size: var(--fs-xs); color: var(--muted); }
.room-head h2 .head-status { display: block; font-size: var(--fs-xs); font-weight: 400; color: var(--room-ink); }
.pin-mark { flex: none; font-size: var(--fs-xs); }
/* 나와의 채팅 before it exists: a button that looks like the other rows */
.room-row.self-row { width: 100%; border: 0; background: none; text-align: left; font: inherit; color: inherit; cursor: pointer; }
/* a topic room in the list: its topic, then who it is with (kept short so the topic leads) */
.room-with { flex: 0 1 auto; max-width: 45%; color: var(--muted); font-weight: 400; font-size: var(--fs-md); }
/* photo bundles: a grid in one bubble, the last cell 「+N」; the bubble of photos still going up */
.bubble.photos, .row .bubble.photos { display: grid; gap: 2px; width: min(240px, 60vw); padding: 0; overflow: hidden; background: transparent; }
/* Grid columns of images are minmax(0, 1fr): a bare 1fr may not get narrower than its content, and
   an image's own size could widen it past the screen (tests/pure.test.mjs keeps style.css to this). */
.bubble.photos.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.bubble.photos.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); width: min(270px, 66vw); }
.photos-cell { position: relative; aspect-ratio: 1; padding: 0; border: 0; background: var(--ph); cursor: zoom-in; overflow: hidden; }
.photos-cell img { display: block; width: 100%; height: 100%; object-fit: cover; }
.photos-file, .room-photo-file { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; font-size: var(--fs-2xs); color: var(--fg-2); }
.photos-more { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: var(--photo-pill); color: var(--on-photo); font-size: 20px; font-weight: 700; }
.bubble.sending { display: flex; flex-direction: column; gap: 6px; min-width: 150px; font-size: var(--fs-md); }
/* the bubble of photos going up is always mine (sendingRow) */
.sending-bar { display: block; height: 4px; border-radius: 2px; background: var(--mine-hairline); overflow: hidden; }
.sending-meter { display: block; height: 100%; width: 0; background: var(--mine-ink-2); transition: width var(--dur); }
.room.drop-over .msgs-wrap { outline: 3px dashed var(--accent); outline-offset: -8px; }
.send-preview { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px; margin-bottom: 10px; }
.send-preview img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: var(--r-sm); background: var(--soft); }
.send-preview-more { display: flex; align-items: center; justify-content: center; aspect-ratio: 1; border-radius: var(--r-sm); background: var(--soft); font-weight: 700; }
/* the room drawer's photos: a few latest in the drawer, all of them in their own sheet */
.room-photos { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 3px; margin-bottom: 8px; }
.room-photos.wide { grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); }
.room-photo { position: relative; aspect-ratio: 1; padding: 0; border: 0; border-radius: var(--r-xs); background: var(--soft); cursor: pointer; overflow: hidden; }
.room-photo img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* ------------------------------------------------------------------ search, imported history */
.head-btns { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: 6px; margin-left: auto; }
.results { display: flex; flex-direction: column; gap: 2px; min-height: 120px; }
.result { text-align: left; padding: 10px 4px; border: 0; border-bottom: 1px solid var(--line); background: none; cursor: pointer; }
.result-body { margin-top: 2px; word-break: break-word; }
.result mark, .result-body mark { background: var(--hilite); color: inherit; border-radius: 3px; padding: 0 1px; }
.latest { display: block; margin: 12px auto 4px; padding: 6px 14px; border: 0; border-radius: var(--r-pill); background: var(--pill-dark); color: var(--on-photo); cursor: pointer; font-size: var(--fs-sm); }
.row.flash .bubble, .row.flash .jumbo, .row.flash .sticker-msg, .day-line.flash span { animation: flash 1.6s ease-out 2; }
@keyframes flash { 0%, 100% { box-shadow: 0 0 0 0 rgba(var(--flash-rgb), 0); } 30% { box-shadow: 0 0 0 4px rgba(var(--flash-rgb), .85); } }
.entry.flash, .tx.flash { animation: flash 1.6s ease-out 2; border-radius: var(--r-md); }
.day-line.system span { background: var(--hairline); color: var(--room-ink-strong); }
.bubble.placeholder { color: var(--room-ink-2); font-style: italic; border: 1px dashed var(--room-line); background: var(--veil); }
.row.mine .bubble.placeholder { background: var(--mine-veil); color: var(--mine-ink-2); }

/* ------------------------------------------------------------------ stickers and emoji */
.composer .emo-btn { position: relative; flex: none; width: 40px; height: 40px; border: 0; border-radius: 50%; background: none; color: var(--muted); cursor: pointer; display: flex; align-items: center; justify-content: center; }
.composer .emo-btn.on { color: var(--accent); }
.sticker-preview { flex: none; position: relative; display: flex; justify-content: flex-end; align-items: flex-start; gap: 4px; padding: 6px calc(10px + var(--col-r)) 6px calc(10px + var(--col-l)); background: var(--veil); }
.sticker-preview img { width: 96px; height: 96px; object-fit: contain; }
/* The panel is the room's width and never scrolls sideways (only its tab strip does, on purpose). */
.emo-panel { flex: none; height: min(42vh, 320px); height: min(42dvh, 320px); display: flex; flex-direction: column; background: var(--surface); border-top: 1px solid var(--line); padding: 0 var(--col-r) var(--safe-b) var(--col-l); }
.emo-tabs { flex: none; display: flex; gap: 2px; overflow-x: auto; overscroll-behavior-x: contain; padding: 4px 6px; border-bottom: 1px solid var(--line); }
.emo-tab { flex: none; width: 44px; height: 40px; border: 0; border-radius: var(--r-md); background: none; cursor: pointer; font-size: 20px; display: flex; align-items: center; justify-content: center; color: var(--muted); }
.emo-tab.on { background: var(--soft); color: var(--fg); }
.emo-tab img { width: 30px; height: 30px; object-fit: contain; }
.emo-body { flex: 1; min-height: 0; overflow-x: hidden; overflow-y: auto; overscroll-behavior-y: contain; padding: 4px 8px 10px; }
.emo-cat { font-size: var(--fs-xs); margin: 8px 2px 4px; }
.emo-grid { display: grid; gap: 4px; }
/* As many sticker cells in a row as fit; a cell is at most --emo-cell-max. A column may get as
   narrow as 3/4 of it before the row takes one more, so the cells fill the row and, four or more to
   a row, stay under the largest on their own; the cell's max-width holds it there in a shorter row.
   (Phones of 352-436 px keep four to a row, as before; wider screens get more, not bigger.) */
.emo-grid.stickers { grid-template-columns: repeat(auto-fill, minmax(calc(var(--emo-cell-max) * 3 / 4), 1fr)); justify-items: center; }
.emo-grid.stickers > * { width: 100%; min-width: 0; max-width: var(--emo-cell-max); }
.emo-grid.stickers.small { grid-template-columns: repeat(6, minmax(0, 1fr)); }
/* the same emoji cell as the reaction picker's (.react-all-grid: 42px) */
.emo-grid.emoji { grid-template-columns: repeat(auto-fill, minmax(42px, 1fr)); }
.emo-sticker { aspect-ratio: 1; padding: 4px; border: 0; border-radius: var(--r-lg); background: none; cursor: pointer; }
.emo-sticker:active, .emo-emoji:active { background: var(--soft); }
.emo-sticker img { width: 100%; height: 100%; object-fit: contain; }
.emo-emoji { min-width: 0; height: 42px; padding: 0; border: 0; border-radius: 8px; background: none; cursor: pointer; font-size: 26px; line-height: 1; }
.emo-empty { text-align: center; padding: 24px 0; }
.emo-manage { padding: 16px 8px; text-align: center; }
.sticker-msg { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.row.mine .sticker-msg { align-items: flex-end; }
.sticker-img { width: 120px; height: 120px; object-fit: contain; display: block; }
.jumbo { font-size: 44px; line-height: 1.15; padding: 2px 2px; }
.row .jumbo { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
.pack { padding: 12px 0; border-top: 1px solid var(--line); }
.pack-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.pack-head strong { flex: 1; min-width: 0; }
.pack-item { position: relative; aspect-ratio: 1; }
.pack-item img { width: 100%; height: 100%; object-fit: contain; }
.pack-x { position: absolute; top: -4px; right: -4px; width: 22px; height: 22px; border: 0; border-radius: 50%; background: var(--pill-strong); color: var(--on-photo); cursor: pointer; line-height: 22px; padding: 0; }
.pack-x::after { content: ''; position: absolute; inset: -11px; border-radius: 50%; }
.drop { margin-top: 8px; padding: 14px; border: 2px dashed var(--line); border-radius: var(--r-lg); text-align: center; color: var(--muted); font-size: var(--fs-sm); cursor: pointer; }
.drop.over, .drop:focus { border-color: var(--accent); color: var(--accent); outline: none; }
.grow { flex: 1; min-width: 0; }
.lib-open { margin-top: 8px; }
.my-packs { list-style: none; margin: 0; padding: 0; }
.my-packs li { display: flex; align-items: center; gap: 6px; }
.my-packs .check { margin: 4px 0; }
.lib-groups { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; }
.lib-group { position: relative; display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 8px 4px; border: 2px solid var(--line); border-radius: var(--r-lg); background: var(--control); cursor: pointer; }
.lib-group.on { border-color: var(--accent); }
.lib-group img { width: 72px; height: 72px; object-fit: contain; }
.lib-item, .edit-item { position: relative; aspect-ratio: 1; padding: 4px; border: 2px solid transparent; border-radius: var(--r-lg); background: none; cursor: pointer; }
.lib-item img, .edit-item img { width: 100%; height: 100%; object-fit: contain; }
.lib-item.on, .edit-item.on { border-color: var(--accent); background: var(--accent-tint); }
.lib-item.done::after { content: '\2713'; position: absolute; left: 6px; top: 2px; font-size: var(--fs-sm); font-weight: 700; color: var(--accent); }
.pick-no { position: absolute; top: 2px; right: 2px; min-width: 22px; height: 22px; padding: 0 5px; border-radius: var(--r-pill); background: var(--accent); color: var(--on-accent); font-size: var(--fs-xs); line-height: 22px; text-align: center; }
.label-under { position: absolute; left: 2px; right: 2px; bottom: 0; font-size: var(--fs-2xs); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.edit-bar { position: sticky; top: var(--sheet-head-h, 54px); z-index: var(--z-raise); display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 8px 0; margin-bottom: 8px; background: var(--sheet); border-bottom: 1px solid var(--line); }
.sel-bar { position: sticky; bottom: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px; padding: 10px 0; background: var(--sheet); border-top: 1px solid var(--line); }
.sel-bar .field { flex: 1; min-width: 140px; }

/* ------------------------------------------------------------------ album */
.grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2px; }
@media (min-width: 700px) { .grid { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
.cell { position: relative; aspect-ratio: 1; padding: 0; border: 0; background: var(--soft); cursor: pointer; overflow: hidden; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
.cell-check { display: none; position: absolute; top: 6px; right: 6px; width: 22px; height: 22px; border: 2px solid var(--on-photo); border-radius: 50%; background: var(--scrim); box-shadow: 0 0 2px var(--photo-pill); }
.grid.selecting .cell-check { display: block; }
.cell.on .cell-check { background: var(--accent); }
.cell.on .cell-check::after { content: '\2713'; position: absolute; inset: 0; color: var(--on-accent); font-size: var(--fs-sm); font-weight: 700; line-height: 18px; text-align: center; }
.cell.on img, .cell.on .cell-file { opacity: .7; transform: scale(.92); transition: transform .12s; }
.top.selecting { background: var(--accent-tint); }
.album-tools { display: flex; align-items: center; justify-content: space-between; padding: 0 8px 6px 14px; }
/* Sideways strips (this, .who-chips, .photo-strip, .emo-tabs) keep their swipe to themselves:
   contain, so the end of the strip does not hand the swipe on to the page or the browser's back. */
.memories { display: flex; gap: 8px; overflow-x: auto; overscroll-behavior-x: contain; padding: 0 14px 10px; scrollbar-width: none; }
.memory { position: relative; flex: none; width: 150px; height: 110px; padding: 0; border: 0; border-radius: var(--r-xl); overflow: hidden; background: var(--soft); cursor: pointer; }
.memory img, .memory-blank { display: block; width: 100%; height: 100%; object-fit: cover; }
.memory-text { position: absolute; left: 0; right: 0; bottom: 0; display: flex; flex-direction: column; padding: 18px 10px 8px; color: var(--on-photo); text-align: left; font-size: var(--fs-xs); background: linear-gradient(transparent, var(--pill-strong)); }
.memory-text strong { font-size: var(--fs-md); }
.trash-grid { margin-top: 8px; }
.trash-cell { cursor: default; }
.trash-restore { position: absolute; left: 6px; right: 6px; bottom: 6px; min-height: 30px; padding: 0 6px; font-size: var(--fs-sm); background: var(--surface-glass); }
.btn.trash-restore::after { inset: -7px -6px; }
.cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cell-file { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; padding: 6px; font-size: var(--fs-xs); word-break: break-all; }
.cell-file.video-blank { background: var(--ph-dark); }
.grid-end { text-align: center; padding: 18px 0; font-size: var(--fs-sm); }
/* An empty album's two ways in: they wrap at 320 px, and putting photos up is the main one. */
.album-empty-btns { flex-wrap: wrap; justify-content: center; margin-top: 10px; padding: 0 14px; }
.album-empty-btns .btn { flex: 0 1 auto; }
.album-empty-btns .btn:first-child { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
.grid-day {
  grid-column: 1 / -1; position: sticky; top: var(--day-top, 56px); z-index: var(--z-float);
  padding: 10px 12px 6px; border: 0; background: var(--surface-glass); font-size: var(--fs-md); font-weight: 600; text-align: left; cursor: default;
}
.grid.selecting .grid-day { cursor: pointer; color: var(--accent); }
.month-chip { border: 0; cursor: pointer; font-size: var(--fs-sm); padding: 4px 10px; }
.album-year { margin: 14px 0 6px; font-size: var(--fs-md); }
.album-month-row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
.album-month { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 8px 4px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--control); cursor: pointer; }
.album-month.on { border-color: var(--accent); background: var(--accent-tint); }
.viewer-when { font-weight: 600; }
.album-switch { margin: 0 14px 8px; }
.album-switch button { min-height: 36px; font-size: var(--fs-md); }
.set-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 10px; padding: 0 14px; }
@media (min-width: 700px) { .set-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.set-card { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 0; border: 0; background: none; text-align: left; cursor: pointer; }
.set-cover { display: block; width: 100%; aspect-ratio: 1; margin-bottom: 4px; border-radius: var(--r-xl); object-fit: cover; background: var(--soft); }
.set-cover.blank, .set-thumb.blank { display: flex; align-items: center; justify-content: center; color: var(--muted); }
.set-pick { display: flex; flex-direction: column; margin-top: 8px; }
.set-row { display: flex; align-items: center; gap: 12px; min-height: 56px; padding: 6px 2px; border: 0; border-top: 1px solid var(--line); background: none; text-align: left; cursor: pointer; }
.set-row:disabled { opacity: var(--disabled); }
.set-thumb { flex: none; width: 44px; height: 44px; border-radius: var(--r-md); object-fit: cover; background: var(--soft); }
.upload-bar { display: flex; align-items: center; gap: 4px; margin: 0 14px 8px; padding: 0 0 0 4px; border-radius: var(--r-lg); background: var(--soft); }
.upload-bar-main { position: relative; flex: 1; min-width: 0; min-height: 40px; padding: 0 10px; border: 0; background: none; text-align: left; cursor: pointer; overflow: hidden; font-size: var(--fs-md); }
.upload-bar-main span { position: relative; z-index: var(--z-raise); }
.upload-meter { position: absolute; left: 0; bottom: 0; height: 3px; background: var(--accent); transition: width var(--dur); }
.upload-list { list-style: none; margin: 8px 0 0; padding: 0; }
.upload-item { display: flex; align-items: center; gap: 8px; padding: 7px 0; border-top: 1px solid var(--line); font-size: var(--fs-md); }
.upload-item.fail .small { color: var(--expense); }
.upload-item.done .small, .upload-item.known .small { color: var(--muted); }
.upload-item .upload-set { flex: 0 1 auto; max-width: 35%; }
.viewer { position: fixed; inset: 0; z-index: var(--z-viewer); background: var(--stage); display: flex; flex-direction: column; color: var(--on-photo); }
/* On a narrow phone the buttons go to a second line (right-aligned) rather than past the screen's edge. */
.viewer-top { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: 8px; padding: calc(8px + var(--safe-t)) calc(10px + var(--safe-r)) 8px calc(10px + var(--safe-l)); }
.viewer-info { flex: 1 1 80px; min-width: 0; font-size: var(--fs-sm); opacity: .85; }
.viewer-top .btn { background: var(--glass-dark); border-color: transparent; color: var(--on-photo); }
.viewer-stage {
  flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden;
  touch-action: none; -webkit-user-select: none; user-select: none;
}
.viewer-stage .viewer-img { width: 100%; height: 100%; object-fit: contain; transform-origin: center center; will-change: transform; -webkit-user-drag: none; }
.viewer-img.animate { transition: transform .18s ease-out; }
/* a video plays in the stage; its touches are the player's own (the stage's touch-action none is lifted) */
.viewer-stage.video { touch-action: auto; }
.viewer-stage .viewer-video { width: 100%; height: 100%; object-fit: contain; background: var(--stage); }
.viewer .nav { position: absolute; top: 50%; transform: translateY(-50%); z-index: var(--z-raise); width: 44px; height: 44px; border: 0; border-radius: 50%; background: var(--glass-dark); color: var(--on-photo); cursor: pointer; }
.viewer .prev { left: calc(8px + var(--safe-l)); }
.viewer .next { right: calc(8px + var(--safe-r)); }
.viewer .nav:disabled { opacity: var(--disabled); cursor: default; }
.viewer-none { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 24px; text-align: center; }
.viewer-none .btn { background: var(--glass-dark); border-color: transparent; color: var(--on-photo); }
/* A sheet opened from the viewer (comments, putting in an album) comes over it. */
.viewer ~ .sheet-back { z-index: var(--z-viewer-sheet); }
.viewer-bottom { padding: 6px calc(10px + var(--safe-r)) calc(8px + var(--safe-b)) calc(10px + var(--safe-l)); }
.viewer-acts { display: flex; align-items: center; gap: 6px; }
.viewer-act { display: inline-flex; align-items: center; gap: 5px; min-height: 40px; padding: 0 12px; border: 0; border-radius: var(--r-3xl); background: var(--glass-dark); color: var(--on-photo); font-size: var(--fs-base); cursor: pointer; }
.viewer-act:disabled { opacity: var(--disabled); cursor: default; }
.viewer-heart { font-size: var(--fs-xl); line-height: 1; }
.viewer-heart .icon { display: block; width: 20px; height: 20px; }
.viewer-act.on .viewer-heart { color: var(--heart); }
.viewer-act.on .viewer-heart .icon { fill: currentColor; }   /* mine: filled (the button says so too: aria-pressed) */
.viewer-last { display: block; width: 100%; margin-top: 6px; padding: 4px 2px; border: 0; background: none; color: var(--on-photo-2); text-align: left; font-size: var(--fs-md); cursor: pointer; }
.cell-meta { position: absolute; left: 4px; bottom: 4px; display: flex; gap: 6px; padding: 1px 6px; border-radius: var(--r-pill); background: var(--photo-pill); color: var(--on-photo); font-size: var(--fs-2xs); line-height: 16px; pointer-events: none; }
.comments { display: flex; flex-direction: column; gap: 12px; margin: 6px 0 10px; }
.comment { display: flex; align-items: flex-start; gap: 10px; }
.comment-main { flex: 1; min-width: 0; }
.comment-head { display: flex; align-items: baseline; gap: 8px; }
.comment-body { white-space: pre-wrap; word-break: break-word; }
.comment-acts { display: flex; gap: 10px; }
.comment-acts .btn.link { min-height: 28px; padding: 0; }
.comment-compose { position: sticky; bottom: calc(-18px - var(--safe-b)); display: flex; flex-direction: column; gap: 6px; padding: 8px 0 4px; background: var(--sheet); border-top: 1px solid var(--line); }
.comment-box { min-height: 46px; margin: 0; resize: vertical; }
.comment-send { display: flex; justify-content: flex-end; gap: 6px; }

/* ------------------------------------------------------------------ diary */
.entries { padding: 0 14px; }
.entry { padding: 14px 0; border-bottom: 1px solid var(--line); }
.entry-head { display: flex; align-items: center; gap: 10px; }
.entry-who { flex: 1; display: flex; flex-direction: column; font-size: var(--fs-sm); }
.entry h3 { margin: 10px 0 4px; font-size: var(--fs-lg); word-break: keep-all; overflow-wrap: anywhere; }
.entry-body { white-space: pre-wrap; margin-top: 6px; cursor: pointer; }
.entry-body.clamp { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
.diary-tools { display: flex; align-items: center; gap: 8px; padding: 0 14px 8px 0; }
.diary-tools .seg.mini { flex: none; margin: 0 0 8px auto; }
.seg.mini button { flex: none; min-height: 32px; padding: 0 12px; font-size: var(--fs-sm); }
.entries.flat { padding: 0; }
.diary-export { padding: 18px 14px 8px; text-align: center; }
.past-card { margin: 0 14px 8px; padding: 6px 12px; border-radius: var(--r-xl); background: var(--paper); }
.past-row { width: 100%; display: flex; align-items: baseline; min-width: 0; padding: 6px 0; border: 0; background: none; text-align: left; font-size: var(--fs-md); cursor: pointer; }
.past-row strong { flex: none; }
.past-card .btn.link { min-height: 28px; padding: 0; }
.cal { padding: 0 10px 8px; }
.cal-row { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 2px; }
.cal-week span { padding: 4px 0; text-align: center; font-size: var(--fs-xs); color: var(--muted); }
.cal-row > :first-child { color: var(--danger); }
.cal-row > :last-child { color: var(--income); }
.cal-cell { position: relative; min-height: 52px; display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 6px 0 4px; border: 0; border-radius: var(--r-md); background: none; cursor: pointer; }
.cal-cell.blank { cursor: default; }
.cal-cell:not(.blank):active { background: var(--soft); }
.cal-cell.has { background: var(--soft); }
.cal-num { font-size: var(--fs-md); line-height: 1; }
.cal-cell.today .cal-num { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; margin-top: -5px; border-radius: 50%; background: var(--accent); color: var(--on-accent); font-weight: 700; }
.cal-dots { display: flex; gap: 3px; min-height: 6px; }
.cal-dots i { width: 6px; height: 6px; border-radius: 50%; }
.who-chips { flex: 1; min-width: 0; display: flex; gap: 6px; overflow-x: auto; overscroll-behavior-x: contain; padding: 0 0 8px 14px; scrollbar-width: none; }
.who-chips::-webkit-scrollbar { display: none; }
.who-chip { flex: none; min-height: 34px; padding: 0 12px; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--control); font-size: var(--fs-sm); cursor: pointer; }
.who-chip.on { background: var(--fg); border-color: var(--fg); color: var(--on-fg); }
.diary-day { margin: 16px 0 0; padding-bottom: 4px; border-bottom: 1px solid var(--line); font-size: var(--fs-sm); }
.diary-day + .entry { padding-top: 10px; }
.entry-more { margin-top: 4px; padding: 2px 0; border: 0; background: none; color: var(--muted); font-size: var(--fs-sm); cursor: pointer; }
.entries .empty p { margin: 0 0 12px; }
.draft-note { display: flex; align-items: center; gap: 4px; padding: 6px 10px; border-radius: var(--r-md); background: var(--warn-bg); font-size: var(--fs-sm); }
.draft-note .btn.link { min-height: 28px; padding: 0 4px; font-size: var(--fs-sm); }
.conflict { padding: 10px 12px; border-radius: var(--r-lg); background: var(--danger-bg); font-size: var(--fs-md); }
.conflict p { margin: 0 0 8px; }
/* reactions and comments under a shared entry, and the comment sheet */
.talk { display: flex; align-items: center; gap: 6px; margin-top: 8px; min-width: 0; }
.talk .reacts { margin-top: 0; min-width: 0; }
.talk .react-chip { background: var(--soft); }
.talk-btn { flex: none; display: inline-flex; align-items: center; gap: 4px; min-height: 32px; padding: 0 8px; border: 0; border-radius: var(--r-md); background: none; color: var(--muted); font-size: var(--fs-sm); cursor: pointer; }
.talk-btn .icon { width: 18px; height: 18px; }
.talk-btn:active { background: var(--soft); }
.talk-preview { display: flex; flex-direction: column; gap: 2px; width: 100%; margin-top: 6px; padding: 8px 10px; border: 0; border-radius: var(--r-md); background: var(--soft); text-align: left; font-size: var(--fs-sm); cursor: pointer; }
.talk-line { display: block; }
.diary-comments { display: flex; flex-direction: column; gap: 2px; }
.diary-comment { display: flex; gap: 10px; padding: 8px 0; border-top: 1px solid var(--line); }
.diary-comment:first-child { border-top: 0; }
.diary-comment-main { flex: 1; min-width: 0; }
.diary-comment-head { display: flex; align-items: baseline; gap: 6px; font-size: var(--fs-sm); }
.diary-comment-body { white-space: pre-wrap; word-break: break-word; overflow-wrap: anywhere; margin-top: 2px; }
.diary-comment-acts { display: flex; gap: 6px; margin-top: 4px; }
.diary-comment-acts .btn.link { min-height: 28px; padding: 0 4px; font-size: var(--fs-sm); }
.diary-comment-box { display: flex; align-items: flex-end; gap: 6px; margin-top: 8px; }
.diary-comment-box .diary-comment-input { flex: 1; margin: 0; }
.diary-comment-input { min-height: 44px; resize: vertical; }
/* moods and tags */
.mood { margin-left: 4px; font-size: var(--fs-base); }
.mood-pick { display: flex; align-items: center; gap: 4px; margin: 4px 0; }
.mood-pick > span { margin-right: 4px; }
.mood-btn { flex: 1; min-width: 0; max-width: 52px; height: 42px; padding: 0; border: 0; border-radius: var(--r-lg); background: var(--soft); font-size: 22px; cursor: pointer; filter: grayscale(.6); opacity: .7; }
.mood-btn.on { background: var(--hilite-soft); box-shadow: inset 0 0 0 2px var(--hilite-ring); filter: none; opacity: 1; }
.cal-dots i.cal-mood { width: auto; height: auto; border-radius: 0; font-size: var(--fs-2xs); line-height: 1; font-style: normal; }
.entry-body a.tag { color: var(--accent); text-decoration: none; font-weight: 600; }
.tag-hint { min-height: 1.2em; }
.tag-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 0; }
.tag-chips > div { width: 100%; }
/* my month: days, streak and moods */
.stats-card { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 14px 8px; padding: 8px 14px; border-radius: var(--r-xl); background: var(--soft); }
.stats-text { display: flex; flex-direction: column; gap: 2px; font-size: var(--fs-base); }
.mood-bars { flex: none; overflow: visible; }
.mood-bars .bar { fill: var(--accent); opacity: .8; }
.mood-bars .bar.zero { fill: var(--ph); }
.mood-bars .count { font-size: 10px; fill: var(--muted); }
.mood-bars .pic { font-size: var(--fs-md); }
/* photos on an entry, a day's own photos and money (그날), choosing photos */
.photo-strip { display: flex; gap: 4px; margin-top: 8px; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; }
.photo-strip::-webkit-scrollbar { display: none; }
.strip-cell { position: relative; flex: none; width: 76px; height: 76px; padding: 0; border: 0; border-radius: var(--r-md); overflow: hidden; background: var(--soft); cursor: pointer; }
div.strip-cell { cursor: default; }
.strip-cell img { display: block; width: 100%; height: 100%; object-fit: cover; }
.strip-cell.broken img { visibility: hidden; }
.strip-gone { display: flex; align-items: center; justify-content: center; height: 100%; padding: 4px; color: var(--muted); font-size: var(--fs-2xs); text-align: center; }
.strip-more { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: var(--photo-pill); color: var(--on-photo); font-size: var(--fs-lg); font-weight: 700; }
.strip-x { position: absolute; top: 3px; right: 3px; display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0; border: 0; border-radius: 50%; background: var(--pill-strong); color: var(--on-photo); font-size: var(--fs-base); line-height: 24px; cursor: pointer; }
.strip-x::after { content: ''; position: absolute; inset: -10px; border-radius: 50%; }
.edit-photos { display: flex; flex-wrap: wrap; gap: 4px; margin: 4px 0; }
.photo-btns { display: flex; align-items: center; gap: 6px; margin: 4px 0; }
.photo-btns .muted { margin-left: auto; }
.day-extra { margin: 4px 0 0; padding: 8px 0 10px; border-bottom: 1px solid var(--line); }
.sheet .day-extra { margin: 0 0 4px; padding-top: 0; }
.day-extra-head { margin-bottom: -2px; }
.day-money { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; width: 100%; margin-top: 8px; padding: 8px 10px; border: 0; border-radius: var(--r-md); background: var(--soft); text-align: left; font-size: var(--fs-md); cursor: pointer; }
.day-money-go { margin-left: auto; }
.pick-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); margin-top: 4px; }
.cal-cell.pics::after { content: ''; position: absolute; top: 5px; right: 7px; width: 5px; height: 5px; border-radius: 2px; background: var(--accent); opacity: .7; }

/* ------------------------------------------------------------------ diary: routines (#/diary/routines) */
.routines { padding: 0 14px 16px; }
.routine-progress { margin: 0 0 8px; }
.routine-bar { flex: 1; max-width: 160px; height: 8px; border-radius: var(--r-xs); background: var(--track); overflow: hidden; }
.routine-bar i { display: block; height: 100%; background: var(--accent); }
.routine-reminders { margin: 0 14px 8px; padding: 8px 12px; border-radius: var(--r-xl); background: var(--warn-bg); }
.routine-reminder { display: flex; align-items: baseline; gap: 6px; padding: 2px 0; font-size: var(--fs-md); }
.routine-reminder .muted { margin-left: auto; white-space: nowrap; }
.routine-group { margin: 12px 0 0; }
.routine-group-head { display: flex; align-items: center; gap: 8px; padding-bottom: 4px; border-bottom: 1px solid var(--line); font-size: var(--fs-md); }
.routine-row { display: flex; align-items: center; gap: 10px; min-height: 52px; padding: 4px 0; border-bottom: 1px solid var(--line); }
/* the circle is 30px; its hit area 44px (::after) */
.routine-check { position: relative; flex: none; display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; padding: 0; border: 2px solid var(--line-strong); border-radius: 50%; background: var(--control); color: var(--on-accent); font-size: var(--fs-lg); font-weight: 700; cursor: pointer; }
span.routine-check { cursor: default; }
.routine-check.on { border-color: var(--accent); background: var(--accent); }
.routine-check::after { content: ''; position: absolute; inset: -7px; border-radius: 50%; }
.routine-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.routine-name { overflow-wrap: anywhere; }
.routine-row.done .routine-name { color: var(--muted); text-decoration: line-through; }
.routine-others-head { margin: 22px 0 4px; font-size: var(--fs-base); }
.routine-person { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
.routine-manage { margin: 4px 0 10px; padding-bottom: 6px; }
.routine-manage.off { opacity: var(--off); }
.routine-manage-row { display: flex; align-items: center; gap: 2px; }
.routine-manage-row.off .ledger-label-row { opacity: var(--off); }
.small-icon { position: relative; width: 32px; height: 32px; font-size: var(--fs-base); }
/* ↑ and ↓ sit 2px apart: the hit area grows up and down, and sideways only into that gap */
.small-icon::after { content: ''; position: absolute; inset: -6px -1px; }
.small-icon:disabled { opacity: var(--disabled); cursor: default; }
.routine-every { display: flex; align-items: center; gap: 8px; margin: 6px 0; }
.routine-num { width: 84px; flex: none; }
/* 일–토 always on one line: seven equal columns, at 320 px too */
.routine-weekdays { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; margin: 6px 0; }
.routine-weekdays .who-chip { min-width: 0; padding: 0; }
.routine-date { flex: 1; min-width: 0; }
.routine-preview { margin: 6px 0; }
.routine-share { padding-left: 0; }
.routine-move { margin-top: 4px; }
.day-routines .day-routine.done { color: var(--muted); }

/* ------------------------------------------------------------------ ledger */
.summary { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin: 0 14px; padding: 14px; border-radius: var(--r-2xl); background: var(--soft); }
.summary div { display: flex; flex-direction: column; align-items: center; gap: 2px; min-width: 0; }
.summary strong { font-size: var(--fs-base); white-space: nowrap; font-variant-numeric: tabular-nums; }
/* three amounts side by side: a size smaller on the narrowest phones so 「-1,234,567원」 fits */
@media (max-width: 360px) { .summary strong, .ledger-stats-sum strong { font-size: var(--fs-sm); } }
.income { color: var(--income); }
.expense { color: var(--expense); }
.cats { padding: 12px 14px 4px; }
.cat { display: grid; grid-template-columns: 76px 1fr auto; align-items: center; gap: 8px; margin: 6px 0; font-size: var(--fs-sm); }
/* a row to tap (a category's trend): a whole finger tall, its spacing in its own height */
button.cat { width: 100%; min-height: var(--tap); margin: 0; padding: 2px 0; border: 0; background: none; text-align: left; cursor: pointer; }
.ledger-tabs, .diary-tabs { margin: 0 14px 6px; }
.ledger-tabs a, .diary-tabs a { flex: 1; display: flex; align-items: center; justify-content: center; min-height: 36px; border: 1px solid var(--line); border-radius: var(--r-md); text-decoration: none; }
.ledger-tabs a.on, .diary-tabs a.on { background: var(--fg); border-color: var(--fg); color: var(--on-fg); }
.ledger-month-pick, .routine-day-pick { min-width: 120px; min-height: 40px; border: 0; border-radius: var(--r-md); background: none; font-weight: 700; cursor: pointer; }
.ledger-month-pick:active, .routine-day-pick:active { background: var(--soft); }
.ledger-month-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
.ledger-month-cell { display: flex; flex-direction: column; align-items: center; gap: 2px; min-height: 56px; padding: 6px 2px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--control); cursor: pointer; }
.ledger-month-cell.on { border-color: var(--accent); background: var(--accent-tint); }
.ledger-stats .card { margin: 10px 14px; }
.ledger-stats-sum { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; margin-bottom: 8px; }
.ledger-stats-sum div { display: flex; flex-direction: column; align-items: center; min-width: 0; }
.ledger-stats-sum strong { font-size: var(--fs-md); white-space: nowrap; font-variant-numeric: tabular-nums; }
.ledger-chart { display: block; width: 100%; height: auto; }
.ledger-chart-grid { stroke: var(--line); stroke-width: 1; }
.ledger-chart-tick { fill: var(--muted); font-size: 10px; }
.ledger-chart-bar { fill: var(--muted); }
.ledger-chart-bar.income { fill: var(--income); }
.ledger-chart-bar.expense { fill: var(--expense); opacity: .8; }
.ledger-chart-hit { fill: transparent; cursor: pointer; }
.ledger-chart-hit:hover { fill: var(--press); }
.ledger-search-custom { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 6px; }
.ledger-search-filters { display: grid; grid-template-columns: 1fr 1fr; gap: 0 8px; }
.ledger-search-filters .field { margin: 3px 0; min-height: 40px; }
.ledger-search-sum { font-weight: 600; padding: 6px 2px; }
.ledger-search-sum:empty { display: none; }
/* div only: the mood chart's bars are SVG <rect class="bar">, and a CSS height would override each
   rect's own height attribute (every bar the same 8px). */
div.bar { height: 8px; border-radius: var(--r-xs); background: var(--soft); overflow: hidden; }
div.bar i { display: block; height: 100%; background: var(--expense); opacity: .7; }
.days { padding: 0 14px; }
.day-head { display: flex; justify-content: space-between; align-items: baseline; margin: 16px 0 4px; font-size: var(--fs-sm); }
.tx { width: 100%; display: flex; align-items: center; gap: 10px; padding: 10px 2px; border: 0; border-bottom: 1px solid var(--line); background: none; text-align: left; cursor: pointer; }
.tx-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.tx .avatar { width: 24px; height: 24px; font-size: var(--fs-2xs); }
.ledger-who { margin: 0 0 4px; }
.ledger-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0; }
.ledger-chip-btn { min-height: 34px; padding: 0 12px; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--control); cursor: pointer; font-size: var(--fs-md); }
.ledger-chip-btn.on { background: var(--fg); border-color: var(--fg); color: var(--on-fg); }
/* categories and payment means */
.ledger-label-list { display: flex; flex-direction: column; }
.ledger-label-row { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; width: 100%; padding: 10px 2px; border: 0; border-bottom: 1px solid var(--line); background: none; text-align: left; cursor: pointer; min-width: 0; }
.ledger-label-row.on { background: var(--soft); }
.ledger-label-edit { display: flex; gap: 8px; align-items: center; padding: 8px 0 12px; border-bottom: 1px solid var(--line); }
.ledger-label-edit .field { margin: 0; }
/* bringing a file in */
.imp-file { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.imp-warn { padding: 8px 10px; border-radius: var(--r-md); background: var(--warn-bg); color: var(--warn-ink); }
.imp-cols { display: flex; flex-direction: column; gap: 6px; }
.imp-col { padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r-lg); }
.imp-col.on { border-color: var(--accent); }
.imp-col .field { margin: 6px 0 0; min-height: 40px; }
.imp-kind { display: grid; grid-template-columns: 1fr 140px; align-items: center; gap: 8px; }
.imp-kind .field { margin: 3px 0; min-height: 40px; }
.imp-preview { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; font-size: var(--fs-sm); }
.imp-row { display: grid; grid-template-columns: 34px 92px 96px 1fr; gap: 6px; align-items: center; padding: 6px 8px; border-top: 1px solid var(--line); }
.imp-row:first-child { border-top: 0; }
.imp-row.bad { grid-template-columns: 34px auto 1fr; background: var(--danger-bg); }
@media (max-width: 380px) {
  .imp-row { grid-template-columns: 26px 78px 84px minmax(0, 1fr); gap: 4px; padding: 6px 4px; }
  .imp-row.bad { grid-template-columns: 26px auto minmax(0, 1fr); }
}
.imp-reason { color: var(--expense); white-space: nowrap; }
.imp-result:empty { display: none; }
.imp-result { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); }
.imp-sum { padding: 12px; border-radius: var(--r-lg); background: var(--soft); }
.imp-total { margin-right: 10px; }
.imp-bad { padding: 8px 10px; border-radius: var(--r-md); background: var(--danger-bg); color: var(--danger-ink); }
.imp-item { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.imp-item.undone { opacity: var(--off); }
/* transfers and refunds */
.ledger-moved { margin: 6px 14px 0; text-align: center; }
.ledger-stats .ledger-moved { margin: 0 0 6px; }
.ledger-kinds button { min-width: 0; padding: 0 4px; }
.ledger-kind-hint { margin: -2px 0 6px; }
.ledger-kind-hint:empty { display: none; }
/* whose money */
.ledger-owner { margin: 6px 0; }
/* months that start on another day, and recurring entries not put in yet */
.ledger-span { margin: -4px 14px 6px; text-align: center; }
.ledger-span:empty, .ledger-due-box:empty { display: none; }
.ledger-due-box { margin: 0 14px 8px; }
.ledger-due { width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 44px; padding: 8px 12px; border: 1px solid var(--warn-line); border-radius: var(--r-lg); background: var(--warn-bg); color: var(--warn-ink); text-align: left; cursor: pointer; }
.ledger-due-list { display: flex; flex-direction: column; margin-bottom: 8px; }
.ledger-due-row { display: flex; align-items: center; gap: 10px; padding: 10px 2px; border-bottom: 1px solid var(--line); cursor: pointer; }
.ledger-due-row .grow { display: flex; flex-direction: column; min-width: 0; }
.ledger-label-row.off { opacity: var(--off); }
/* budgets */
.ledger-budget { display: block; width: calc(100% - 28px); margin: 10px 14px 0; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r-2xl); background: var(--control); text-align: left; cursor: pointer; }
.ledger-budget-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.ledger-budget-head strong { font-size: var(--fs-xl); }
.ledger-budget .bar { margin-top: 6px; }
.ledger-budget .cat { margin: 6px 0 0; }
.ledger-budget-sep { height: 1px; margin: 10px 0 4px; background: var(--line); }
.bar.budget i { background: var(--accent); opacity: .8; }
.bar.budget.over i { background: var(--expense); opacity: 1; }
.cat.over .cat-total { color: var(--expense); }
.ledger-budget-none { margin: 6px 14px 0; text-align: right; }
.ledger-budget-fields { display: flex; flex-direction: column; gap: 4px; }
.ledger-budget-field { display: grid; grid-template-columns: 1fr 140px; align-items: center; gap: 8px; }
.ledger-budget-field .field { margin: 2px 0; min-height: 40px; }
/* receipt photos */
.ledger-receipt { display: flex; align-items: center; gap: 8px; margin: 6px 0; }
.ledger-receipt.busy { opacity: var(--disabled); pointer-events: none; }
.ledger-receipt-thumb { width: 56px; height: 56px; padding: 0; border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; background: var(--soft); cursor: pointer; }
.ledger-receipt-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* many rows at once (the search) */
.ledger-pick-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 4px 0; }
.ledger-pick-bar:empty { display: none; }
.results.picking .result { border-left: 4px solid var(--line); }
.results.picking .result.picked { border-left-color: var(--accent); background: var(--accent-tint); }

/* ------------------------------------------------------------------ more */
.people { list-style: none; margin: 0; padding: 0; }
.person { width: 100%; display: flex; align-items: center; gap: 12px; padding: 6px 0; border: 0; background: none; cursor: pointer; text-align: left; }
.pending { padding: 12px 0; border-top: 1px solid var(--line); }
.pending:first-of-type { border-top: 0; }
.pending-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.devices { list-style: none; margin: 0; padding: 0; }
.devices li { padding: 8px 0; border-top: 1px solid var(--line); }
.devices li:first-child { border-top: 0; }
/* 내 기기: one row per device (deviceRow) -- nickname, the name it came with, buttons on the right */
.device-row { display: flex; align-items: center; gap: 10px; }
.device-main { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.device-btns { flex: none; display: flex; gap: 6px; }
.chip.device-badge { margin-left: 6px; background: var(--accent-tint); color: var(--accent-ink); vertical-align: 1px; }
.chip.device-badge.temp { background: var(--warn-bg); color: var(--warn-ink); }
/* 알림 보내 보기: a line a device, what came of it in words, the push service's answer small beside it */
.push-result { display: block; color: var(--fg); }
.push-raw { color: var(--muted); font-size: var(--fs-xs); }
/* someone's profile (openProfile), and the faces and names in a room that open it */
.avatar.huge { width: 112px; height: 112px; font-size: 44px; }
.profile-sheet { display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; }
.profile-face { padding: 10px 0 4px; }
.profile-name { font-size: 19px; font-weight: 700; overflow-wrap: anywhere; }
.profile-status { min-height: 1.4em; overflow-wrap: anywhere; }
.profile-sheet .row-btns { align-self: stretch; margin-top: 12px; }
.to-profile { cursor: pointer; }

/* ------------------------------------------------------------------ install (앱으로 설치) */
.install-strip { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border-radius: var(--r-xl); background: var(--soft); font-size: var(--fs-sm); }
.install-strip p { margin: 0; }
.install-strip .row-btns { flex-wrap: wrap; }
.install-strip .btn.link { align-self: flex-end; min-height: 28px; padding: 0 4px; font-size: var(--fs-xs); }
.field.install-field { font-size: var(--fs-md); }
.install-steps { margin: 0 0 8px; padding-left: 20px; }
.install-steps li { margin: 4px 0; line-height: var(--lh-read); word-break: keep-all; overflow-wrap: break-word; }
.icon.inline { width: 18px; height: 18px; vertical-align: -3px; }

/* ------------------------------------------------------------------ sheet */
.sheet-back { position: fixed; inset: 0; z-index: var(--z-sheet); background: var(--scrim); display: flex; align-items: flex-end; justify-content: center; }
/* A sheet scrolls on its own and stops at its edges (contain); the page under it does not move. */
/* No side insets here: a sheet is at most 560px wide and centred, and a phone turned sideways is wider
   than that by more than its notch -- the sheet never reaches it. */
.sheet {
  --sheet-px: 18px;
  width: 100%; max-width: 560px; max-height: 92vh; max-height: 92dvh; overflow-y: auto; overscroll-behavior-y: contain;
  background: var(--sheet); border-radius: var(--r-3xl) var(--r-3xl) 0 0; padding: 0 var(--sheet-px) calc(18px + var(--safe-b));
}
.sheet.settling { transition: transform var(--dur); }
@media (min-width: 700px) { .sheet-back { align-items: center; } .sheet { border-radius: var(--r-3xl); } }
/* The head (title, ✕) stays on top while a long sheet scrolls: sticky, on the page colour, across the
   sheet's whole width (out over its side padding). The sheet's top padding is the head's own, so
   nothing shows above it. Bars that stick inside a sheet sit under it (--sheet-head-h). */
.sheet-head {
  position: sticky; top: 0; z-index: var(--z-float); display: flex; align-items: center; justify-content: space-between; gap: 8px;
  min-height: 54px; margin: 0 calc(-1 * var(--sheet-px)); padding: 6px var(--sheet-px) 0; background: var(--sheet);
}
.sheet-head strong { flex: 1 1 auto; min-width: 0; word-break: keep-all; overflow-wrap: anywhere; }
/* The handle on top: drag it down to close (sheet(); not on a sheet that keeps its place, nor on a PC). */
.sheet-grip { position: absolute; top: 0; left: 50%; width: 88px; height: 22px; margin-left: -44px; touch-action: none; cursor: grab; }
.sheet-grip::before { content: ''; position: absolute; top: 7px; left: 26px; right: 26px; height: 4px; border-radius: var(--r-pill); background: var(--line-strong); }
.sheet.can-drag .sheet-head { touch-action: none; }   /* a finger on the head drags the sheet, not its scroll */
@media (min-width: 700px) { .sheet-grip { display: none; } }
.sheet-body { display: flex; flex-direction: column; gap: 4px; }

/* ------------------------------------------------------------------ admin page */
body.admin { background: var(--soft); }
#admin { max-width: 760px; margin: 0 auto; padding: 24px 16px 48px; }
#admin h1 { font-size: 22px; margin: 0 0 4px; }
#admin .card { background: var(--surface); margin: 14px 0; }
.cred { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 0; border-top: 1px solid var(--line); font-size: var(--fs-md); }
.cred .grow { flex: 1; min-width: 160px; }
.samples { white-space: pre-wrap; background: var(--soft); padding: 8px; border-radius: 8px; font-size: var(--fs-xs); overflow-x: auto; }
.import-out { margin-top: 10px; }

/* fix-security */
/* A temporary sign-in's last minutes (www/app.js tempBand, TEMP.warnMs): the band stands out. */
.temp-band.soon { border-color: var(--badge); }
.temp-band.soon .muted { color: var(--fg); font-weight: 600; }
/* The note in an editor on a temporary device (tempDraftNote). */
.temp-draft-note { margin: 0 0 8px; }
/* 「이 기기 알림이 꺼져 있어요 · 켜기」 once after a logout turned them off (pushOffNote). */
.fsec-band { display: flex; align-items: center; gap: 8px; margin: 8px 14px 0; padding: 8px 12px; }
.fsec-band-text { flex: 1; min-width: 0; }

/* fix-diary-ledger -- the diary, routines and ledger fixes (UX review). New selectors only, colours from the tokens. */
/* 「12건을 지웠어요 · 되돌리기」: its own element, because #toast lets taps through. It sits above where #toast shows. */
#undo-toast {
  position: fixed; left: 0; right: 0; bottom: calc(var(--tab-h) + var(--safe-b) + 72px + var(--fab-lift)); z-index: 51;
  display: flex; align-items: center; gap: 8px; width: max-content; max-width: 92vw; margin: 0 auto; padding: 2px 4px 2px 16px;
  border-radius: 22px; background: var(--fg); color: var(--bg); font-size: 14px;
  opacity: 0; transform: translateY(20px); pointer-events: none; transition: .2s;
}
#undo-toast.show { opacity: 1; transform: translateY(0); pointer-events: auto; }
#undo-toast .undo-btn { flex: none; min-height: 44px; padding: 0 12px; border: 0; border-radius: 18px; background: none; color: var(--bg); font-weight: 700; text-decoration: underline; cursor: pointer; }
/* Loading and failing, the same on every page and sheet of the three. */
.fl-loading { text-align: center; padding: 24px 0; }
.fl-failed { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 16px 0; }
.fl-failed .status { margin: 0; }
.fl-error { color: var(--expense); }
/* A routine of mine: the circle and its words are one button, the whole row tall. */
.fl-routine-hit { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; min-height: 48px; padding: 4px 0; border: 0; background: none; color: inherit; text-align: left; cursor: pointer; }
.fl-routine-hit .routine-check { cursor: pointer; }
.fl-routine-hit:active .routine-check { border-color: var(--accent); }
/* The other device's version in a conflict box. */
.fl-conflict-there { display: flex; flex-direction: column; gap: 2px; margin: 0 0 8px; padding: 8px 10px; border-radius: 10px; background: var(--bg); }
/* The ledger chart's key and its row of month buttons. */
.fl-legend { display: flex; justify-content: flex-end; gap: 14px; margin: 8px 0 2px; font-size: 12px; color: var(--muted); }
.fl-chart-months { margin-top: 8px; }
/* 「오늘이 아니에요 · 오늘로」 under the date of a new record. */
.fl-hint-row { display: flex; align-items: center; gap: 4px; margin: -4px 0 2px; font-size: 13px; color: var(--muted); }
/* The import's 「날짜 A열 · 금액 D열 [열 바꾸기]」. */
.fl-imp-summary { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--soft); }
/* A fixed expense to put in, with 「이번 달은 안 넣어요」 under it. */
.fl-due-item { border-bottom: 1px solid var(--line); }
.fl-due-item .ledger-due-row { border-bottom: 0; }
.fl-due-item .btn.link { min-height: 36px; margin: -6px 0 4px 30px; padding: 0 4px; font-size: 13px; }
.fl-empty-btns { justify-content: center; }
.fl-empty-btns .btn { flex: none; }

/* fix-chat-album: chat and album UX fixes. New selectors only, colours from the custom properties above. */
.room-title .topic-chip { flex: none; align-self: center; padding: 0 5px; border: 1px solid var(--accent); border-radius: 8px; color: var(--accent); font-size: 11px; font-weight: 600; line-height: 16px; }
.react-chip .react-mine-mark { color: var(--accent); font-size: 11px; font-weight: 700; }
/* The quick row: the server's quick ones and 「+」 in fixed columns, the recent ones wrapping under them. */
.react-pick.react-grid { display: grid; grid-template-columns: repeat(var(--react-cols, 7), minmax(0, 1fr)); gap: 4px; }
.bubble.sending .sending-stop { align-self: flex-end; }
.bubble:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The emoticon panel: the tabs scroll, ⚙ stays at the right end. */
.emo-tabs-row { flex: none; display: flex; align-items: stretch; border-bottom: 1px solid var(--line); }
.emo-tabs-row .emo-tabs { flex: 1 1 auto; min-width: 0; border-bottom: 0; }
.emo-tabs-row .emo-gear { flex: none; align-self: center; margin: 0 6px 0 2px; }
.emo-manage-head { margin: 16px 0 6px; color: var(--muted); font-size: 14px; }
.sheet-body > .emo-manage-head:first-child { margin-top: 0; }
/* Choosing an album's cover: one tap, no ticks. */
.grid.cover-pick .cell-check { display: none; }
.grid.cover-pick .grid-day { cursor: default; color: inherit; }
/* The viewer: where the photo is, the reaction chips, 지우기 greyed on someone else's photo. */
.viewer-info .viewer-pos { font-size: 12px; opacity: .85; }
.viewer-bottom .viewer-reacts { padding-bottom: 6px; }
.viewer-reacts .react-chip { background: var(--bg); color: var(--fg); }
.viewer-reacts .react-chip.mine { border-color: var(--accent); }
.viewer-top .btn.is-off { opacity: .45; }
/* The trash: 선택 · 모두 되살리기, and picking cells while choosing. */
.trash-bar { align-items: center; margin: 6px 0 2px; }
.trash-grid.selecting .trash-cell { cursor: pointer; }
.trash-grid.selecting .trash-restore { display: none; }
