/* tugtile — markdown kanban (prototype, read-only renderer) */

/* Squircle (continuous-curvature) corners — progressive enhancement.
   corner-shape is Chromium-only as of mid-2026; Safari/WebKit & Firefox ignore it and keep the
   border-radius arc, so this is ZERO visual regression. Requires a non-zero border-radius (still
   set per-surface below). Token lives here because corner-shape is NOT inherited but a custom
   property IS. Applied only to finite-radius card/button/panel/input surfaces — never pills/circles. */
:root { --corner: squircle; }
:where(
  .tugtile-brand, .tugtile__lane, .tugtile__tile, .tugtile__num, .tugtile__popup,
  .tugtile__popup-d, .tugtile__popup-t, .tugtile__add-btn, .tugtile__add-input, .tugtile__add-ok,
  .tugtile-prompt-field, .tugtile-iconbtn, .tugtile-ed-find-i, .tugtile__tile-edit,
  .tugtile__lane-rename, .tugtile-viewcycle, .tugtile__archivebar, .tugtile__lane-chevron,
  .tugtile-archive-row, .tugtile__addcol-btn
) { corner-shape: var(--corner); }

.tugtile {
  height: 100%;
  padding: 0;
  position: relative;   /* Contain the view-swap snapshot overlay */
}
/* Search/Undo/Redo live in the native back/forward container (repurposed by setupLeftActions), so they line up with the left-sidebar toggle above. */
.tugtile-leftacts { display: flex; align-items: center; gap: 2px; padding-left: 4px; }   /* Nudge right to line up the first icon with the left-sidebar toggle above (auto-measure couldn't find the toggle on iPad) */
.tugtile-leftacts > * { margin: 0 !important; }   /* Reset Obsidian's per-action margins so Search/Undo/Redo are evenly spaced and aligned */
/* Board⇄table swap: the outgoing snapshot slides up & out while the new view rises from below (two panels shuffling). */
.tugtile-viewsnap {
  position: absolute; inset: 0; z-index: 3;
  background: var(--background-primary); overflow: hidden; pointer-events: none;
  animation: tugtile-view-out 0.38s cubic-bezier(.7, 0, .3, 1) forwards;   /* ease-in-out: slow → fast → slow */
}
@keyframes tugtile-view-out { from { transform: translateY(0); } to { transform: translateY(-100%); } }
.tugtile--switching .tugtile__board,
.tugtile--switching .tugtile__table-wrap { animation: tugtile-view-in 0.38s cubic-bezier(.7, 0, .3, 1); }
@keyframes tugtile-view-in { from { transform: translateY(100%); } to { transform: translateY(0); } }
/* iOS26 border radius fix (merged from the old kanban-card-radius snippet): iOS26 stretches the core variable --input-radius
   into a pill shape, deforming any input box or button using it. We override it locally within tugtile's scope (kanban view
   and the two modals) instead of applying it globally to the body, keeping this OSS plugin clean. Remove once Obsidian/iOS is fixed. */
.tugtile,
.tugtile-edit-modal-full,
.tugtile-prompt-modal,
.tugtile-archive-modal { --input-radius: var(--radius-m) !important; }
/* Phone: the centered control (view-cycle · lock) sits in a top-bar in the content, not the cramped header */
/* Solid bg + z-index above the view-swap snapshot (z-index 3): the bar stays put on top while board/table
   slide beneath it, so the up-slide animation never leaks through the control row. */
.tugtile__ctlbar { flex: 0 0 auto; width: 100%; box-sizing: border-box; display: flex; justify-content: center; padding: 2px 8px 8px; position: relative; z-index: 4; background: var(--background-primary); }
/* Phone: kill the view-content top padding so the control bar sits flush against the header — otherwise that
   strip sits ABOVE the bar and the view-swap snapshot (which covers the whole content) leaks through it. */
body.is-phone .workspace-leaf-content[data-type="tugtile-board"] .view-content { padding: 0; }   /* All sides: the left/right padding inset the content so the full-width bar still left gaps on both edges */
/* Greyed-out (disabled-looking) header action — used for undo/redo when their stack is empty, instead of hiding the button */
.tugtile-act-off { opacity: 0.35; pointer-events: none; }
/* Dimmed brand suffix appended to the centered header title: "<file name> · tugtile-ing" */
/* Centered header controls: [view-cycle] · [lock]. Sized up (font-size) to roughly match the right-hand toolbar icons. */
.tugtile-headerctl {
  display: inline-flex; align-items: center; gap: 6px; vertical-align: middle;
  font-size: 18px;
}
.tugtile-sep { color: var(--text-faint); }   /* Neutral separator, NOT a button — equal button padding + the flex gap keep its two sides symmetric */
/* Brand suffix + lock icon = one button (t('brandSuffix') + padlock) that toggles the read-only lock */
.tugtile-brand {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--text-faint); font-weight: var(--font-normal);
  cursor: pointer; border-radius: var(--radius-s); padding: 2px 6px;
}
.tugtile-brand:hover { background: var(--background-modifier-hover); color: var(--text-muted); }

/* TileEditModal (tugtile's big editor) control strip — scoped to the modal so marktile's own strip is untouched.
   A uniform gray bar spanning the full width: ✕ pinned far left, ✓ far right, the mode·lock centered between. */
