/* Generated by tailwindcss-rails, rewritten by Kaktus to inline engine.css and avoid an absolute-path @import that would 404 when served by Propshaft. */

/* Kaktus engine — tells Tailwind to scan engine views for utility classes */
@source "/usr/local/bundle/ruby/3.4.0/bundler/gems/kaktus-21979c79bd98/app/views";

/* Classes built in JS by toast_controller.js (alert types, the action button) — they
   appear in no template, so Tailwind's scan can't see them and they must be safelisted */
@source inline("alert-success alert-warning btn btn-sm btn-ghost");

@layer components {
    /* Flash toast container. Mobile: bottom-center. Top-center (the old default)
       lands under the notch / Dynamic Island in a Hotwire Native shell, where the
       WKWebView is edge-to-edge and `top` measures from the true screen top. So
       anchor at the bottom, above the home indicator via the safe-area inset.
       Apps with a floating bottom tab bar raise --kaktus-toast-bottom so a
       centered toast clears the bar (e.g. CleverMontessori sets it under
       body.so-canvas). Desktop (>= md): top-right corner. */
    .kaktus-toast-container {
        @apply fixed z-50 space-y-2 left-1/2 -translate-x-1/2 md:top-18 md:left-auto md:translate-x-0 md:right-2;
        bottom: var(--kaktus-toast-bottom, calc(env(safe-area-inset-bottom) + 1rem));
    }
}

/* Desktop (>= md) is top-anchored via md:top-18 above; clear the mobile bottom
   anchor so the fixed box isn't stretched between top and bottom. Unlayered so
   it wins over the layered base at md regardless of source order. */
@media (width >= 48rem) {
    .kaktus-toast-container {
        bottom: auto;
    }
}

/* ─── Kaktus UI primitives ──────────────────────────────────────────────
   Tokens with neutral defaults; consumer apps override in their own theme.
   No literal color/font values in the classes themselves — values resolve
   through these tokens or through Tailwind utilities applied via @apply. */
@layer components {
    :root {
        --kx-page-app-max-width: 66rem;
        --kx-page-doc-max-width: 48rem;

        --kx-card-bg:      var(--color-base-100);
        --kx-card-border:  1px solid var(--color-base-300);
        --kx-card-radius:  0.75rem;
        --kx-card-padding: 1.5rem;

        --kx-actions-gap: 0.5rem;
    }

    /* Page shell — three semantic width regimes (docs/decisions/cross-cutting/page-width-regimes.md).
       Alignment belongs to each regime: :app keeps a stable left edge, :doc centers. */
    .kx-page       { /* common hook */ }
    .kx-page-app   { max-width: var(--kx-page-app-max-width); }
    .kx-page-media { /* full width; the layout's container governs */ }
    .kx-page-doc   { max-width: var(--kx-page-doc-max-width); @apply mx-auto; }
    .kx-page-header   { @apply flex flex-wrap items-center justify-between gap-3 mb-6; }
    .kx-page-header > .kx-actions { @apply ml-auto flex-wrap justify-end shrink; }
    .kx-page-title    { @apply text-2xl font-bold; }
    .kx-page-heading  { @apply mt-4 mb-6; }

    /* Card surface */
    .kx-card {
        background: var(--kx-card-bg);
        border: var(--kx-card-border);
        border-radius: var(--kx-card-radius);
        padding: var(--kx-card-padding);
    }
    /* flex-wrap + ml-auto: when title + actions don't fit on one line (narrow
       viewports, long titles), the actions drop to their own row, right-aligned;
       a cluster wider than the row wraps its buttons too (flex-wrap justify-end;
       shrink overrides the base .kx-actions shrink-0, else the cluster keeps its
       content width and overflows instead of wrapping).
       When everything fits, rendering is pixel-identical to the unwrapped version. */
    .kx-card-header { @apply flex flex-wrap items-center justify-between gap-3 mb-4; }
    .kx-card-header > .kx-actions { @apply ml-auto flex-wrap justify-end shrink; }
    .kx-card-title  { @apply text-xl font-bold; }
    .kx-card-body   { /* no extra styles by default */ }

    /* Action cluster */
    .kx-actions {
        @apply flex items-center shrink-0;
        gap: var(--kx-actions-gap);
    }

    /* Version-history diff highlights (Kaktus::HtmlDiff output) */
    .ins-block {
        @apply bg-success/20 px-0.5 rounded-sm;
        text-decoration: none;
    }
    .del-block {
        @apply bg-error/20 line-through px-0.5 rounded-sm;
    }
    /* Line-through doesn't render on images. Use a visible outline + tint instead. */
    .ins-block > img {
        @apply outline outline-2 outline-success rounded-sm;
    }
    .del-block > img {
        @apply outline outline-2 outline-error rounded-sm opacity-60 grayscale;
    }

    /* Search hits (Kaktus::SearchHelper#search_highlight) */
    .kx-search-mark {
        @apply bg-primary/15 text-inherit rounded-sm;
    }
}

