/* ==========================================================================
 * kik-task-board — kanban board over Task workflow states.
 *
 * Rendered by templates/partials/kik_task_board.html, driven by
 * assets/main/script/kik-task-board.js. Loaded as a standalone stylesheet
 * (see templates/partials/quaive_head.html) so it ships without recompiling
 * the global bundle and keeps its blast radius to the board only.
 *
 * The board fills the whole document-body: its content wrapper takes the full
 * height of the (definite-height, absolutely-positioned) .document-body, and
 * the columns flex to share the width equally and the height fully, each with
 * its own scrolling body.
 *
 * Cards reuse the task-detail info-well markup verbatim
 * (.page-module.pat-well.type-sample-sheet-task + the rich-description well);
 * the rules below only shrink it.
 * ========================================================================== */

/* The board's content wrapper — a .document-content that must fill, not pad. */
.kik-task-board-content {
    /* ONE GUTTER, AND IT IS THE WINDOW'S INSET (Daniel, 2026-09-07): the
       room around the columns — right, bottom, left — and the room between
       them equal the distance from the window's close control to the side of
       the window (or of the viewport), which --kik-win-inset is the one
       measure of (app-window.css / windowed-mode.css seat the bar, the
       controls and a modal's bar on it). Every gap below reads --grid-gutter,
       so it is set once here; the fallback is the board's old 12px on a face
       that declares no inset. */
    --grid-gutter: var(--kik-win-inset, 12px);
    box-sizing: border-box;
    height: 100%;
    min-height: 0;
    display: flex;
    overflow: hidden;
    /* Override .document-content's large default padding so the columns reach
       the document-body edges (a small gutter keeps them off the corners). */
    padding: var(--grid-gutter, 12px) !important;
    /* …except at the head, which keeps the app bar's clearance on top of that
       gutter. The board branch renders no toolbar of its own (the list branch
       does), so the windowed clearance rule — which keys on a
       .kik-win-main-toolbar inside the content — never applied, and the
       shorthand above would have flattened it anyway. Without it the first
       column's title sat under the window's own maximise and close controls.

       --kik-win-toolbar-height is declared on the app body by windowed-mode.css
       and is simply absent on faces that have no window chrome, so the 0px
       fallback leaves those exactly as they were: gutter all round. */
    padding-top: calc(var(--kik-win-toolbar-height, 0px) + var(--grid-gutter, 12px)) !important;
}

.kik-task-board {
    flex: 1 1 auto;
    display: flex;
    align-items: stretch;
    gap: var(--grid-gutter, 12px);
    width: 100%;
    min-height: 0;
    min-width: 0;
}

/* -------- Columns ---------------------------------------------------------- */

.kik-task-board-column {
    /* A COLUMN'S SIDE ROOM, DECLARED ONCE. The header's padding, the body's
       padding and the rule between them all read it, so a card's edge, a
       title's edge and the end of that rule are one vertical by construction
       rather than by three numbers agreeing. The faces below set it. */
    --kik-column-inset: 0.6em;
    flex: 1 1 0;            /* equal width, share the row fully */
    min-width: 0;
    display: flex;
    flex-direction: column;
    min-height: 0;
    background-color: var(--colour-well-background, rgba(0, 0, 0, 0.03));
    border-radius: var(--border-radii, 8px);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.07);
}

/* ON ONE AND PRO A COLUMN IS NO PLATE (Daniel, 2026-09-08): the pane is its
   own sheet and the cards its content, so the column keeps its box and its
   header rule and paints nothing of its own. The hub keeps the well-toned
   plate above — its board stands beside sidebar cards that are plates too. */
html.kik-one .kik-task-board-column {
    background-color: transparent;
    box-shadow: none;
    position: relative;
    /* …and the side room the zeroed gutter gave back (see below). One number
       here reaches the header's padding, the body's padding and the rule
       between them, so there is nothing left for a later shorthand to undo —
       which matters on PRO, which is both a hub face and a kik-one face. */
    --kik-column-inset: calc(0.6em + var(--grid-gutter, 12px) / 2);
}