.tugtile-edit-modal-full .tugtile__ctlbar { background: var(--background-secondary); }
.tugtile-edit-modal-full .tugtile-headerctl { display: flex; width: 100%; }
.tugtile-edit-modal-full .tugtile-headerctl > .tugtile-iconbtn:first-child { margin-right: auto; }   /* ✕ → far left */
.tugtile-edit-modal-full .tugtile-headerctl > .tugtile-iconbtn:last-child { margin-left: auto; }      /* ✓ → far right */
.tugtile-lock-icon { display: inline-flex; }
.tugtile-lock-icon svg { width: 0.9em; height: 0.9em; }
/* Locked board (read-only): hide add/edit affordances, disable checkboxes, drop drag cursors. View-only actions (collapse, view switch, search) still work. */
.tugtile--locked .tugtile__add,
.tugtile--locked .tugtile__addcol,
.tugtile--locked .tugtile__tile-more,
.tugtile--locked .tugtile__lane-more { display: none; }
.tugtile--locked .tugtile__check,
.tugtile--locked .tugtile__row-title input { pointer-events: none; }
.tugtile--locked .tugtile__tile,
.tugtile--locked .tugtile__lane-head { cursor: default; }
.tugtile__empty {
  padding: 16px;
  color: var(--text-muted);
}
.tugtile__board {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  height: 100%;
  overflow-x: auto;
  padding: 12px;
}
.tugtile__lane {
  flex: 0 0 auto;
  width: var(--tugtile-lane-width, 300px);   /* Uniform fixed column width (configurable in settings); all lanes line up evenly */
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--background-secondary);
  border-radius: var(--radius-m);
  padding: 8px;
  overflow: hidden;         /* Cleanly clips content into the changing lane width during collapse/expand (dragging uses a clone on body and is unaffected) */
  --tg-d1: 0.26s;           /* Stage 1: lane narrows + horizontal title tucks into the chevron (collapse) / widens + title grows back (expand) */
  --tg-d2: 0.24s;           /* Stage 2: vertical title grows down out of the chevron (collapse) / retracts up into it (expand) */
  transition: width var(--tg-d1) cubic-bezier(.4, 0, .2, 1);   /* Smooth width change on collapse/expand */
}
.tugtile__lane-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 4px 0 8px;       /* Horizontal 0 → header rails line up with the card column (cards sit at the lane's own 8px padding on both sides) */
  font-weight: 700;
  cursor: grab;             /* Header acts as the drag handle for the lane */
}
.tugtile__lane-head:active { cursor: grabbing; }
.tugtile__lane--ghost { opacity: 0.4; }
.tugtile__lane--chosen { box-shadow: 0 0 0 2px var(--interactive-accent); }
.tugtile__lane--drag {   /* Slightly tilts and lifts the lane when dragging the entire column */
  rotate: 2deg;
  box-shadow: 0 16px 36px rgba(0, 0, 0, 0.5);
}
.tugtile__lane-count {
  flex: 0 0 auto;
  opacity: 0.55;
  font-size: 0.85em;
  transition: opacity var(--tg-d1) ease;
}
.tugtile__lane-count--over {   /* WIP: Warning indicator when exceeding limits */
  color: var(--text-error);
  opacity: 1;
  font-weight: 700;
}
.tugtile--hide-counts .tugtile__lane-count {
  display: none;
}
.tugtile__list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow-y: auto;
  /* Scrollbar gutter without changing the card layout: the list extends 8px to the right (eating the lane's right padding) via the negative margin, while padding-right keeps the cards in their original position. The iOS/iPadOS overlay scrollbar then sits in that right padding instead of on top of the cards. */
  margin-right: -8px;
  padding-right: 8px;
}
.tugtile__tile {
  position: relative;
  background: var(--background-primary);
  border: 1px solid var(--background-modifier-border);
  border-radius: var(--radius-m);
  padding: 8px;
  cursor: grab;
}
.tugtile__tile:active {
  cursor: grabbing;
}
.tugtile__tile:hover {
  border-color: var(--background-modifier-border-hover, var(--interactive-accent));
}
/* Dragging state (corresponding to the Sortable class in main.js) */
/* Placeholder left behind: faded out with a dashed border, similar to a Trello card slot */
.tugtile__tile--ghost {
  opacity: 0.4;
  background: var(--background-modifier-hover);
  border-style: dashed;
}
.tugtile__tile--ghost > * { visibility: hidden; }
.tugtile__tile--chosen {
  box-shadow: inset 0 0 0 2px var(--interactive-accent);   /* Inner ring — an outer ring's left/right 2px gets clipped by the list's overflow (cards are full-width with no horizontal room) */
}
/* The card following the cursor feels "picked up": slightly tilted, scaled up, with a floating shadow.
   Uses standalone rotate/scale properties (not transform) to avoid overwriting translations applied by Sortable. */
.tugtile__tile--drag {
  opacity: 0.97;
  rotate: 3deg;
  scale: 1.03;
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.5);
  cursor: grabbing;
}
/* Index number: a rounded inline chip prepended to the card text.
   Key detail: keeping font-size unscaled (matching body text size) aligns the number and adjacent text to the same baseline,
   achieving natural vertical alignment without magic numbers, font, line-height, or scale issues. The background chip sits snug
   around the number (slight horizontal, minimal vertical padding), centering itself on the first line and wrapping naturally. */
.tugtile__num {
  display: inline-block;
  margin-right: 0.4em;
  padding: 0 0.4em;
  background: var(--interactive-accent);
  color: var(--text-on-accent);
  border-radius: var(--radius-s);
  font-weight: 700;
  line-height: 1.25;
}
/* Floating control area in the top-right: checkbox + ⋯. Using float allows the first line of text to wrap around it, keeping content aligned to the top without wasting space. */
.tugtile__tile-ctrls {
  float: right;
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 2px 8px;
}
.tugtile__tile-body {
  padding-top: 0.15em;       /* Subtle top padding for breathing room (kept smaller than the control height so the first line still wraps around it) */
  overflow-wrap: anywhere;   /* Alphanumeric text/URLs do not overflow the lane */
  contain: layout paint;     /* The body's internal layout is self-contained, so animating the tile's height doesn't re-flow the rendered markdown every frame — kills the stutter on content-heavy tiles */
}
/* Long card collapse: hides the full text when folded, showing only the first line of the title (Approach C, without clipping) */
.tugtile__tile-title {
  display: none;
  padding-top: 0.15em;
  font-weight: 600;
}
.tugtile__tile-title > :first-child { margin-top: 0; }
.tugtile__tile-title > :last-child { margin-bottom: 0; }
.tugtile__tile--folded .tugtile__tile-body { display: none; }
.tugtile__tile--folded .tugtile__tile-title { display: block; }
/* On expand the tile height snaps (one reflow), then the body slides + fades in on the GPU — no per-frame
   layout, so it stays smooth no matter how much content the tile holds. */
.tugtile__tile--revealing .tugtile__tile-body { animation: tugtile-body-reveal 0.24s cubic-bezier(.4, 0, .2, 1) both; }
@keyframes tugtile-body-reveal {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}.tugtile__tile-body > :first-child {
  margin-top: 0;
}
.tugtile__tile-body > :last-child {
  margin-bottom: 0;
}
/* Date and time picker popup */
.tugtile__popup {
  position: fixed;
  z-index: 9999;
  background: var(--background-primary);
  border: 1px solid var(--background-modifier-border);
  border-radius: var(--radius-m);
  box-shadow: var(--shadow-l);
  padding: 8px;
  user-select: none;
}
.tugtile__popup-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-weight: 600;
  padding: 2px 4px 8px;
}
.tugtile__popup-nav {
  cursor: pointer;
  padding: 0 8px;
  display: inline-flex;
  align-items: center;
}
.tugtile__popup-nav svg { width: 1.15em; height: 1.15em; }
.tugtile__popup-nav:hover { color: var(--text-accent); }
.tugtile__popup-grid {
  display: grid;
  grid-template-columns: repeat(7, 2em);
  gap: 2px;
  text-align: center;
}
.tugtile__popup-w { opacity: 0.5; font-size: 0.8em; }
.tugtile__popup-d {
  cursor: pointer;
  border-radius: var(--radius-s);
  padding: 3px 0;
  line-height: 1.4;
}
.tugtile__popup-d:hover { background: var(--background-modifier-hover); }
.tugtile__popup-d--today { color: var(--text-accent); font-weight: 700; }
.tugtile__popup--time {
  max-height: 240px;
  overflow-y: auto;
  padding: 4px;
}
.tugtile__popup-t {
  cursor: pointer;
  padding: 4px 16px;
  border-radius: var(--radius-s);
}
.tugtile__popup-t:hover { background: var(--background-modifier-hover); }
/* Tag chips at the bottom of the card (when move-tags-to-card-footer is enabled) */
.tugtile__tags {
  margin-top: 6px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.tugtile__tag {
  font-size: 0.78em;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--background-modifier-hover);
  color: var(--text-accent);
  cursor: pointer;
}
.tugtile__tag:hover { filter: brightness(1.1); }
/* Date/time chips at the bottom of the card */
.tugtile__date {
  margin-top: 6px;
  font-size: 0.78em;
  color: var(--text-muted);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}
