/* =============================================================================
   Batch 03 — UX P2: Perf / Polish / Responsive (AX-0268…AX-0360)
   Owned file — parent @imports this. Do not edit app.css / js from this batch.
   Tokens: existing --act-* / CLIO surfaces only. Containment + will-change sparingly.
   ============================================================================= */

/* -----------------------------------------------------------------------------
   Visual rhythm (AX-0331…AX-0360)
   Card padding / gap stay on a 2px grid (2…60). Snap rem tokens to even px so
   density media queries cannot land on odd subpixels.
   ----------------------------------------------------------------------------- */
.kanban:not(.projects-board),
.home-lanes {
  --act-rhythm-unit: 2px;
  --act-rhythm-max: 60px;
  /* Snap pads/gaps to nearest even px on the 2…60 rhythm. */
  --act-card-pad-y-snap: clamp(
    var(--act-rhythm-unit),
    round(nearest, var(--act-card-pad-y), var(--act-rhythm-unit)),
    var(--act-rhythm-max)
  );
  --act-card-pad-x-snap: clamp(
    var(--act-rhythm-unit),
    round(nearest, var(--act-card-pad-x), var(--act-rhythm-unit)),
    var(--act-rhythm-max)
  );
  --act-card-gap-snap: clamp(
    var(--act-rhythm-unit),
    round(nearest, var(--act-card-gap), var(--act-rhythm-unit)),
    var(--act-rhythm-max)
  );
  --act-lane-pad-snap: clamp(
    var(--act-rhythm-unit),
    round(nearest, var(--act-lane-pad), var(--act-rhythm-unit)),
    var(--act-rhythm-max)
  );
  --act-board-gap-snap: clamp(
    var(--act-rhythm-unit),
    round(nearest, var(--act-board-gap), var(--act-rhythm-unit)),
    var(--act-rhythm-max)
  );
  --act-lane-gap-snap: clamp(
    var(--act-rhythm-unit),
    round(nearest, var(--act-lane-gap), var(--act-rhythm-unit)),
    var(--act-rhythm-max)
  );
}

.kanban:not(.projects-board) {
  gap: var(--act-lane-gap-snap, var(--act-lane-gap));
}

.kanban:not(.projects-board) .kanban-card,
.kanban:not(.projects-board) .kc {
  padding: var(--act-card-pad-y-snap, var(--act-card-pad-y))
    var(--act-card-pad-x-snap, var(--act-card-pad-x));
  margin-bottom: var(--act-card-gap-snap, var(--act-card-gap));
  gap: var(--act-card-gap-snap, var(--act-space-2));
}

.home-lane-card {
  padding: var(--act-card-pad-y-snap, var(--act-home-card-pad-y, var(--act-card-pad-y)))
    var(--act-card-pad-x-snap, var(--act-home-card-pad-x, var(--act-card-pad-x)));
}

/* Fallback when round() unsupported: even rem aliases already map to 2px grid @16px root. */
@supports not (width: round(nearest, 1px, 2px)) {
  .kanban:not(.projects-board) .kanban-card,
  .kanban:not(.projects-board) .kc {
    padding: var(--act-card-pad-y) var(--act-card-pad-x);
    margin-bottom: var(--act-card-gap);
    gap: var(--act-space-2);
  }
}

/* -----------------------------------------------------------------------------
   Scroll containment — no double scroll traps (AX-0268/0273/0278/0283/0288)
   Board owns X; lane owns Y; lane-body never nests a second scrollport.
   ----------------------------------------------------------------------------- */
.page:has(.act-toolbar-ks) > .kanban:not(.projects-board),
.page:has(.act-toolbar-ks) .kanban:not(.projects-board) {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  overscroll-behavior-y: none;
  -webkit-overflow-scrolling: touch;
  min-width: 0;
  max-width: 100%;
}

.kanban:not(.projects-board) .lane,
.kanban:not(.projects-board) .kanban-lane {
  overflow-x: hidden;
  overflow-y: auto;
  /* Y only — contain on both axes blocks Mac trackpad H-scroll chaining to the board. */
  overscroll-behavior-x: auto;
  overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
  /* Keep drop outline/inset ring paintable inside the scrollport. */
  background-clip: padding-box;
}

.kanban:not(.projects-board) .kanban-lane-body,
.kanban:not(.projects-board) .lane > .kanban-lane-body {
  overflow: visible;
  min-height: 0;
  /* Body grows; lane is the only vertical scrollport. */
  flex: 1 1 auto;
}