/* ─── Rich-text editor (kaktus/ui/rich_text_editor + kaktus--tiptap-editor) ───
   Token defaults = TaskFlow's editor at extraction time (KAK-156); an app
   restyles through the tokens in its theme block. The surface's prose classes,
   the active button state and the error banner are set in JS, out of the views
   scan: safelisted here. docs/features/rich-text-editor.md */
@source inline("prose prose-sm max-w-none focus:outline-none btn-active alert alert-error alert-sm mb-2 text-sm");

@layer components {
    :root {
        --kx-rte-border:              1px solid var(--color-base-300);
        --kx-rte-bg:                  var(--color-base-100);
        --kx-rte-radius:              0.75rem;
        --kx-rte-padding:             0.75rem 1rem;
        --kx-rte-min-height-compact:  5rem;
        --kx-rte-min-height-full:     12rem;
        --kx-rte-toolbar-gap:         0.25rem;
        --kx-rte-toolbar-bg:          var(--color-base-200);
        --kx-rte-toolbar-padding:     0.5rem 0.75rem;
        --kx-rte-placeholder-color:   color-mix(in oklab, var(--color-base-content) 40%, transparent);
    }

    /* No overflow: hidden on the root: it would become the scroll container
       and kill the sticky toolbar of the :full variant. */
    .kx-rte {
        border: var(--kx-rte-border);
        background: var(--kx-rte-bg);
        border-radius: var(--kx-rte-radius);
    }
    .kx-rte-toolbar {
        display: inline-flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--kx-rte-toolbar-gap);
    }
    .kx-rte-toolbar-row {
        display: flex;
        padding: var(--kx-rte-toolbar-padding);
        border-bottom: var(--kx-rte-border);
        background: var(--kx-rte-toolbar-bg);
        border-radius: var(--kx-rte-radius) var(--kx-rte-radius) 0 0;
    }
    /* Sticks below the surrounding chrome; the ancestor (app <main>, drawer)
       sets --editor-sticky-top for its context. */
    .kx-rte-full .kx-rte-toolbar-row {
        position: sticky;
        top: var(--editor-sticky-top, 0);
        z-index: 10;
    }
    .kx-rte-surface { padding: var(--kx-rte-padding); }
    /* The height is reserved on the server-rendered container too: the
       surface only exists once the editor mounts (asynchronously), and the
       page below must not jump when it does. */
    .kx-rte-compact :is(.kx-rte-editor, .kx-rte-surface) { min-height: var(--kx-rte-min-height-compact); }
    .kx-rte-full :is(.kx-rte-editor, .kx-rte-surface)    { min-height: var(--kx-rte-min-height-full); }
}

/* Content rules: unlayered, as TaskFlow's were, so they win over the
   typography plugin's `.prose` rules whatever layer those land in. */
.kx-rte .is-empty::before {
    content: attr(data-placeholder);
    float: left;
    height: 0;
    color: var(--kx-rte-placeholder-color);
    pointer-events: none;
}
.kx-rte-surface img {
    max-width: 100%;
    @apply rounded-xl my-2;
}
.kx-rte-surface img.uploading {
    @apply opacity-50 animate-pulse;
}
/* TipTap wraps each list item's text in a <p>: without this a tight list
   reads as a loose one, unlike its rendered markdown. */
.kx-rte-surface li > p { margin: 0; }
.kx-rte-surface :is(p + ul, p + ol) { margin-top: 0.25em; }

/* ─── Auth layout (sign in / sign up / password reset) ───────────────────────
   The engine ships layouts/auth.html.erb (KAK-105): a panel that goes edge-to-edge
   on mobile (the panel IS the page — seamless full-bleed) and floats as a card from
   the sm breakpoint up, safe-area aware in the Hotwire Native shell.

   daisyUI sets NO background on <body>, so a single body.kaktus-auth rule wins the
   cascade with no !important. These defaults are LAYERED (@layer components), so a
   consumer app's own plain (unlayered) body.kaktus-auth / .kaktus-auth-panel rule
   always overrides them regardless of source order or specificity — that's how an
   app re-assigns the surface colours (or the font) without an !important fight.

   Default scheme = TaskFlow's: panel base-100; page base-200 on desktop and base-100
   on mobile (so the mobile page matches the panel and the full-bleed is seamless).
   An app wanting the inverse (e.g. CleverMontessori's cream panel on a white page)
   overrides these two surfaces in its own stylesheet. The layout itself carries NO
   colour — structure only — so the scheme stays app-driven and dark-mode-correct
   (the surfaces resolve through whatever the app's theme defines for base-100/200). */