/* Date status colors: red for overdue, accent for today, yellow for upcoming (≤7 days), and green for safe (>7 days); colored whenever a date exists */
.tugtile__date--overdue { color: var(--color-red); font-weight: 600; }
.tugtile__date--today { color: var(--text-accent); font-weight: 600; }
.tugtile__date--soon { color: var(--color-yellow); font-weight: 600; }
.tugtile__date--safe { color: var(--color-green); }
.tugtile__date--unknown { color: var(--text-muted); }   /* Neutral color used when the date cannot be parsed */

/* Freeze container scrolling during dragging (adding .is-dragging in main.js onStart) to prevent native mobile scrolling from fighting the drag gesture */
.tugtile.is-dragging .tugtile__board,
.tugtile.is-dragging .tugtile__list {
  overflow: hidden !important;
  touch-action: none !important;
}
/* Add card button and input */
.tugtile__add {
  padding-top: 8px;          /* Match the 8px gap between cards so the add button keeps the same vertical rhythm */
}
.tugtile__add-btn {
  width: 100%;
  text-align: left;
  background: transparent;
  border: 1px dashed var(--background-modifier-border);
  border-radius: var(--radius-m);
  color: var(--text-muted);
  padding: 8px;              /* Match card padding and the add-lane button (consistent 8px rhythm); left-aligned "+" lines up with the card number chip */
  cursor: pointer;
}
.tugtile__add-btn:hover {
  background: var(--background-modifier-hover);
  color: var(--text-normal);
}
.tugtile__add-input {
  width: 100%;
  min-height: 4em;
  resize: vertical;
  border-radius: var(--radius-m);
}
.tugtile__add-ok {
  background: var(--interactive-accent);
  color: var(--text-on-accent);
  border: none;
  border-radius: var(--radius-s);
  padding: 4px 12px;
  cursor: pointer;
}
/* ⋯ More actions */
.tugtile__tile-more {
  color: var(--text-faint);
  line-height: 1;
  cursor: pointer;
  opacity: 0.3;
  display: inline-flex;
  align-items: center;
}
.tugtile__tile-more svg { width: 1em; height: 1em; }
.tugtile__tile-more:hover {
  opacity: 1;
  color: var(--text-normal);
}
/* Card checkbox */
.tugtile__check {
  margin: 0;
  flex: none;
  cursor: pointer;
}
/* "Undo" button for toast notifications (to restore deleted/archived items in one tap) */
.tugtile-toast-undo {
  margin-left: 10px;
  font-weight: 700;
  text-decoration: underline;
  cursor: pointer;
  color: var(--text-accent);
}
/* Submit button for mobile editing */
.tugtile__edit-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 6px;
}
/* Focused editing sheet (integrated with iOS 26 rounded sheet design; hides native ✕ to use our own left ❌ and right ✓) */
.tugtile-edit-modal-full {
  width: 100vw;
  max-width: 100vw;
  height: 100vh;
  height: 100dvh;           /* Dynamic viewport: shrinks with the on-screen keyboard so the editor stays inside the visible area and the textarea can scroll */
  max-height: 100vh;
  max-height: 100dvh;
  border-radius: 0;   /* Full-screen focus editor — nothing of the board shows behind */
  /* Inset content past the iPad status bar / notch / home indicator; env() = 0 on desktop. The solid background still bleeds into these strips. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
  overflow: hidden;
  transform-origin: center;
  animation: tugtile-pop-in 0.4s cubic-bezier(.34, 1.56, .64, 1);    /* Keep the bounce — full-screen ≠ unrefined */
}
/* Backdrop darkens to solid FAST (so the board vanishes quickly, not lingering behind the bounce); zero container padding */
.modal-container:has(.tugtile-edit-modal-full) { padding: 0; align-items: flex-start; }   /* Top-pinned, so shrinking the editor to the visual viewport (keyboard) keeps it above the keyboard */
/* The status-bar / home-indicator strips show --background-secondary (white in light, grey in dark) and sit
   outside the modal's reach. So instead of fighting to cover them, dye the WHOLE editor --background-secondary
   to match — seamless in both modes (the old --background-primary only matched in light mode). */
.modal-container:has(.tugtile-edit-modal-full) .modal-bg { background: var(--background-secondary); opacity: 1; animation: tugtile-bg-in 0.12s ease both; }
.tugtile-edit-modal-full,
.tugtile-edit-modal-full .tugtile-edit-modal { background: var(--background-secondary); }
/* macOS: push the editor down so its top bar (✕) isn't covered by the window traffic lights (class is .mod-macos) */
.mod-macos .tugtile-edit-modal-full { padding-top: 28px; }
@keyframes tugtile-bg-in { from { opacity: 0; } to { opacity: 1; } }
.tugtile-edit-modal-full.tugtile-ed-closing {
  animation: tugtile-pop-out 0.28s ease-in forwards;                 /* Exit transition: reverse scaling animation */
}
/* Opacity reaches 1 early (~0.1s) so the editor covers the board fast — the scale still bounces to settle. Fixes the "laggy / see-through during the animation" feel. */
@keyframes tugtile-pop-in {
  0%   { opacity: 0; transform: scale(0.92); }
  35%  { opacity: 1; }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes tugtile-pop-out {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(0.92); }
}
/* Small input modal (search / rename / add lane): sit near the top like Spotlight, so the virtual keyboard doesn't cover it. */
.modal-container:has(.tugtile-prompt-modal) { align-items: flex-start; }
.tugtile-prompt-modal {
  margin-top: 10vh;
  transform-origin: center;
  animation: tugtile-pop-in 0.4s cubic-bezier(.34, 1.56, .64, 1);
  /* Make the modal PANEL invisible — we stop fighting its box/padding/alignment and just float the search row
     (field + buttons, which carry their own backgrounds) over a frosted backdrop. */
  background: transparent !important;
  box-shadow: none !important;
  border: none !important;
}
/* Phone: sit below the header toolbar instead of colliding with it (this one was safe; the box/padding
   overrides that broke the layout — clobbering Obsidian's mobile safe-area — have been removed). */