/* …AND A HAIRLINE BETWEEN THEM (Daniel, 2026-09-08). With the plates gone the
   columns are three runs of cards on one sheet, so a line says where one ends
   and the next begins. Drawn by each column after the first, centred in the
   gutter beside it — half a gutter out from its own leading edge — in the
   same 8% ink the column headers and card outlines take, so the board has
   one hairline and not two. */
html.kik-one .kik-task-board-column + .kik-task-board-column::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    /* ON THE COLUMN'S OWN EDGE, because there is no gutter left to centre in
       (see below). */
    inset-inline-start: 0;
    width: 1px;
    /* Darker than the 8% hairline the headers and cards take (Daniel,
       2026-09-08): a line standing alone in open gutter needs more ink than
       one edging a box. */
    background-color: color-mix(in srgb, var(--body-font-colour, #000) 16%, transparent);
    pointer-events: none;
}

/* THE SCROLLBAR STANDS AGAINST THE LINE (Daniel, 2026-09-08). A column's list
   scrolls, and its bar was drawn at the edge of the column BOX — which, with a
   gutter between the columns, left the bar floating in white with the hairline
   a few px further out. Read as a bar belonging to nothing.

   So the gutter goes to zero and the room it gave is folded into the columns
   themselves (--kik-column-inset, set on the column above): each pads by half
   the old gutter more on both sides, which leaves the distance between two
   cards ACROSS a boundary exactly what it was — 0.6em + gutter + 0.6em before,
   two paddings of 0.6em + gutter/2 now — and puts the scrollbar inside the
   column's own padding, hard against the line. The header takes the SAME
   number, so a title, the rule under it and the cards below all start on one
   vertical.

   Not on the hub: its columns are still plates with a gutter between them, and
   a plate's scrollbar belongs at the plate's edge. */
html.kik-one .kik-task-board {
    gap: 0;
}


.kik-task-board-column-header {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    /* Title + count cluster on the left; the '+' is pushed right by its own
       margin-left:auto (so no justify-content needed). */
    justify-content: flex-start;
    gap: 0.5em;
    /* 8px of height, not an em (Daniel, 2026-09-08). */
    padding: 8px var(--kik-column-inset);
    position: relative;
}

/* THE RULE UNDER A HEADER IS AS WIDE AS THE CARDS (Daniel, 2026-09-08). It was
   a border-bottom, and a border runs the whole border box — so it reached past
   the cards on both sides and read as a rule belonging to the column rather
   than to the list under it. Drawn as a line inset by the column's own side
   room instead, which is the same number the cards are padded by, so the two
   ends and a card's two edges are one vertical.

   A pseudo-element rather than a background: the header is a flex row, and a
   background sized to the content box would sit at the bottom of THAT box —
   8px above the header's own edge, where the line does not belong. */
.kik-task-board-column-header::after {
    content: '';
    position: absolute;
    inset-inline: var(--kik-column-inset);
    bottom: 0;
    height: 1px;
    /* The same 8% ink the card outlines take, so header and cards agree. */
    background-color: color-mix(in srgb, var(--body-font-colour, #000) 8%, transparent);
    pointer-events: none;
}

.kik-task-board-column-title {
    margin: 0;
    /* Don't grow — keep the count badge right after the title; shrink + ellipsis
       only when the title is too long. */
    flex: 0 1 auto;
    min-width: 0;
    font-size: 0.95rem;
    font-weight: bold;
    line-height: 1.2;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    /* Body ink, not the app colour (Daniel, 2026-09-08): the accent rule under
       the header already names the app, so the title need not repeat it. */
    color: var(--body-font-colour);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The column-header '+': create a task directly in this column. A flat,
   accent-tinted square that fills in on hover — opens the create modal pre-set
   to the column's workflow state (see kik_task_board.html). The '+' is a
   fontello glyph drawn by ::before (same mechanism as the toolbar create icon):
   the absolutely-positioned glyph fills the button and is flex-centred, so it
   sits dead-centre regardless of the link text (which is hidden via text-indent
   but kept for screen readers / the tooltip). */
/* THE COLUMN'S '+' IS A HOUSE DISC (Daniel, 2026-09-08) — the same frosted
   plate with a centred line icon that a card's More menu wears, and that every
   other icon control in the app is. It was a fontello glyph on a bare square,
   which is the one shape here that is neither.

   Sized by the menu-list's own two tokens, as the card's disc is, so the two
   marks on one board agree without a third number. ``font-size: 0`` silences
   the link's own word-text (the label is still there for a screen reader and
   the tooltip), and every measure below is in rem for the reason the card's
   disc learned the hard way: an em on an element whose font-size is zero is
   zero. */
.kik-task-board-column-add {
    --pat-menu-list-more-size: 1.4rem;
    --pat-menu-list-more-glyph-size: 0.9rem;
    flex: 0 0 auto;
    margin-left: auto;          /* push to the far right of the header */
    position: relative;
    box-sizing: border-box;
    width: var(--pat-menu-list-more-size);
    height: var(--pat-menu-list-more-size);
    min-width: 0;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background-color: var(--pat-toolbar-icon-frost-background, var(--pat-surface-background-colour));
    box-shadow: var(--pat-button-box-shadow);
    -webkit-backdrop-filter: var(--pat-toolbar-icon-backdrop-filter);
    backdrop-filter: var(--pat-toolbar-icon-backdrop-filter);
    /* Body ink, inherited — no colour of its own (Daniel, 2026-09-08). */
    text-decoration: none;
    cursor: default;            /* match the toolbar icon buttons (no pointer) */
    font-size: 0;
    transition: background-color 0.12s ease, color 0.12s ease;

    > svg.kik-icon {
        display: block;
        width: var(--pat-menu-list-more-glyph-size);
        height: var(--pat-menu-list-more-glyph-size);
        font-size: var(--pat-menu-list-more-glyph-size);
    }
}

/* The component's own fontello glyph, which the SVG replaces. */
.kik-task-board-column-add::before {
    display: none;
}

/* THE DISC SHOWS ITSELF, THEN WAITS FOR THE POINTER (Daniel, 2026-09-08).
   Three discs sitting on a board all day are furniture; the control is only
   wanted when a column is being looked at. So: on a freshly loaded board
   every disc is visible for a moment and fades away, which tells a new pair
   of eyes where the control lives — and from then on a disc appears the
   instant its column is hovered, and while anything in the column has focus
   so the keyboard is never left guessing.

   The reveal is an animation and the hover is a plain declaration, and an
   animation outranks a declaration for as long as it runs — so the hover
   rule also cancels the animation, or a column hovered during the fade would
   wait for it to finish. The board is injected into its pane, and an element
   arriving in the DOM starts its animation then, which is exactly the
   "freshly loaded" the fade is for.

   Only where a pointer can hover: a touch screen has no hover to reveal on,
   so there the discs simply stay. */
@media (hover: hover) {
    .kik-task-board-column-add {
        opacity: 0;
        /* Opacity is NOT transitioned (Daniel, 2026-09-08): the disc is there
           the instant the column is hovered and gone the instant it is not.
           Only the load-time reveal below fades. */
        transition: background-color 0.12s ease, color 0.12s ease;
        animation: kik-task-board-add-reveal 2.4s ease-out both;
    }
    .kik-task-board-column:hover .kik-task-board-column-add,
    .kik-task-board-column:focus-within .kik-task-board-column-add {
        opacity: 1;
        animation: none;
    }
}

@keyframes kik-task-board-add-reveal {
    0%, 55% { opacity: 1; }
    100% { opacity: 0; }
}

/* LIT LIKE A TOOLBAR ICON DISC (Daniel, 2026-09-08): the same wash, ink and
   frost quaive/toolbar-icons gives a bar's icon on hover, from the same
   tokens, rather than an accent fill of its own. Focus takes it too, so the
   keyboard sees what the pointer sees. No underline either way — the glyph
   would otherwise pick up the global a:hover underline. */
.kik-task-board-column-add:focus {
    text-decoration: none;
    color: var(--pat-toolbar-icon-text-colour-hover);
    background-color: var(--pat-toolbar-icon-background-colour-hover);
    -webkit-backdrop-filter: var(--pat-toolbar-button-backdrop-filter-hover);
    backdrop-filter: var(--pat-toolbar-button-backdrop-filter-hover);
}

@media (hover: hover) {
  .kik-task-board-column-add:hover {
      text-decoration: none;
      color: var(--pat-toolbar-icon-text-colour-hover);
      background-color: var(--pat-toolbar-icon-background-colour-hover);
      -webkit-backdrop-filter: var(--pat-toolbar-button-backdrop-filter-hover);
      backdrop-filter: var(--pat-toolbar-button-backdrop-filter-hover);
  }
}

.kik-task-board-column-count {
    flex: 0 0 auto;
    box-sizing: border-box;
    min-width: 1.6em;
    padding: 0 0.45em;
    text-align: center;
    font-size: 0.75rem;
    font-weight: bold;
    line-height: 1.5;
    border-radius: 1em;
    /* App-colour text on a 10% app-colour tint of white, with a very subtle
       (slightly stronger) app-colour border. */
    color: var(--app-colour, var(--colour-accent, #888));
    background-color: color-mix(in srgb, var(--app-colour, var(--colour-accent, #888)) 10%, white);
    border: 1px solid color-mix(in srgb, var(--app-colour, var(--colour-accent, #888)) 22%, white);
}

/* ON THE HUB FACES — hub and pro (Daniel, 2026-09-08) — the header is quieter:
   a tighter inset, and the count disc edged with the same 8% hairline the
   header itself ends in rather than a tint of the app colour. One (the
   personal cloud) keeps the base rules above. */
body:is([data-platform-mode="hub"], [data-platform-mode="pro"]) {
    .kik-task-board-column-header {
        /* The vertical only. The side room is one number now
           (--kik-column-inset, set per face on the column), and a shorthand
           here would take it back — on PRO especially, which is both a hub
           face and a kik-one face. */
        padding-block: 8px;
    }
    .kik-task-board-column-count {
        border-color: color-mix(in srgb, var(--body-font-colour, #000) 8%, transparent);
    }
}

.kik-task-board-column-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 0.6em var(--kik-column-inset);
    display: flex;
    flex-direction: column;
    gap: 0.6em;
    transition: background-color 0.15s ease;
}

/* A column the dragged card is hovering over. */
.kik-task-board-column-body.kik-drop-target {
    background-color: color-mix(in srgb, var(--app-colour, #888) 12%, transparent);
    box-shadow: inset 0 0 0 2px var(--app-colour, var(--colour-accent, #888));
    border-radius: var(--border-radii, 8px);
}

/* -------- Cards ------------------------------------------------------------ */

.kik-task-card {
    position: relative;
    flex: 0 0 auto;
    box-sizing: border-box;
    padding: 0.6em 0.7em;
    /* THE THEME'S OPAQUE SURFACE, not a literal white (Daniel, 2026-09-07:
       "board looks bad in dark mode"). The card kept #fff while its text is
       the theme's ink — light on the dark theme — so every card came out pale
       type on a white slab.

       --kik-sidebar-flat-colour and not --pat-surface-background-colour: the
       surface token is translucent by design and expects the backdrop-filter
       that goes with it, which a card does not carry. The flat one is that
       same glass already composited to one opaque colour, for exactly this
       case — the theme calls it the wash "flattened for the surfaces that no
       longer carry the filter". #FFFAF2 light, #25211E dark. */
    background-color: var(--kik-sidebar-flat-colour, #fff);
    border-radius: 10px;
    /* The rim carries the ink rather than a literal black, so it is a hairline
       on either ground instead of vanishing into the dark one. */
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12),
                0 0 0 1px color-mix(in srgb, var(--body-font-colour, #000) 8%, transparent);
    /* No grab hand at rest (Daniel, 2026-09-07): the card is a click surface
       first — select, open — and the house shows no special cursor on a
       control; the drag cursor appears only while a drag is under way. */
    cursor: default;
    /* Left accent stripe coloured by priority (overridden below). */
    border-left: 3px solid var(--app-colour, var(--colour-accent, #bbb));
    transition: box-shadow 0.15s ease, transform 0.05s ease;

    /* THE PICKED CARD (board.js: a click selects, a double click opens).

       THE ACCENT, AND STANDING OFF (Daniel, 2026-09-08). It took the shared
       selection token, which draws in body ink at 1px — right on a calendar,
       where the ring sits on a tinted block and needs no colour of its own,
       and wrong here: a card is already outlined at 8% and rimmed on its left
       by its priority, so an ink ring read as one more edge among three rather
       than as "this one". The accent says it in a colour nothing else on the
       card uses. The offset is 3px so the ring clears the card's own outline
       and its rounded corner instead of tracing them. */
    &.kik-task-card-selected {
        outline: 2px solid var(--colour-accent);
        outline-offset: 3px;
    }
}

@media (hover: hover) {
  .kik-task-card:hover {
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18),
                  0 0 0 1px color-mix(in srgb, var(--body-font-colour, #000) 12%, transparent);
  }
}

.kik-task-card.kik-dragging {
    cursor: grabbing;
    opacity: 0.55;
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.28);
}

/* A SIDEBAR ROW on its way to a column (board.js: the Reminders wells sit
   beside the board, so a ticket can be dropped on a column without being
   opened). The row never leaves its well while it travels — there is no card
   to lift yet — so its own dimming is the whole of the feedback it can carry;
   the column it is over lights up as usual. */
.pat-checklist .label.kik-dragging {
    cursor: grabbing;
    opacity: 0.55;
}

/* THE WELL A DRAGGED CARD CAME FROM, while the card is over it — where the
   ticket goes back to when it is parked. Only that one well ever wears this,
   so the highlight is also the answer to "which of these will take it": the
   accent ring the columns use, drawn round the whole well. */
.pat-well.kik-drop-target {
    outline: 2px dashed var(--app-colour, var(--colour-accent, #888));
    outline-offset: -2px;
    border-radius: var(--border-radii, 8px);
}

/* THE TO-DO SCREEN taking a row from the sidebar — the same dashed ring the
   well wears, since it is the same gesture arriving somewhere else. The box
   fills the body, so the ring is drawn inside it rather than around it. */
[data-todo-drop-url].kik-drop-target {
    outline: 2px dashed var(--app-colour, var(--colour-accent, #888));
    /* OUTSET, not hugging (Daniel, 2026-09-08). The list fills the body, so a
       ring drawn at its edge reads as a border on the content rather than as a
       target around it; pushed out it sits in the document's own padding and
       the whole screen reads as the thing being dropped on. */
    outline-offset: 15px;
    border-radius: var(--border-radii, 8px);
}

/* …and while the answer is on its way. The screen is replaced wholesale when
   it lands, so the only thing to say meanwhile is that something is happening. */
[data-todo-drop-url].kik-drop-busy {
    opacity: 0.6;
    transition: opacity 0.12s ease;
}

.kik-task-card.priority-high { border-left-color: #d0021b; }
.kik-task-card.priority-medium { border-left-color: #f5a623; }
.kik-task-card.priority-low { border-left-color: #7ed321; }

/* Open-detail affordance — the only click target; pinned top-right, clear of
   the title, so the rest of the card is free for dragging. */
/* THE OPEN CONTROL (Daniel, 2026-09-07): the house angle-right line icon on
   a small frosted disc — the same disc a checklist row's More menu wears
   (quaive/checklist), sized by the menu-list's tokens so the two agree —
   revealed while the pointer is on the card, while the control has focus,
   and while the card is selected, so it is reachable without a pointer. The
   whole card is the click surface (board.js: a click selects, a double
   click or a tap opens); the disc is the visible way in. */
/* THE OPENER, KEPT AND CLIPPED. The corner shows a More menu now, but a
   double click on the card (one tap on touch) still opens the ticket, and
   board.js does that by clicking this anchor — the one control that has always
   meant "open". Hidden the house way, visually and never semantically: it
   stays in the tree, reachable by the click board.js sends, by the keyboard
   and by a screen reader, which display:none would take away from all three. */
.kik-task-card-open {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.kik-task-card-more {
    /* Slightly smaller than a menu row's disc (Daniel, 2026-09-07) — the
       same size a checklist row's More disc takes, through the same two
       tokens, so a card and a row agree. */
    --pat-menu-list-more-size: 1.4rem;
    --pat-menu-list-more-glyph-size: 0.85rem;
    position: absolute;
    /* Centred on the title's first line (Daniel, 2026-09-07): the card's own
       padding down to the title, then half the title's line box, less half
       the disc — so on a one-line card the disc sits level with the title,
       and on a taller card it stays on the title's first line. The numbers
       are the card's padding and the title's size and line-height below;
       the em is the card's own — the disc inherits the card's font size,
       and carries no text of its own for it to size (the icon has its own
       measure). */
    /* ONE INSET, THREE SIDES (Daniel, 2026-09-07: equal space around it). The
       disc is centred on the title's first line, so on a one-line card the
       room above it and the room below it are the same number by symmetry —
       and the trailing room was a different one (the card's 0.7em padding),
       which read as the disc sitting too far from the edge it is nearest. It
       takes the vertical figure on all three sides now, so the three cannot
       drift apart when the card's padding or the title's size changes. */
    /* IN REM, NOT EM — and that is not a detail (Daniel, 2026-09-08: "more
       menu disc is displaced now"). The disc silences its own label with
       ``font-size: 0``, and an ``em`` on the same element resolves against
       THAT: the 0.6em collapsed to nothing, the inset went negative and the
       disc climbed out of the card's top-right corner. The 0.6 is the card's
       own padding, and the card sets no font size of its own, so a rem is the
       same measure written in a unit the disc cannot zero. */
    --kik-card-open-inset:
        calc(0.6rem + (0.9rem * 1.25 - var(--pat-menu-list-more-size, 1.6rem)) / 2);
    top: var(--kik-card-open-inset);
    right: var(--kik-card-open-inset);
    box-sizing: border-box;
    width: var(--pat-menu-list-more-size, 1.6rem);
    height: var(--pat-menu-list-more-size, 1.6rem);
    min-width: 0;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background-color: var(--pat-toolbar-icon-frost-background, var(--pat-surface-background-colour));
    box-shadow: var(--pat-button-box-shadow);
    -webkit-backdrop-filter: var(--pat-toolbar-icon-backdrop-filter);
    backdrop-filter: var(--pat-toolbar-icon-backdrop-filter);
    color: var(--body-font-colour);
    text-decoration: none;
    cursor: default;
    z-index: 2;
    opacity: 0;
    transition: opacity 0.15s ease;
    /* THE LABEL IS FOR SCREEN READERS, NOT FOR THE CARD. pat_context_menu
       writes "More…" into the trigger as real text, and a disc this size has
       nowhere to put it — it sat beside the dots, spilling out of the plate.
       Zeroed rather than removed, the way a checklist row's own disc does it
       (quaive/checklist), so the words stay in the accessibility tree. The
       ::before is that component's fontello glyph, which the SVG replaces. */
    font-size: 0;

    &::before {
        display: none;
    }

    > svg.kik-icon {
        display: block;
        width: var(--pat-menu-list-more-glyph-size, 1rem);
        height: var(--pat-menu-list-more-glyph-size, 1rem);
        font-size: var(--pat-menu-list-more-glyph-size, 1rem);
    }
}
.kik-task-card-more:focus,
.kik-task-card:focus-within > .kik-task-card-more,
.kik-task-card.kik-task-card-selected > .kik-task-card-more {
    opacity: 1;
}
@media (hover: hover) {
    .kik-task-card:hover > .kik-task-card-more {
        opacity: 1;
    }
    .kik-task-card-more:hover {
        background-color: var(--pat-toolbar-icon-background-colour-hover);
        color: var(--pat-toolbar-icon-text-colour-hover, inherit);
    }
}

/* Card title — plain text (the card itself is the drag handle); leave room on
   the right for the open icon. */
.kik-task-card-title {
    margin: 0 1.9em 0.35em 0;
    font-weight: bold;
    font-size: 0.9rem;
    line-height: 1.25;
    color: inherit;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
}

/* Title-only card (no info well, no description): drop the bottom margin so the
   card hugs the title. The open icon is absolutely positioned, so a lone title
   is the card's last child. */
.kik-task-card-title:last-child {
    margin-bottom: 0;
}

/* -------- The reused detail-well markup, shrunk ---------------------------- */

.kik-task-card .page-module.pat-well {
    /* Strip the well chrome — the card itself is the container now. */
    --pat-well-box-shadow: none;
    --pat-well-background-colour: transparent;
    --pat-well-padding: 0;
    --pat-well-padding-top: 0;
    --pat-well-padding-right: 0;
    --pat-well-padding-bottom: 0;
    --pat-well-padding-left: 0;
    --pat-well-margin-bottom: 0;
    padding: 0;
    margin: 0;
    background: transparent;
    box-shadow: none;
    font-size: 0.72rem;
}

/* A CARD WITH A DESCRIPTION ENDS WHERE THE DESCRIPTION DOES (Daniel,
   2026-09-08): the body fades out at its clamp, and the card's own bottom
   padding under a fade is a strip of blank card below something that has
   already gone quiet. Title-only cards keep their padding. */
.kik-task-card:has(> .type-rich.kik-task-card-description) {
    padding-bottom: 0;
}

/* Description well: keep the markup, but clamp it so a card stays small. */
.kik-task-card-description {
    margin-top: 0.45em !important;
    padding-top: 0.4em !important;
}

.kik-task-card-description .pat-rich {
    /* ZOOMED, NOT RE-SIZED (Daniel, 2026-09-08). The body is the same
       pat-rich a ticket renders in full, scaled down whole: a font-size
       shrank the paragraphs and left every rem-sized heading at its full
       height, so "What & why" stood taller than the card's own title. A
       zoom keeps headings, lists and paragraphs in their normal proportion
       to one another — and to the em-measured clamp below. */
    zoom: 0.72;
    max-height: 7.5em;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
    mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
}

.kik-task-card-description .pat-rich > :first-child { margin-top: 0; }

/* The description's checklist tickboxes are read-only inside a card; how they
   are DRAWN is the checklist component's business (quaive/checklist), since a
   markdown task list renders as a real .pat-checklist. */
.kik-task-card-description input[type=checkbox] {
    pointer-events: none;
}

/* A DRAG FROM THE DESCRIPTION DRAGS THE CARD (Daniel, 2026-09-07). Rich text
   is selectable by its own rule, and a press-and-move on selectable text
   starts a selection, not the ancestor's drag — so a card picked up by its
   middle or its foot went nowhere and showed no drag cursor. Nothing in a
   card is there to be selected or dragged on its own; a link in it is
   opened by a click, not carried off. */
.kik-task-card-description,
.kik-task-card-description * {
    -webkit-user-select: none;
    user-select: none;
}
.kik-task-card-description a,
.kik-task-card-description img {
    -webkit-user-drag: none;
}

/* -------- Small screens ---------------------------------------------------- */
/* On a narrow viewport the three columns won't fit side-by-side; let the board
   scroll horizontally with sensibly-wide columns instead of crushing them. */
@container screen style(--screen-small: true) {
    .kik-task-board {
        overflow-x: auto;
    }
    .kik-task-board-column {
        flex: 0 0 78%;
        min-width: 78%;
    }
}