/* Board owns scrollports: .main must not steal vertical wheel or clip the H-bar. */
.main:has(.kanban:not(.projects-board)) {
  overscroll-behavior-y: contain;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.home-lanes {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}
.home-lane {
  overflow-x: hidden;
  overflow-y: auto;
  /* Y only — contain on both axes blocks Mac trackpad H-scroll chaining to the board. */
  overscroll-behavior-x: auto;
  overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
}
.home-lane-body {
  overflow: visible;
  min-height: 0;
}

/* Narrow ≤1100 (data-narrow + media) */
html[data-narrow='1'] .page:has(.act-toolbar-ks) .kanban:not(.projects-board) {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
}
html[data-narrow='1'] .kanban:not(.projects-board) .lane,
html[data-narrow='1'] .kanban:not(.projects-board) .kanban-lane {
  overflow-x: hidden;
  overflow-y: auto;
  /* Y only — contain on both axes blocks Mac trackpad H-scroll chaining to the board. */
  overscroll-behavior-x: auto;
  overscroll-behavior-y: contain;
}
html[data-narrow='1'] .kanban:not(.projects-board) .kanban-lane-body {
  overflow: visible;
}
html[data-narrow='1'] .main:has(.kanban:not(.projects-board)) {
  overscroll-behavior-y: contain;
}

@media (max-width: 1100px) {
  .page:has(.act-toolbar-ks) .kanban:not(.projects-board) {
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
  }
  .kanban:not(.projects-board) .lane,
  .kanban:not(.projects-board) .kanban-lane {
    overflow-x: hidden;
    overflow-y: auto;
    /* Y only — contain on both axes blocks Mac trackpad H-scroll chaining to the board. */
  overscroll-behavior-x: auto;
  overscroll-behavior-y: contain;
  }
  .kanban:not(.projects-board) .kanban-lane-body {
    overflow: visible;
  }
}

/* Compact ≤600 */
html[data-compact='1'] .page:has(.act-toolbar-ks) .kanban:not(.projects-board) {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
}
html[data-compact='1'] .kanban:not(.projects-board) .lane,
html[data-compact='1'] .kanban:not(.projects-board) .kanban-lane {
  overflow-x: hidden;
  overflow-y: auto;
  /* Y only — contain on both axes blocks Mac trackpad H-scroll chaining to the board. */
  overscroll-behavior-x: auto;
  overscroll-behavior-y: contain;
}
html[data-compact='1'] .kanban:not(.projects-board) .kanban-lane-body {
  overflow: visible;
}
html[data-compact='1'] .main:has(.kanban:not(.projects-board)) {
  overscroll-behavior-y: contain;
}

@media (max-width: 600px) {
  .page:has(.act-toolbar-ks) .kanban:not(.projects-board) {
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
  }
  .kanban:not(.projects-board) .lane,
  .kanban:not(.projects-board) .kanban-lane {
    overflow-x: hidden;
    overflow-y: auto;
    /* Y only — contain on both axes blocks Mac trackpad H-scroll chaining to the board. */
  overscroll-behavior-x: auto;
  overscroll-behavior-y: contain;
  }
  .kanban:not(.projects-board) .kanban-lane-body {
    overflow: visible;
  }
}

/* Phone ≤480 */
html[data-phone='1'] .page:has(.act-toolbar-ks) .kanban:not(.projects-board) {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
}
html[data-phone='1'] .kanban:not(.projects-board) .lane,
html[data-phone='1'] .kanban:not(.projects-board) .kanban-lane {
  overflow-x: hidden;
  overflow-y: auto;
  /* Y only — contain on both axes blocks Mac trackpad H-scroll chaining to the board. */
  overscroll-behavior-x: auto;
  overscroll-behavior-y: contain;
}
html[data-phone='1'] .kanban:not(.projects-board) .kanban-lane-body {
  overflow: visible;
}
html[data-phone='1'] .main:has(.kanban:not(.projects-board)) {
  overscroll-behavior-y: contain;
}

@media (max-width: 480px) {
  .page:has(.act-toolbar-ks) .kanban:not(.projects-board) {
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
  }
  .kanban:not(.projects-board) .lane,
  .kanban:not(.projects-board) .kanban-lane {
    overflow-x: hidden;
    overflow-y: auto;
    /* Y only — contain on both axes blocks Mac trackpad H-scroll chaining to the board. */
  overscroll-behavior-x: auto;
  overscroll-behavior-y: contain;
  }
  .kanban:not(.projects-board) .kanban-lane-body {
    overflow: visible;
  }
}

/* Coarse pointer */
html[data-coarse='1'] .page:has(.act-toolbar-ks) .kanban:not(.projects-board) {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}
html[data-coarse='1'] .kanban:not(.projects-board) .lane,
html[data-coarse='1'] .kanban:not(.projects-board) .kanban-lane {
  overflow-x: hidden;
  overflow-y: auto;
  /* Y only — contain on both axes blocks Mac trackpad H-scroll chaining to the board. */
  overscroll-behavior-x: auto;
  overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
}
html[data-coarse='1'] .kanban:not(.projects-board) .kanban-lane-body {
  overflow: visible;
}
html[data-coarse='1'] .main:has(.kanban:not(.projects-board)) {
  overscroll-behavior-y: contain;
}

@media (pointer: coarse) {
  .page:has(.act-toolbar-ks) .kanban:not(.projects-board) {
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
  }
  .kanban:not(.projects-board) .lane,
  .kanban:not(.projects-board) .kanban-lane {
    overflow-x: hidden;
    overflow-y: auto;
    /* Y only — contain on both axes blocks Mac trackpad H-scroll chaining to the board. */
  overscroll-behavior-x: auto;
  overscroll-behavior-y: contain;
    -webkit-overflow-scrolling: touch;
  }
  .kanban:not(.projects-board) .kanban-lane-body {
    overflow: visible;
  }
}

/* Dark theme — same scroll geometry; tokens already theme-aware */
[data-theme='dark'] .page:has(.act-toolbar-ks) .kanban:not(.projects-board) {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
}
[data-theme='dark'] .kanban:not(.projects-board) .lane,
[data-theme='dark'] .kanban:not(.projects-board) .kanban-lane {
  overflow-x: hidden;
  overflow-y: auto;
  /* Y only — contain on both axes blocks Mac trackpad H-scroll chaining to the board. */
  overscroll-behavior-x: auto;
  overscroll-behavior-y: contain;
}
[data-theme='dark'] .kanban:not(.projects-board) .kanban-lane-body {
  overflow: visible;
}
[data-theme='dark'] .main:has(.kanban:not(.projects-board)) {
  overscroll-behavior-y: contain;
}

/* -----------------------------------------------------------------------------
   Full titles via wrap (AX-0269/0274/0279/0284/0289 + all surfaces)
   Essential identity — never clamp / ellipsis the title away.
   ----------------------------------------------------------------------------- */
.kanban-card-title,
.home-lane-card .kanban-card-title,
.project-card .kanban-card-title {
  min-width: 0;
  display: block;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  overflow-wrap: anywhere;
  word-break: break-word;
  hyphens: auto;
  -webkit-line-clamp: unset;
  line-clamp: unset;
}

html[data-narrow='1'] .kanban-card-title,
html[data-compact='1'] .kanban-card-title,
html[data-phone='1'] .kanban-card-title,
html[data-coarse='1'] .kanban-card-title,
[data-theme='dark'] .kanban-card-title {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  overflow-wrap: anywhere;
  word-break: break-word;
  -webkit-line-clamp: unset;
  line-clamp: unset;
}

@media (max-width: 1100px), (max-width: 600px), (max-width: 480px), (pointer: coarse) {
  .kanban-card-title,
  .home-lane-card .kanban-card-title {
    white-space: normal;
    overflow: visible;
    overflow-wrap: anywhere;
    word-break: break-word;
    -webkit-line-clamp: unset;
    line-clamp: unset;
  }
}

/* -----------------------------------------------------------------------------
   Drop target highlight remains visible (AX-0270/0275/0280/0285/0290)
   Inset ring + dashed outline (not color-only); not clipped by lane scroll.
   ----------------------------------------------------------------------------- */
.kanban:not(.projects-board) .lane.is-drop-target,
.kanban:not(.projects-board) .kanban-lane.is-drop-target,
.home-lane.is-drop-target {
  position: relative;
  z-index: 2;
  /* Prefer inset paint so overflow:auto does not clip the feedback. */
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 55%, transparent);
  outline: 1px dashed color-mix(in srgb, var(--accent) 60%, transparent);
  outline-offset: -3px;
  background: color-mix(in srgb, var(--accent-soft) 55%, transparent);
}