body.is-phone .tugtile-prompt-modal { margin-top: calc(env(safe-area-inset-top) + 64px); }
.tugtile-prompt-modal.tugtile-ed-closing { animation: tugtile-pop-out 0.28s ease-in forwards; }
.tugtile-prompt-modal .modal-close-button { display: none; }   /* Hides Obsidian's native ✕ button to use our custom ❌ button instead */
/* Our own inner spacing — replaces Obsidian's 16px .modal-content padding. NEVER touch .modal's box: that
   carries the mobile safe-area insets (overriding it clipped the buttons off-screen). justify-content:center
   vertically centres the one-line row, so any leftover modal height is balanced top/bottom (no top-heavy gap). */
.tugtile-prompt-modal .modal-content { padding: 8px; justify-content: center; }
/* Dim the board behind the search modal (Obsidian forces the prompt backdrop transparent — opacity:0 !important —
   so without this the board shows at full brightness and crowds the field on a narrow phone). */
.modal-container:has(.tugtile-prompt-modal) .modal-bg {
  opacity: 1 !important;
  background-color: rgba(0, 0, 0, 0.4);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
}
.tugtile-prompt-row { display: flex; align-items: center; gap: 8px; }
/* The input is wrapped in a rounded "field" so a leading icon (search) can sit inside it */
.tugtile-prompt-field {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: 8px;
  box-sizing: border-box; height: 40px; padding: 0 12px;
  background: var(--background-modifier-form-field);
  border: 1px solid var(--background-modifier-border);
  border-radius: var(--radius-m);
  overflow: hidden;   /* Clip the input to the rounded box so it can never poke out of the corners */
}
.tugtile-prompt-field:focus-within { border-color: var(--interactive-accent); }
.tugtile-prompt-fieldicon { flex: 0 0 auto; display: inline-flex; align-items: center; color: var(--text-muted); }
.tugtile-prompt-fieldicon svg { width: 1.15em; height: 1.15em; }
/* The input is bare — the .tugtile-prompt-field IS the visible box. High-specificity selector + appearance:none
   + !important on every state to fully strip Obsidian's input chrome (the focus border/box-shadow was bigger
   than the field and broke the nesting). */
input.tugtile-prompt-input,
input.tugtile-prompt-input:hover,
input.tugtile-prompt-input:focus,
input.tugtile-prompt-input:focus-visible,
input.tugtile-prompt-input:active {
  flex: 1; min-width: 0; width: auto;
  -webkit-appearance: none; appearance: none;
  background: transparent !important;
  border: 0 !important; border-radius: 0 !important;
  outline: 0 !important; box-shadow: none !important;
  padding: 0 !important; margin: 0 !important;
  height: 100%; min-height: 0;
  font-size: 16px; color: var(--text-normal);   /* 16px prevents iOS auto-zoom on focus */
}
/* .tugtile-prompt-x / -ok inherit from .tugtile-iconbtn (the ✓ colour override lives up there) */
.tugtile-edit-modal-full .modal-close-button { display: none; }   /* Hides the native top-right ✕ button in favor of our custom ❌ button with safety checks */
.tugtile-edit-modal {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  padding: 0;
  background: var(--background-primary);   /* Opaque: in the marktile pane there's no modal-bg behind it, so without this the area the iOS keyboard reveals shows through as a black gap */
}
/* marktile lives in a leaf, not a modal — fill the pane opaquely and use the dynamic viewport so the editor
   shrinks WITH the on-screen keyboard instead of leaving a black void below it. */
.workspace-leaf-content[data-type="marktile-editor"] .view-content {
  padding: 0;
  background: var(--background-primary);
}
.workspace-leaf-content[data-type="marktile-editor"] .tugtile-ed-scroll { max-height: 100dvh; }
.tugtile-ed-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--background-modifier-border);
}
/* ── Single source of truth for EVERY icon button ──────────────────────────────────────────────
   editor ✕/✓, toolbar tools, find/replace, search ✕/✓. 40×40, flat, non-flex centering. Two hard
   rules baked in here so they can never drift again and re-break iPad:
     1) NO display:flex on the button — iPad WebKit blanks an inline svg that's a flex item.
     2) the svg is nested in a <span> (done in JS) — iPad WebKit won't render an svg that's a direct
        button child. So a size rule on the svg is safe.
   Per-button classes below only override what's genuinely unique (colour of ✓, hover colour of ✕). */
.tugtile-iconbtn {
  flex: 0 0 auto;
  -webkit-appearance: none; appearance: none;
  position: relative;
  width: 40px; height: 40px;
  border: none; border-radius: var(--radius-m);
  background: transparent; color: var(--text-muted);
  cursor: pointer;
}
/* The icon's svg is wrapped in a <span> (the iPad svg-in-button fix). Absolutely-centre that span — true
   pixel-centre, and NO flex on the button (flex on a <button> blanks the inline svg on iPad). */
.tugtile-iconbtn > span { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); display: block; }
.tugtile-iconbtn svg { width: 20px; height: 20px; display: block; }
.tugtile-iconbtn:hover { background: var(--background-modifier-hover); color: var(--text-normal); }
/* unique bits only */
.tugtile-ed-ok, .tugtile-ed-ok:hover { color: var(--color-green); }
.tugtile-ed-ok { font-weight: 700; }
.tugtile-ed-x:hover { color: var(--text-error); }
.tugtile-prompt-ok, .tugtile-prompt-ok:hover { color: var(--text-success, var(--interactive-accent)); }
.tugtile-ed-tools {
  flex: 1; min-width: 0;
  display: flex; flex-wrap: nowrap; gap: 4px; align-items: center;
  justify-content: safe center;   /* Centered in the bar (not crammed next to ✕); falls back to start-aligned + scrollable when it overflows */
  overflow-x: auto; scrollbar-width: thin;
}
/* .tugtile-ed-tool, .tugtile-ed-find-b, .tugtile-prompt-x/ok all inherit everything from .tugtile-iconbtn above */
/* Group divider in the editor toolbar */
.tugtile-ed-sep { flex: 0 0 auto; width: 1px; height: 20px; margin: 0 4px; background: var(--background-modifier-border); }
/* Phone: second toolbar row (format/insert tools), below the bar's essentials. Centered, scrolls if it overflows. */
.tugtile-ed-tools2 {
  flex: 0 0 auto;
  display: flex; flex-wrap: nowrap; gap: 2px; align-items: center;
  justify-content: safe center;
  overflow-x: auto; scrollbar-width: thin; -webkit-overflow-scrolling: touch;
  padding: 6px 10px;
  border-bottom: 1px solid var(--background-modifier-border);
}
body.is-phone .tugtile-ed-sep { display: none; }   /* no group separators in the compact phone rows */
/* Phone: pull the editor near the top so the toolbar hugs the Dynamic Island (4px breathing room below it) */
body.is-phone .tugtile-edit-modal-full { padding-top: 4px; }
body.is-phone .tugtile-ed-bar { padding-top: 6px; padding-bottom: 4px; }
/* Find / replace bar (below the toolbar) */
.tugtile-ed-find {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  padding: 6px 10px;
  border-bottom: 1px solid var(--background-modifier-border);
  background: var(--background-secondary);
}
.tugtile-ed-find-i {
  flex: 1; min-width: 80px;
  font-size: 16px; padding: 5px 8px; border-radius: var(--radius-s);   /* 16px avoids iOS focus-zoom */
}
.tugtile-ed-find-n { flex: 0 0 auto; color: var(--text-muted); font-size: 0.85em; min-width: 1.6em; text-align: center; }
/* .tugtile-ed-find-b inherits everything from .tugtile-iconbtn */
/* Settings: compact checkbox grid for choosing which editor toolbar buttons show */
.tugtile-tools-pick { display: flex; flex-wrap: wrap; gap: 6px 12px; justify-content: flex-end; }
.tugtile-tools-pick .tugtile-tool-chk { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; }
.tugtile-tools-pick .tugtile-tool-chk input { margin: 0; }
.tugtile-tools-pick .tugtile-tool-chk-i { display: inline-flex; align-items: center; min-width: 1.2em; justify-content: center; }
.tugtile-tools-pick .tugtile-tool-chk-i svg { width: 1.15em; height: 1.15em; }
.tugtile-ed-scroll { flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; }
/* Single contenteditable surface — the visible styled text IS the editable text (no overlay to keep aligned),
   which is what lets a heading line render bigger while the literal '## ' markers stay in place. */