@layer components {
    body.kaktus-auth {
        font-family: "Inter", sans-serif;
        background-color: var(--color-base-100); /* mobile: page == panel, seamless */
    }
    .kaktus-auth-panel {
        background-color: var(--color-base-100);
    }
    /* Desktop: the card floats on a distinct page surface. */
    @media (width >= 40rem) {
        body.kaktus-auth {
            background-color: var(--color-base-200);
        }
    }
}

/* daisyUI 5's .label is `inline-flex` + `white-space: nowrap`. Fine for the
   short field captions it was designed for, but every app also uses .label as
   the ROW WRAPPER of a checkbox/toggle/radio ("<label class=label><input…>
   <span>long text</span></label>") — and there a long libellé can never wrap,
   so it overflows the viewport on mobile. This has been re-fixed instance by
   instance across apps (TaskFlow TAS-200 prefix hint, the incubation checkbox,
   settings toggles…); fix the default once instead: labels wrap. Unlayered on
   purpose — daisyUI's rules live in @layer utilities/daisyui.*, so these win
   regardless of specificity. The second rule restores nowrap for the one place
   daisyUI NEEDS it: a .label rendered INSIDE an .input/.select wrapper
   (floating/inline field labels), which would otherwise grow the field. */
.label {
    white-space: normal;
}
.label:is(.input > *, .select > *) {
    white-space: nowrap;
}

/* Lock the page behind any open modal dialog (kaktus--dialog and any native
   <dialog> opened with showModal()). Without it, on iOS Safari a tap on the
   backdrop scrolls the page underneath instead of closing/acting on the modal,
   and the viewport visibly shifts. Modality-keyed (dialog:modal), so it covers
   every modal with no per-dialog coupling. Unlayered (behavioural, not a
   themeable surface). */
html:has(dialog:modal) {
    overflow: hidden;
}

/* Native shell (Hotwire Native): viewport-fit=cover makes the WKWebView edge-to-edge,
   so reserve the safe-area insets on the centering body. Android's WebView ignores
   env() (the inset is reserved natively at the activity root), so this reads 0 there
   — no double inset; iOS honours it with viewport-fit=cover. Unlayered (behavioural,
   not a themeable surface). Shared by the two chrome-free full-bleed gates: the
   auth pages and the minimal ToS gate (KAK-98). */
body.kaktus-auth.native-app,
body.kaktus-minimal.native-app {
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
}

/* Scroll-canvas alignment for base-200 layouts. daisyUI 5 sets --root-bg: base-100
   on :root and paints the html canvas (the beyond-document / overscroll region, and
   the home-indicator band in the Hotwire Native shell) with it. A layout whose body
   sits on base-200 flashes that white canvas below the document at scroll bottom
   (native shell + mobile Safari with the chrome collapsed) and on rubber-band
   overscroll. Mark its <html> with `canvas-base-200` to align the canvas: the
   `html.` specificity (0,1,1) beats daisyUI's layered :root (0,1,0) in both light
   and dark, base-200 resolving per theme. Layouts on a base-100 body stay unmarked
   and keep the daisyUI default. Unlayered (behavioural, not a themeable surface).
   Opt-in per layout — NOT theme-scoped — so base-100 layouts under the same theme
   are unaffected. See the hotwire-native skill, ios/chrome-and-rendering.md (KAK-98). */
html.canvas-base-200 {
    --root-bg: var(--color-base-200);
}

/* Rendered-markdown tables: keep a wide table's overflow local to itself.
   `Kaktus::Markdown.wrap_tables` (used by whatsnew_body_html and by each app's
   own render_markdown) wraps every rendered <table> in this container. Without
   it a table wider than the viewport widens the whole document and gives the
   PAGE a horizontal scroll — worst on a phone, where the fixed chrome slides
   along with it.

   The table keeps its NATURAL width, capped at the container (`width: auto` +
   `max-width: 100%`): a small table stays small instead of being stretched to
   full width, a wide one wraps its cell text and fits, and only a table that
   genuinely cannot fit (long unbreakable content, many columns) exceeds the cap
   and scrolls locally. Do NOT reintroduce `width: max-content` here: it forbids
   cell wrapping outright, so every table wider than the screen scrolls even
   when wrapping would have made it fit — the drift this rule replaced (KRO-144).

   Unlayered on purpose, twice over: it has to beat the typography plugin's own
   `.prose table` width, and it is behavioural, not a themeable surface. */
.kx-table-scroll {
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.prose .kx-table-scroll > table {
    width: auto;
    max-width: 100%;
}