[data-theme='dark'] .kanban:not(.projects-board) .lane.is-drop-target,
[data-theme='dark'] .kanban:not(.projects-board) .kanban-lane.is-drop-target,
[data-theme='dark'] .home-lane.is-drop-target {
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 70%, transparent);
  outline-color: color-mix(in srgb, var(--accent) 75%, transparent);
  background: color-mix(in srgb, var(--accent-soft) 40%, var(--kanban-lane));
}

html[data-narrow='1'] .kanban:not(.projects-board) .lane.is-drop-target,
html[data-narrow='1'] .kanban:not(.projects-board) .kanban-lane.is-drop-target,
html[data-compact='1'] .kanban:not(.projects-board) .lane.is-drop-target,
html[data-compact='1'] .kanban:not(.projects-board) .kanban-lane.is-drop-target,
html[data-phone='1'] .kanban:not(.projects-board) .lane.is-drop-target,
html[data-phone='1'] .kanban:not(.projects-board) .kanban-lane.is-drop-target,
html[data-coarse='1'] .kanban:not(.projects-board) .lane.is-drop-target,
html[data-coarse='1'] .kanban:not(.projects-board) .kanban-lane.is-drop-target {
  z-index: 2;
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 55%, transparent);
  outline: 1px dashed color-mix(in srgb, var(--accent) 60%, transparent);
  outline-offset: -3px;
}