.tugtile-ed-rich {
  min-height: 100%;
  margin: 0; border: 0; outline: none;
  padding: 14px 16px;
  font-family: var(--font-text); font-size: 16px; line-height: 1.7;
  white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word;
  color: var(--text-normal); caret-color: var(--text-normal);
  -webkit-user-modify: read-write-plaintext-only;   /* Keep paste/IME plaintext so no rich markup leaks into the markdown */
}
.tugtile-ed-rich:focus, .tugtile-ed-rich:focus-visible { outline: none; box-shadow: none; }
.tugtile-ed-rich .tg-line { min-height: 1.7em; }   /* Empty lines stay tappable and visible */
/* marktile IS a markdown editor, so its body font is monospace in BOTH modes (Seasoned keeps the
   syntax highlighting, just on a monospace base). Scoped to .marktile-ed so tugtile's card editor
   (a board surface, not a markdown editor) stays on the proportional --font-text. */
.marktile-ed .tugtile-ed-rich { font-family: var(--font-monospace); tab-size: 2; }   /* default tab-size is 8 — far too wide; a tab shows as 2 cells (one space · rule · one space) */
/* Distinguish a literal tab from spaces: a 1px accent rule drawn INSIDE each 2-cell tab (one space · rule ·
   one space) so it neither hugs the right edge nor touches the following text. A background gradient (not a
   border) lets the rule sit at an arbitrary x. marktile + Seasoned only — Plain's neutralizer strips backgrounds
   too. padding-block extends the rule's height (bg fills the padding box) to ~line-height so consecutive tab
   lines join into one continuous vertical guide. Tune background-position (rule x) / padding-block (continuity). */
.marktile-ed .tg-tab {
  background: linear-gradient(var(--text-accent), var(--text-accent)) no-repeat;
  background-size: 1px 100%;
  background-position: 1ch center;   /* one cell in; tab is 2 cells, so one cell of space after the rule */
  padding-block: 0.35em;
}
/* A leading tab on a heading line must NOT inflate the indent guide — its 1ch position / 100% height / 0.35em
   padding are all font-size-relative, so on a grown heading the thin rule balloons. Counter-scale the tab's own
   font-size by the heading's scale so its metrics fall back to the base text size (the guide stays thin). */
.marktile-ed .tg-h1 .tg-tab { font-size: calc(1em / 1.7); padding-block: 0.85em; }
.marktile-ed .tg-h2 .tg-tab { font-size: calc(1em / 1.45); padding-block: 0.68em; }
.marktile-ed .tg-h3 .tg-tab { font-size: calc(1em / 1.25); padding-block: 0.55em; }
.marktile-ed .tg-h4 .tg-tab { font-size: calc(1em / 1.12); padding-block: 0.45em; }
.marktile-ed .tg-h5 .tg-tab, .marktile-ed .tg-h6 .tg-tab { font-size: calc(1em / 1.05); padding-block: 0.4em; }

/* ── Table of contents (marktile only). A toggle side panel of H1–H3; click a heading to scroll to it.
   Absolute overlay anchored (top set in JS) to the editor content, so the toolbar + the TOC toggle button
   stay above/clickable. Desktop/iPad: a left sidebar that pushes the text right. Phone: a full-width overlay. */
.marktile-ed { position: relative; }
.marktile-ed .tugtile-ed-bar { position: relative; z-index: 6; }   /* keep the toggle (top-left) above the panel */
.marktile-toc {
  position: absolute; left: 0; bottom: 0; width: 240px; z-index: 5;
  background: var(--background-secondary); border-right: 1px solid var(--background-modifier-border);
  overflow-y: auto; padding: 0.6em 0;
  transform: translateX(-100%); transition: transform 0.18s ease;   /* slid out by default */
}
.marktile-toc-open .marktile-toc { transform: none; }
.marktile-ed .tugtile-ed-scroll { transition: padding-left 0.18s ease; }
.marktile-toc-open .tugtile-ed-scroll { padding-left: 240px; }      /* clear the sidebar (desktop/iPad) */
.marktile-toc-open .tugtile-ed-toc { color: var(--text-accent); }   /* toggle active state */
.marktile-toc-title { font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); padding: 0 1.1em 0.5em; }
.marktile-toc-list { display: flex; flex-direction: column; }
.marktile-toc-item { padding: 0 1.1em; line-height: 40px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }   /* ~40px tap target (single-line, vertically centred) — button-like, easier to hit */
.marktile-toc-item:hover { background: var(--background-modifier-hover); }
.marktile-toc-l1 { font-weight: 600; }
.marktile-toc-l2 { padding-left: 2.2em; }
.marktile-toc-l3 { padding-left: 3.3em; color: var(--text-muted); font-size: 0.95em; }
.marktile-toc-empty { padding: 0.4em 1.1em; color: var(--text-muted); font-style: italic; }
/* drag-reorder feedback (borrowed from tugtile's tile Sortable, scaled to a TOC row) */
.marktile-toc-item--ghost { opacity: 0.4; background: var(--background-modifier-hover); }   /* the gap left behind */
.marktile-toc-item--chosen { box-shadow: inset 0 0 0 2px var(--interactive-accent); }        /* the picked row */
.marktile-toc-item--drag {   /* the row following the finger (re-parented to <body> by fallbackOnBody) */
  opacity: 0.97; background: var(--background-secondary); border-radius: 6px;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.4); cursor: grabbing;
  rotate: 3deg; scale: 1.03;   /* "picked up" tilt — standalone props (NOT transform) so Sortable's drag translation isn't overwritten */
}
.is-phone .marktile-toc { width: 100%; border-right: none; }       /* phone: full-width overlay (sidebar too cramped) */
.is-phone .marktile-toc-open .tugtile-ed-scroll { padding-left: 0; }
/* marktile viewcycle → Plain mode: neutralize ALL editor styling so it's a bare plain-text markdown editor
   (uniform size/weight, no colours, markers shown as-is). Pure CSS — no rendering, no parser, fully portable. */
.tugtile-plain .tg-line,
.tugtile-plain .tg-line * {
  font-size: 1em !important; font-weight: normal !important; font-style: normal !important;
  text-decoration: none !important; color: var(--text-normal) !important; background: transparent !important;
  font-family: var(--font-monospace) !important; line-height: inherit !important;   /* monospace = raw-source feel */
  border: 0 !important; padding-left: 0 !important; margin: 0 !important;
}
/* marktile lock → read-only. contenteditable=false alone is OVERRIDDEN on WebKit by the editor's
   -webkit-user-modify: read-write-plaintext-only, so flip that to read-only here (the real switch). Also
   disable the toolbar so a stray tap can't edit (Obsidian has no save step — whatever changes IS saved). */
.tugtile--locked .tugtile-ed-rich { -webkit-user-modify: read-only; user-modify: read-only; }
.tugtile--locked .tugtile-ed-tools,
.tugtile--locked .tugtile-ed-tools2,
.tugtile--locked .tugtile-ed-find { pointer-events: none; opacity: 0.4; }   /* find/replace too — else Replace All bypasses contenteditable (B2) */
/* Phone: tall bottom padding gives scroll room so the last real line can always be scrolled up above the
   on-screen keyboard (see keepCaretVisible) instead of being trapped under it. */
body.is-phone .tugtile-ed-rich { padding-bottom: 55vh; }

/* Headings: markers kept (## still shows), the line just renders larger */
.tugtile-ed-rich .tg-h { font-weight: 700; color: var(--text-accent); }
.tugtile-ed-rich .tg-h1 { font-size: 1.7em; line-height: 1.35; }
.tugtile-ed-rich .tg-h2 { font-size: 1.45em; line-height: 1.4; }
.tugtile-ed-rich .tg-h3 { font-size: 1.25em; }
.tugtile-ed-rich .tg-h4 { font-size: 1.12em; }
.tugtile-ed-rich .tg-h5, .tugtile-ed-rich .tg-h6 { font-size: 1.05em; }
.tugtile-ed-rich .tg-quote { color: var(--text-muted); }
.tugtile-ed-rich .tg-b { font-weight: 700; }
.tugtile-ed-rich .tg-i { font-style: italic; }
.tugtile-ed-rich .tg-strike { text-decoration: line-through; color: var(--text-muted); }
.tugtile-ed-rich .tg-check { font-family: var(--font-monospace); color: var(--text-accent); }
.tugtile-ed-rich .tg-check-done { color: var(--color-green); }
.tugtile-ed-rich .tg-task-done { color: var(--text-muted); }   /* whole completed task line dims */
.tugtile-ed-rich .tg-code { font-family: var(--font-monospace); color: var(--text-accent); }
.tugtile-ed-rich .tg-tag,
.tugtile-ed-rich .tg-link { color: var(--text-accent); }
.tugtile-ed-rich .tg-date { color: var(--color-yellow); }
/* Seasoned "彩色" palette — opt-in via settings (seasonedColor → .marktile-palette-color on the editor mount).
   Each syntax token gets its own hue instead of the single accent tint. Obsidian theme color vars (hex fallbacks
   for the web host) so it tracks light/dark + custom themes. quote/strike/date/check keep their base styling. */