@media (max-width: 1100px), (max-width: 600px), (max-width: 480px), (pointer: coarse) {
  .kanban:not(.projects-board) .lane.is-drop-target,
  .kanban:not(.projects-board) .kanban-lane.is-drop-target {
    z-index: 2;
    box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 55%, transparent);
    outline: 1px dashed color-mix(in srgb, var(--accent) 60%, transparent);
    outline-offset: -3px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .kanban:not(.projects-board) .lane,
  .kanban:not(.projects-board) .kanban-lane,
  .home-lane {
    transition: none !important;
  }
}

/* -----------------------------------------------------------------------------
   Drag frame budget (AX-0291…AX-0330) — scales to ~1000 cards
   Contain paint; skip offscreen cards; during drag, freeze sibling chrome.
   will-change only on the active drag + drop target.
   ----------------------------------------------------------------------------- */
.kanban:not(.projects-board) {
  contain: layout;
}

.kanban:not(.projects-board) .kanban-lane,
.kanban:not(.projects-board) .lane {
  contain: layout paint;
  content-visibility: auto;
  contain-intrinsic-size: auto 20rem;
}

.kanban:not(.projects-board) .kanban-card,
.kanban:not(.projects-board) .kc {
  contain: layout style;
  content-visibility: auto;
  contain-intrinsic-size: auto 4.5rem;
}

.home-lane {
  contain: layout paint;
}
.home-lane-card {
  contain: layout style;
  content-visibility: auto;
  contain-intrinsic-size: auto 3.5rem;
}

/* Drag session: kill hover transforms / filters that thrash layers. */
html.act-is-dragging .kanban:not(.projects-board) .kanban-card,
html.act-is-dragging .kanban:not(.projects-board) .kc,
html.act-is-dragging .home-lane-card {
  transition: none !important;
  transform: none !important;
  filter: none !important;
}

html.act-is-dragging .kanban:not(.projects-board) .kanban-card:hover,
html.act-is-dragging .kanban:not(.projects-board) .kc:hover,
html.act-is-dragging .home-lane-card:hover {
  transform: none !important;
  filter: none !important;
  box-shadow: var(--act-elev);
}

html.act-is-dragging .kanban:not(.projects-board) .lane,
html.act-is-dragging .kanban:not(.projects-board) .kanban-lane,
html.act-is-dragging .home-lane {
  transition: background var(--act-motion-d, 120ms) var(--act-motion-ease, ease),
    box-shadow var(--act-motion-d, 120ms) var(--act-motion-ease, ease) !important;
}

/* Sparse will-change — only live drag actors. */
html.act-is-dragging .kanban-card.is-dragging,
html.act-is-dragging .kc.is-dragging {
  will-change: transform, opacity;
  content-visibility: visible;
  contain: layout style paint;
}

html.act-is-dragging .lane.is-drop-target,
html.act-is-dragging .kanban-lane.is-drop-target,
html.act-is-dragging .home-lane.is-drop-target {
  will-change: background, box-shadow;
  content-visibility: visible;
}

html.act-is-dragging .kanban:not(.projects-board) .kanban-card:not(.is-dragging),
html.act-is-dragging .kanban:not(.projects-board) .kc:not(.is-dragging) {
  will-change: auto;
  pointer-events: none; /* hit-test lanes, not every card under the ghost */
}

html.act-is-dragging .kanban:not(.projects-board) .lane,
html.act-is-dragging .kanban:not(.projects-board) .kanban-lane,
html.act-is-dragging .kanban:not(.projects-board) .kanban-lane-body {
  pointer-events: auto;
}

@media (prefers-reduced-motion: reduce) {
  html.act-is-dragging .kanban:not(.projects-board) .lane,
  html.act-is-dragging .kanban:not(.projects-board) .kanban-lane,
  html.act-is-dragging .home-lane {
    transition: none !important;
  }
  html.act-is-dragging .kanban-card.is-dragging,
  html.act-is-dragging .kc.is-dragging,
  html.act-is-dragging .lane.is-drop-target,
  html.act-is-dragging .kanban-lane.is-drop-target,
  html.act-is-dragging .home-lane.is-drop-target {
    will-change: auto;
  }
}