.marktile-palette-color .tugtile-ed-rich .tg-h { color: var(--color-orange, #d19a66); }
.marktile-palette-color .tugtile-ed-rich .tg-b { color: var(--color-yellow, #e5c07b); }
.marktile-palette-color .tugtile-ed-rich .tg-i { color: var(--color-purple, #c678dd); }
.marktile-palette-color .tugtile-ed-rich .tg-code { color: var(--color-green, #98c379); }
.marktile-palette-color .tugtile-ed-rich .tg-tag,
.marktile-palette-color .tugtile-ed-rich .tg-link { color: var(--color-cyan, #56b6c2); }
/* Textarea for card editing */
.tugtile__tile-edit {
  width: 100%;
  min-height: 4em;
  resize: vertical;
  border-radius: var(--radius-s);
}
/* Lane header container */
.tugtile__lane-title {
  flex: 1;
  cursor: text;
  white-space: nowrap;        /* Prevents the title from wrapping to expand the lane width, avoiding overlap with count/buttons */
  overflow: hidden;
  text-overflow: ellipsis;
  transform-origin: left center;   /* Tucks toward the chevron (left) on collapse */
  transition: transform var(--tg-d1) ease, opacity var(--tg-d1) ease;
}
.tugtile__lane-title-v { display: none; }   /* Vertical title clone — only rendered in the collapsed strip */
/* Collapse stage 1 / expand stage 2: lane is narrow, the horizontal title is tucked into the chevron, and the row trimmings fade */
.tugtile__lane--narrowing { width: 40px; }
.tugtile__lane--narrowing .tugtile__lane-title { transform: scaleX(0); opacity: 0; }
.tugtile__lane--narrowing .tugtile__lane-count,
.tugtile__lane--narrowing .tugtile__lane-more { opacity: 0; }
.tugtile__lane-rename {
  flex: 1;
  font-weight: 700;
  border-radius: var(--radius-s);
}
/* Responsive board (opt-in, default off): on a narrow pane the horizontal lanes reflow into a single full-width vertical stack — the behaviour of the retired List view, now automatic via a container query. Drag-and-drop/editing logic is shared with the board view. */
.tugtile--rwd { container-type: inline-size; container-name: tugtileboard; }
@container tugtileboard (max-width: 640px) {
  .tugtile__board { flex-direction: column; overflow-x: hidden; overflow-y: auto; align-items: stretch; }
  .tugtile__lane { width: auto; max-width: none; flex: 0 0 auto; }
  .tugtile__addcol { width: auto; }
  /* Collapsed lane in vertical layout: keep a standard horizontal header (the board view's narrow vertical strip would become a full-width black box here). The board view's two-stage strip animation no-ops here — the horizontal title stays, the vertical clone is unused. */
  .tugtile__lane--collapsed { min-width: 0; max-width: none; width: auto; }
  .tugtile__lane--collapsed .tugtile__lane-head { flex-direction: row; height: auto; padding: 8px; overflow: visible; }
  .tugtile__lane--collapsed .tugtile__lane-title { display: revert; writing-mode: horizontal-tb; max-height: none; transform: none; opacity: 1; }
  .tugtile__lane--collapsed .tugtile__lane-title-v { display: none; }
  .tugtile__lane--narrowing { width: auto; }
  .tugtile__lane--narrowing .tugtile__lane-title { transform: none; opacity: 1; }
}
/* Centered header view-cycle control: "<icon> <name>" — click cycles board → table → markdown. Same padding as the lock button so the "·" separator sits symmetrically between them. */
.tugtile-viewcycle {
  display: inline-flex; align-items: center; gap: 5px;
  cursor: pointer; border-radius: var(--radius-s);
  padding: 2px 6px;
}
.tugtile-viewcycle:hover { background: var(--background-modifier-hover); }
.tugtile-viewcycle-icon { display: inline-flex; }
.tugtile-viewcycle-icon svg { width: 1em; height: 1em; }
.tugtile-viewcycle-name { font-weight: var(--font-medium, 500); }
/* Table view */
.tugtile__table-wrap {
  height: 100%;
  overflow: auto;
  padding: 0 12px;   /* No top padding → the sticky header sits flush at the top, so rows can't peek above it while scrolling */
}
.tugtile__table {
  width: 100%;
  border-collapse: collapse;
}
.tugtile__table th,
.tugtile__table td {
  text-align: left;
  padding: var(--tugtile-row-pad, 8px) 10px;   /* Vertical padding is the table density preset (settings) */
  border-bottom: 1px solid var(--background-modifier-border);
  vertical-align: top;
}
.tugtile__table th {
  cursor: pointer;
  position: sticky;
  top: 0;
  z-index: 2;   /* Stay above scrolling rows */
  background: var(--background-primary);   /* Solid — rows scroll cleanly behind, no show-through */
  box-shadow: 0 1px 0 var(--background-modifier-border);
  user-select: none;
}
.tugtile__table th:hover { color: var(--text-accent); }
.tugtile__row:hover { background: var(--background-modifier-hover); }
.tugtile__row-title { cursor: text; }
/* Table rows: displays plain text where we manage styles (without markdown rendering), keeping font sizes uniform and truncating long text to avoid breaking the layout */
.tugtile__row-text { font-size: var(--font-text-size); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 60ch; display: inline-block; vertical-align: bottom; }
.tugtile__row-title input { margin-right: 6px; }
.tugtile__row-lane { color: var(--text-muted); white-space: nowrap; }
/* Tag cell stays a normal table cell (NOT display:flex — that breaks cell layout and collapses when empty). Chips flow inline-block instead. */
.tugtile__row-tags .tugtile__tag { display: inline-block; margin: 0 4px 2px 0; }
/* Drag-to-archive zone (only shown when dragging cards) */
.tugtile__archivebar {
  display: none;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 0 12px 8px;
  padding: 12px;
  border: 2px dashed var(--background-modifier-border);
  border-radius: var(--radius-m);
  background: var(--background-secondary);
  color: var(--text-muted);
  font-weight: 600;
}
/* Lane collapse chevron: a visible rounded square button. Its left edge lines up with the tile (card) left edge below — both sit at the lane's inner padding (8px), so no extra offset is needed. Square size also gives a comfortable tap target. */
.tugtile__lane-chevron {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 1.6em;
  height: 1.6em;
  border-radius: var(--radius-s);
  /* Opaque (translucent hover tint composited over the lane bg) so the vertical title retracting INTO the
     chevron during collapse/expand is fully masked — a see-through button would leak that text. */
  background: linear-gradient(var(--background-modifier-hover), var(--background-modifier-hover)), var(--background-secondary);
  cursor: pointer;
  color: var(--text-muted);
  user-select: none;
  position: relative;
  z-index: 3;   /* Highest in the lane header, so it sits over the retracting title */
}
.tugtile__lane-chevron:hover { background: linear-gradient(var(--background-modifier-border), var(--background-modifier-border)), var(--background-secondary); color: var(--text-normal); }
/* Only the inner glyph rotates (the button stays still). Rotating the same ▾ by -90° to ▸ rather than swapping glyphs keeps height perfectly consistent. */
.tugtile__lane-chevron-glyph {
  display: inline-flex;
  transition: transform 0.3s cubic-bezier(.34, 1.56, .64, 1);   /* Springy overshoot */
}
.tugtile__lane-chevron-glyph svg { width: 1.1em; height: 1.1em; }
.tugtile__lane--collapsed .tugtile__lane-chevron-glyph { transform: rotate(-90deg); }
/* In the collapsed vertical strip: drop the margin AND pin the toggle to the top-left corner (align-self:
   flex-start) instead of centering it — so it stays exactly where the expanded header's chevron was and
   doesn't drift sideways when a lane collapses/expands. */
.tugtile__lane--collapsed .tugtile__lane-chevron { margin: 0; align-self: flex-start; }
/* Collapsed lanes: a fixed narrow column with vertical title orientation (upright CJK characters, truncated if too long) */
.tugtile__lane--collapsed {
  width: 40px;              /* Base lane sets a plain width + width transition, so collapsing to 40px animates smoothly */
  max-height: 100%;
  cursor: pointer;
}
.tugtile__lane--collapsed .tugtile__list,
.tugtile__lane--collapsed .tugtile__add,
.tugtile__lane--collapsed .tugtile__lane-more {
  display: none;
}
.tugtile__lane--collapsed .tugtile__lane-head {
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;   /* Reset the base header's space-between, which in a column would spread chevron/title/count apart and push the chevron off the top */
  gap: 10px;
  padding: 4px 0 8px;            /* Top 4 matches the expanded header so the chevron doesn't jump down when a lane collapses */
  height: 100%;
  overflow: hidden;
}
.tugtile__lane--collapsed .tugtile__lane-title { display: none; }   /* Horizontal title hidden in the strip; the vertical clone takes over */
.tugtile__lane--collapsed .tugtile__lane-title-v {
  display: block;
  writing-mode: vertical-rl;
  text-orientation: mixed;       /* Keeps CJK characters upright while rotating alphanumeric text */
  white-space: nowrap;           /* Prevents wrapping into multiple vertical lines, which is the primary cause of text overlap */
  overflow: hidden;
  text-overflow: ellipsis;
  max-height: 100%;
  flex: 0 1 auto;
  cursor: pointer;
  transform: scaleY(0);          /* Grows down out of / shrinks up into the chevron */
  opacity: 0;
  transform-origin: top center;
  /* Grow and retract are the SAME motion in reverse: scaleY + opacity, one shared easing + duration, both
     directions — so "grow out" and "shrink in" read as one symmetric concept (a dissolve into the button). */
  transition: transform var(--tg-d2) ease-in-out, opacity var(--tg-d2) ease-in-out;
}
.tugtile__lane--collapsed.tugtile__lane--grown .tugtile__lane-title-v { transform: scaleY(1); opacity: 1; }
/* Retract = the exact reverse of grow (same easing + duration), driven by a keyframe so it reliably animates
   instead of snapping. scaleY + opacity dissolve up into the chevron in lockstep. */
.tugtile__lane--retracting .tugtile__lane-title-v { animation: tugtile-title-retract var(--tg-d2) ease-in-out both; }
@keyframes tugtile-title-retract {
  from { transform: scaleY(1); opacity: 1; }
  to   { transform: scaleY(0); opacity: 0; }
}
.tugtile__lane--collapsed .tugtile__lane-count {
  writing-mode: horizontal-tb;
  flex: 0 0 auto;
  margin-top: auto;   /* Sink the count to the bottom of the strip (chevron + title stay grouped at the top) */
}
/* Lane ⋯ menu */
.tugtile__lane-more {
  cursor: pointer;
  color: var(--text-faint);
  opacity: 0.4;
  display: inline-flex;
  align-items: center;
}
.tugtile__lane-more svg { width: 1em; height: 1em; }
.tugtile__lane-more:hover {
  opacity: 1;
  color: var(--text-normal);
}
/* Archive list modal */
.tugtile-archive-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 60vh;
  overflow-y: auto;
}
.tugtile-archive-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px;
  border: 1px solid var(--background-modifier-border);
  border-radius: var(--radius-m);
}
.tugtile-archive-body {
  flex: 1;
  min-width: 0;
}
.tugtile-archive-body > :first-child { margin-top: 0; }
.tugtile-archive-body > :last-child { margin-bottom: 0; }
.tugtile-archive-actions {
  flex: 0 0 auto;
  display: flex;
  gap: 6px;
}
/* Add lane container */
.tugtile__addcol {
  flex: 0 0 auto;
  width: var(--tugtile-lane-width, 300px);   /* Same fixed width as a lane, so the add-lane button lines up with the columns */
  /* No padding: the button's top lines up with the lane boxes (both at the board padding), and the board's 12px gap already spaces it from the last lane */
}
.tugtile__addcol-btn {
  width: 100%;
  text-align: left;          /* Match the add-card button (consistent left-aligned "+ label") */
  background: transparent;
  border: 1px dashed var(--background-modifier-border);
  border-radius: var(--radius-m);
  color: var(--text-muted);
  padding: 8px;
  cursor: pointer;
}
.tugtile__addcol-btn:hover {
  background: var(--background-modifier-hover);
  color: var(--text-normal);
}

/* ── In-grid markdown tables — the "locked markers" editor (decorateTables in editor-core.js). SHARED by
   marktile (inlined) and ejecta (this file is vendored as tile-core.css). Gate = .marktile-grid: hosts add it
   in Seasoned + Rendered, drop it in Plain. The pipe `|` is the table's `##` — in Seasoned it stays VISIBLE
   (dimmed, locked) so the markdown coexists with the layout; the .tugtile-preview overlay (Rendered) hides the
   pipes into a real bordered grid. textContent is byte-identical either way → round-trip exact. (Dormant in
   tugtile, which never sets .marktile-grid.) */
.marktile-grid .ej-trow { display: table-row; white-space: normal; }
.marktile-grid .ej-trow .ej-cell { display: table-cell; padding: 3px 12px; vertical-align: top; cursor: text; min-width: 36px; }
.marktile-grid .ej-trow .ej-pipe { display: table-cell; color: var(--text-faint, #9aa0a6); padding: 0 1px; }   /* the locked pipe, dimmed — the visible "seasoning" in Seasoned */
.marktile-grid .ej-tsep .ej-cell { color: var(--text-faint, #9aa0a6); }                                       /* the |---| separator row, dimmed (also shown in Seasoned) */
.marktile-grid .ej-trow .ej-cell[data-a=center] { text-align: center; }
.marktile-grid .ej-trow .ej-cell[data-a=right] { text-align: right; }
/* Rendered overlay: the markers give way to a real grid (pipes + separator gone, cells get borders). */
.tugtile-preview .ej-trow .ej-pipe { display: none; }
.tugtile-preview .ej-tsep { display: none; }
.tugtile-preview .ej-trow .ej-cell { border: 1px solid var(--background-modifier-border, #e4e6e9); padding: 6px 14px; }
.tugtile-preview .ej-trow.ej-thead .ej-cell { background: var(--background-secondary, #f6f7f9); font-weight: 700; }
/* Rendered hides EVERY marker, not just the table pipes — the `##`/`**`/`` ` `` markers (.tg-mk) give way too, so
   the whole view reads as output (the third point of raw → coexist → output). Still fully editable; markup never
   leaves the text. Dormant in tugtile (no .tugtile-preview). */
.tugtile-preview .tg-mk { display: none; }

/* table right-click menu (desktop: insert/delete row/column) */
.ej-tblmenu { position: fixed; z-index: 2000; min-width: 168px; background: var(--background-primary, #fff); border: 1px solid var(--background-modifier-border, #e4e6e9); border-radius: 10px; box-shadow: 0 10px 30px rgba(10, 22, 40, .18); padding: 5px; display: flex; flex-direction: column; }
.ej-tblmenu button { font: 13.5px var(--font-interface, system-ui, sans-serif); text-align: left; border: 0; background: transparent; border-radius: 7px; padding: 8px 12px; cursor: pointer; color: var(--text-normal, #1f2328); }
.ej-tblmenu button:hover:not(:disabled) { background: var(--background-modifier-hover, rgba(0, 0, 0, .06)); }
.ej-tblmenu button:disabled { opacity: .35; cursor: default; }
.ej-tblmenu hr { border: 0; border-top: 1px solid var(--background-modifier-border, #e4e6e9); margin: 4px 6px; }

/* Inline image thumbnails (decorateImages in editor-core.js) — the picture shows beside its still-editable source
   line; the source markdown stays but dims. Hidden in Plain so power users see raw source only. Shared by marktile
   + ejecta; dormant in tugtile (never calls decorateImages). The first step of Rendered "growing". */
.ej-inlimg { display: block; max-width: min(280px, 100%); max-height: 220px; border-radius: 8px; margin: 4px 0 8px; border: 1px solid var(--background-modifier-border, #e4e6e9); }
.tg-line.ej-hasimg { color: var(--text-faint, #9aa0a6); font-size: 12px; }
.tugtile-plain .ej-inlimg { display: none; }
.tugtile-plain .tg-line.ej-hasimg { color: var(--text-normal, #1f2328); font-size: 1em; }
