html {
    margin: 0px;
    height: 100%;
    width: 100%;
    overflow-y: auto;
    scroll-behavior: smooth;
}

body {
    margin: 0px;
    min-height: 100%;
    width: 100%;
}

.pointer {
    cursor: pointer;
}

/* Toast system */
.toast-card {
    will-change: transform, opacity;
    pointer-events: auto;
}

/* Toast level colors */
.toast-success {
    background-color: #f92672;
    color: white;
}
.toast-success .toast-progress {
    background-color: rgba(255, 255, 255, 0.4);
}

.toast-error {
    background-color: #dc2626;
    color: white;
}
.toast-error .toast-progress {
    background-color: rgba(255, 255, 255, 0.4);
}

.toast-warning {
    background-color: #f59e0b;
    color: white;
}
.toast-warning .toast-progress {
    background-color: rgba(255, 255, 255, 0.4);
}

.toast-info {
    background-color: white;
    color: #1f2937;
    border: 1px solid #e5e7eb;
}
.toast-info .toast-progress {
    background-color: rgba(249, 38, 114, 0.4);
}

/* Dark mode info toast */
[data-theme="dark"] .toast-info,
.dark .toast-info {
    background-color: #1d232a;
    color: #e5e7eb;
    border-color: #374151;
}

.modal-action > :not([hidden]) ~ :not([hidden]) {
    margin: 0px 0px !important;
}

.force-text-white {
    color: white !important;
}

.text-xp-pink {
    color: #f92672;
}

.bg-xp-pink {
    background-color: #f92672;
}

.btn.btn-bluesky {
    background-color: #0085ff;
    color: white;
    border: none;
    transition: background-color 150ms ease-out, transform 150ms ease-out, box-shadow 150ms ease-out;
}

.btn.btn-bluesky:hover {
    background-color: #0062cc;
    box-shadow: 0 2px 8px rgba(0, 133, 255, 0.3);
}

.btn.btn-bluesky:active {
    transform: scale(0.97);
}

.btn.btn-bluesky:focus-visible {
    outline: 2px solid #0085ff;
    outline-offset: 2px;
}

/* ==========================================================================
   AI-associated buttons — distinct violet color
   ==========================================================================
   Applied via the .ai-btn class so every button that triggers an AI action
   (auto-label, auto-generate, per-record linker) reads as AI work instead
   of blending into the pink xptracker primary theme. Ghost variants used in
   table rows keep their transparent background but colorize the icon. */

/* AI opt-out failsafe: server-side gates are the real boundary; this just
   hides any AI button a template guard missed. Body gets .ai-off from
   base.html when the user disabled AI features in account settings. */
.ai-off .ai-btn {
    display: none !important;
}

:root {
    --xp-ai: #8b5cf6;
    --xp-ai-hover: #7c3aed;
}

.btn.ai-btn:not(.btn-ghost) {
    background-color: transparent;
    color: var(--xp-ai);
    border-color: var(--xp-ai);
    transition: background-color 150ms ease-out, color 150ms ease-out,
                border-color 150ms ease-out, transform 150ms ease-out,
                box-shadow 150ms ease-out;
}

.btn.ai-btn:not(.btn-ghost):hover {
    background-color: var(--xp-ai);
    color: white;
    border-color: var(--xp-ai);
    box-shadow: 0 2px 8px rgba(139, 92, 246, 0.25);
}

.btn.ai-btn:not(.btn-ghost):active {
    background-color: var(--xp-ai-hover);
    border-color: var(--xp-ai-hover);
    transform: scale(0.98);
}

.btn.ai-btn:not(.btn-ghost):focus-visible {
    outline: 2px solid var(--xp-ai);
    outline-offset: 2px;
}

/* Ghost AI buttons (icon-only action-column buttons) — tint the icon and
   soften the hover wash to the same violet so they still read as AI. */
.btn.btn-ghost.ai-btn {
    color: var(--xp-ai);
}

.btn.btn-ghost.ai-btn:hover {
    background-color: rgba(139, 92, 246, 0.12);
    color: var(--xp-ai);
}

/* Icon color is driven by the button's current color so both hover and
   rest states stay in sync without extra rules. */
.ai-btn .ai-icon,
.ai-btn .ai-icon svg {
    color: inherit;
}

/* AI feedback cards — accent borders, header strip, and inline text all
   share the same violet so pending/success/header states read as AI. */
.ai-feedback-accent {
    border: 1px solid rgba(139, 92, 246, 0.4);
}

.ai-feedback-header {
    background-color: rgba(139, 92, 246, 0.1);
    border-bottom: 1px solid rgba(139, 92, 246, 0.2);
}

.ai-feedback-text {
    color: var(--xp-ai);
}

.ai-feedback-spinner {
    color: var(--xp-ai);
}

.ai-feedback-progress {
    background-color: var(--xp-ai);
    transition: width 300ms ease-out;
}

.file-input[data-theme="xptracker"]::file-selector-button {
    color: white;
}

.off-white {
    background-color: #fafafa;
}

@keyframes breathe {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.15); }
}

.animate-breathe {
    animation: breathe 3s ease-in-out infinite;
}

#parsing-overlay {
    display: none !important;
}

#parsing-overlay.htmx-request {
    display: flex !important;
    align-items: center;
    justify-content: center;
}

.no-padding {
    padding: 0px !important;
}

/* Collapsible sidebar nav groups: rotate the chevron when open */
details[data-nav-group][open] > summary .nav-group-chevron {
    transform: rotate(180deg);
}

/* When collapsed there's no child <ul> providing spacing — keep the
   group from sitting flush against the next section heading. */
details[data-nav-group]:not([open]) {
    margin-bottom: 0.875rem;
}

/* ==========================================================================
   Modal scroll body — pinned footer + inset scrollbar
   ==========================================================================
   Apply .modal-box-scroll to a DaisyUI .modal-box and wrap the scrollable
   region in .modal-scroll-body. The result:
   - The modal-box itself no longer scrolls (no scrollbar at the rounded edge).
   - The inner body gets a slim scrollbar that sits inside the modal padding.
   - .modal-action footers stay pinned and always visible while content scrolls.
*/

.modal-box.modal-box-scroll {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.modal-box.modal-box-scroll > .modal-action {
    flex-shrink: 0;
    margin-top: 0.75rem;
    padding-top: 0.75rem;
    border-top: 1px solid oklch(var(--bc) / 0.08);
}

.modal-scroll-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* Slim themed scrollbar (Firefox) */
    scrollbar-width: thin;
    scrollbar-color: oklch(var(--bc) / 0.22) transparent;
    /* Tiny right padding so content doesn't kiss the scrollbar */
    padding-right: 0.5rem;
    /* Room for input focus outlines (2px outline + 2px offset) — the
       overflow container otherwise clips the ring on inputs flush
       against its left/top edge. */
    padding-left: 4px;
    padding-top: 4px;
}

.modal-scroll-body::-webkit-scrollbar {
    width: 8px;
}

.modal-scroll-body::-webkit-scrollbar-track {
    background: transparent;
}

.modal-scroll-body::-webkit-scrollbar-thumb {
    background-color: oklch(var(--bc) / 0.22);
    border-radius: 4px;
    border: 2px solid transparent;
    background-clip: padding-box;
}

.modal-scroll-body::-webkit-scrollbar-thumb:hover {
    background-color: oklch(var(--bc) / 0.4);
    background-clip: padding-box;
}

/*
 * Variant: hide the scrollbar entirely while still pinning the footer.
 * Used by special modals that intentionally suppress the scrollbar
 * (fullscreen, bluesky share, journal new-entry). Deliberately does NOT
 * override padding-right here — compound-class specificity (0,0,2,0)
 * would beat Tailwind utilities like `p-5` (0,0,1,0) and clobber the
 * right padding asymmetrically (the fullscreen resume preview hit this:
 * content rendered flush against the right edge while keeping left
 * padding from p-5).
 */
.modal-scroll-body.scrollbar-hidden {
    scrollbar-width: none;
}

.modal-scroll-body.scrollbar-hidden::-webkit-scrollbar {
    width: 0;
    height: 0;
}

/* Variant: this modal-scroll-body holds an HTMX-swapped form whose Save
   button is hoisted into the modal's footer (see `data-modal-submit-trigger`
   in the modal-action). Hide the inline submit so the form has only one
   visible Save button. */
.modal-form-body input[type="submit"],
.modal-form-body button[type="submit"] {
    display: none !important;
}

/* ==========================================================================
   Quick-action: anchor hover affordance
   ========================================================================== */

.quickaction-anchor {
    position: relative;
    cursor: pointer;
    transition: transform 100ms ease-out;
}

/* No background-color highlights on hover/active/popup-open per design:
   the only feedback for clicking an item is the popup itself popping in
   and the brief press scale below. Earlier versions of this file applied
   pink-tinted backgrounds and a flash animation; that turned into noise
   on touch devices where :hover sticks until the next tap, so all the
   color states have been removed. Don't add them back without revisiting
   the touch-stickiness problem. */
.quickaction-anchor:active {
    transform: scale(0.985);
}

@media (prefers-reduced-motion: reduce) {
    .quickaction-anchor {
        transition: none;
    }
    .quickaction-anchor:active {
        transform: none;
    }
}

.quickaction-anchor:focus-visible {
    outline: 2px solid oklch(var(--p));
    outline-offset: 2px;
    border-radius: 6px;
}

/* The skill button in ats.html is itself a quick-action anchor. Strip
   default <button> chrome so it blends into the comma-separated list. */
button.quickaction-anchor {
    background-color: transparent;
    border: none;
    padding: 0 2px;
    color: inherit;
    font: inherit;
    text-align: inherit;
    cursor: pointer;
}

/* ==========================================================================
   Quick-action popup
   ========================================================================== */

.quickaction-popup {
    position: fixed;
    z-index: 60;
    min-width: 140px;
    max-width: 220px;
    padding: 4px;
    background-color: oklch(var(--b1));
    border: 1px solid oklch(var(--bc) / 0.08);
    border-radius: 8px;
    box-shadow:
        0 8px 24px -6px rgb(0 0 0 / 0.18),
        0 2px 6px -2px rgb(0 0 0 / 0.08);
    opacity: 0;
    transform: scale(0.92);
    transform-origin: top left;
    transition:
        opacity 160ms cubic-bezier(0.16, 1, 0.3, 1),
        transform 160ms cubic-bezier(0.16, 1, 0.3, 1);
    pointer-events: auto;
}

[data-theme="dark"] .quickaction-popup,
.dark .quickaction-popup {
    background-color: oklch(var(--b2));
    border-color: rgb(255 255 255 / 0.08);
    box-shadow:
        0 8px 24px -6px rgb(0 0 0 / 0.6),
        0 2px 6px -2px rgb(0 0 0 / 0.4),
        inset 0 1px 0 rgb(255 255 255 / 0.04);
}

.quickaction-popup.is-open {
    opacity: 1;
    transform: scale(1);
}

.quickaction-popup[hidden] {
    display: none;
}

.quickaction-btn {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 12px;
    background-color: transparent;
    border: none;
    border-radius: 6px;
    color: oklch(var(--bc));
    font-size: 0.875rem;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
    transition: background-color 100ms ease-out, color 100ms ease-out, transform 100ms ease-out;
}

.quickaction-btn[hidden] {
    display: none;
}

.quickaction-btn:hover {
    background-color: oklch(var(--b2));
}

[data-theme="dark"] .quickaction-btn:hover,
.dark .quickaction-btn:hover {
    background-color: oklch(var(--b3));
}

.quickaction-btn:active {
    background-color: oklch(var(--b3));
    transform: scale(0.98);
}

.quickaction-btn:focus-visible {
    outline: 2px solid oklch(var(--p));
    outline-offset: -2px;
}

.quickaction-btn-icon {
    flex-shrink: 0;
}

/* Hide button shows the eye-off icon by default; when in "show" mode (item
   already hidden), swap to the eye icon. */
.quickaction-btn-hide .quickaction-icon-show {
    display: none;
}

.quickaction-btn-hide.is-show-mode .quickaction-icon-hide {
    display: none;
}

.quickaction-btn-hide.is-show-mode .quickaction-icon-show {
    display: block;
}

.quickaction-btn-hide:hover {
    color: oklch(var(--wa));
}

.quickaction-btn-hide.is-show-mode:hover {
    color: oklch(var(--su));
}

@media (prefers-reduced-motion: reduce) {
    .quickaction-popup {
        transition: opacity 100ms linear;
        transform: none !important;
    }
    .quickaction-btn {
        transition: none;
    }
}

/* ==========================================================================
   Click-origin ripple
   ========================================================================== */

.quickaction-ripple {
    position: fixed;
    z-index: 59;
    width: 0;
    height: 0;
    border-radius: 50%;
    pointer-events: none;
    transform: translate(-50%, -50%);
    background: radial-gradient(circle, oklch(var(--p) / 0.4) 0%, oklch(var(--p) / 0) 70%);
    opacity: 0;
}

.quickaction-ripple[hidden] {
    display: none;
}

.quickaction-ripple.is-active {
    animation: quickaction-ripple-anim 420ms ease-out forwards;
}

@keyframes quickaction-ripple-anim {
    0% {
        width: 0;
        height: 0;
        opacity: 0.55;
    }
    100% {
        width: 120px;
        height: 120px;
        opacity: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .quickaction-ripple.is-active {
        animation: none;
    }
}

/* ==========================================================================
   First-visit coach mark
   ========================================================================== */

.quickaction-coach {
    position: absolute;
    z-index: 55;
    display: flex;
    align-items: center;
    gap: 10px;
    max-width: 280px;
    padding: 10px 12px 10px 14px;
    background-color: oklch(var(--n));
    color: oklch(var(--nc));
    border-radius: 8px;
    box-shadow: 0 8px 24px -6px rgb(0 0 0 / 0.35);
    font-size: 0.8125rem;
    line-height: 1.3;
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity 280ms ease-out, transform 280ms ease-out;
    pointer-events: auto;
}

.quickaction-coach[hidden] {
    display: none;
}

.quickaction-coach.is-visible {
    opacity: 1;
    transform: translateY(0);
}

.quickaction-coach-text {
    flex: 1 1 auto;
    font-weight: 500;
}

.quickaction-coach-dismiss {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    background-color: transparent;
    border: none;
    border-radius: 4px;
    color: oklch(var(--nc) / 0.7);
    cursor: pointer;
    transition: background-color 120ms ease-out, color 120ms ease-out;
}

.quickaction-coach-dismiss:hover {
    background-color: oklch(var(--nc) / 0.1);
    color: oklch(var(--nc));
}

.quickaction-coach-arrow {
    position: absolute;
    top: -6px;
    left: 24px;
    width: 12px;
    height: 12px;
    background-color: oklch(var(--n));
    transform: rotate(45deg);
    border-top-left-radius: 2px;
}

@media (prefers-reduced-motion: reduce) {
    .quickaction-coach {
        transition: opacity 200ms linear;
        transform: none;
    }
}

/* ==========================================================================
   Hidden-state redesign (hatching + badge)
   ========================================================================== */

.resume-item-hidden {
    opacity: 0.55;
    position: relative;
    transition: opacity 250ms ease-out;
}

.resume-item-hidden::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image: repeating-linear-gradient(
        135deg,
        transparent 0,
        transparent 8px,
        oklch(var(--bc) / 0.08) 8px,
        oklch(var(--bc) / 0.08) 9px
    );
    border-radius: inherit;
    pointer-events: none;
    transition: opacity 200ms ease-out;
    opacity: 1;
}

.resume-item-hidden:hover::before {
    opacity: 0.55;
}

/* HIDDEN badge — only on parent items (experience/project/achievement) where
   there's room. Smaller items (descriptors, skills) rely on the hatching alone
   to avoid overlapping the text. */
.resume-item-hidden[data-hide-type="experience"]::after,
.resume-item-hidden[data-hide-type="project"]::after,
.resume-item-hidden[data-hide-type="achievement"]::after {
    content: 'HIDDEN';
    position: absolute;
    top: 2px;
    right: 4px;
    padding: 2px 6px;
    background-color: oklch(var(--bc) / 0.12);
    color: oklch(var(--bc) / 0.65);
    border-radius: 4px;
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.06em;
    pointer-events: none;
    transform-origin: top right;
    animation: quickaction-badge-in 320ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes quickaction-badge-in {
    0% {
        opacity: 0;
        transform: scale(0.8);
    }
    100% {
        opacity: 1;
        transform: scale(1);
    }
}

@media (prefers-reduced-motion: reduce) {
    .resume-item-hidden,
    .resume-item-hidden::before,
    .resume-item-hidden::after {
        transition: none;
        animation: none;
    }
}

@media print {
    .resume-item-hidden {
        display: none !important;
    }
    /* Force light colors */
    * {
        color-scheme: light !important;
    }

    body, html {
        background: white !important;
        color: black !important;
    }

    /* Strip all layout padding/margin so resume fills the page */
    body {
        min-height: 0 !important;
    }

    main {
        margin: 0 !important;
        padding: 0 !important;
    }

    .min-h-\[70vh\] {
        min-height: 0 !important;
    }

    /* Remove card-body padding (DaisyUI) */
    .card-body {
        padding: 0 !important;
    }

    /* Remove resume container internal padding */
    #resume {
        padding: 0 !important;
    }

    /* Remove flex gap on dashboard layout */
    .flex.flex-col.gap-3 {
        gap: 0 !important;
        padding: 0 !important;
    }

    /* Clean up resume card */
    .shadow-md, .shadow-lg, .shadow-xl, .shadow-2xl {
        box-shadow: none !important;
    }

    .dark\:bg-base-100,
    .dark\:bg-slate-500,
    .dark\:bg-slate-600,
    .off-white {
        background-color: white !important;
    }

    .dark\:text-white,
    .dark\:text-gray-200,
    .dark\:text-gray-300 {
        color: black !important;
    }

    /* Remove max-width constraints for full-page printing */
    #public-resume,
    .max-w-\[924px\] {
        max-width: 100% !important;
        width: 100% !important;
    }

    /* Zero @page margin so browser suppresses its header/footer text,
       then use body padding for the actual print margins */
    @page {
        margin: 0;
    }

    body {
        padding: 0.4in !important;
    }

    /* Pull resume content up to eliminate residual top space */
    #resume {
        margin-top: -0.2in !important;
    }

    /* Prevent page breaks inside content blocks */
    .experience-block,
    .project-block,
    [data-section] {
        page-break-inside: avoid;
        break-inside: avoid;
    }
}
/* Descriptor modal two-column layout. Defined here rather than as a Tailwind
   arbitrary value because the grid class is emitted from Python
   (_common/forms/layout.py), which Tailwind's content scanner does not read.

   On desktop the two columns scroll independently: the modal body stops being
   the scroller (overflow hidden) and hands its height down through the form to
   the grid, whose columns each get their own overflow. Below lg it collapses to
   a single stacked, normally-scrolling column. */
.descriptor-grid {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}
@media (min-width: 1024px) {
    .descriptor-scroll {
        overflow: hidden;
        display: flex;
        flex-direction: column;
    }
    .descriptor-scroll > [id$="ModalBody"],
    .descriptor-scroll [id$="ModalBody"] > form {
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
    }
    .descriptor-grid {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 20rem;
        grid-template-rows: minmax(0, 1fr);
        flex: 1 1 auto;
        min-height: 0;
    }
    .descriptor-grid > * {
        min-height: 0;
        overflow-y: auto;
        padding-right: 0.25rem;
    }
}

/* Recent-notes sticky notes on the home page. Colors + rotation cycle via
   nth-child so no per-note Tailwind classes are needed (and arbitrary values
   emitted from templates would compile, but keeping the palette here keeps it
   in one place). Dark ink stays legible on every pastel in light or dark mode. */
.sticky-note {
    position: relative;
    /* Fixed height so a tall note never stretches the rest of its grid row. */
    height: 11rem;
    display: flex;
    flex-direction: column;
    padding: 0.7rem 0.8rem;
    border-radius: 2px;
    color: #38352b;
    cursor: pointer;
    overflow: hidden;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.14);
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.sticky-note:focus-visible {
    outline: 2px solid #f92672;
    outline-offset: 2px;
}
/* Body grows, clips at the card edge; the date footer stays pinned. */
.sticky-note-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    position: relative;
}
/* Fade the clipped bottom edge so cut-off content reads as intentional. */
.sticky-note-body::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1.25rem;
    background: linear-gradient(to bottom, transparent, var(--sticky-bg, transparent));
}
.sticky-note-date {
    flex-shrink: 0;
    margin-top: 0.35rem;
}
/* Keep markdown ink dark on the pastel paper (even in dark mode) and force
   compact, uniform typography so a `# heading` can't balloon a card. Code
   blocks are deliberately excluded — prose keeps their light-on-dark styling,
   which the earlier blanket override was breaking. */
.sticky-note .prose,
.sticky-note .prose :where(p, li, h1, h2, h3, h4, strong, em, a),
#noteViewModal .prose,
#noteViewModal .prose :where(p, li, h1, h2, h3, h4, strong, em, a) {
    color: #38352b;
}

/* The note modal is post-it paper too: same pastel, square corners, dark
   ink in both light and dark mode. */
#noteViewModal .modal-box {
    background: #fff6b8;
    color: #38352b;
    border-radius: 2px;
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.28);
}
#noteViewModal .note-click-edit {
    cursor: text;
}
#noteViewModal .note-click-edit:focus-visible {
    outline: 2px solid #f92672;
    outline-offset: 2px;
}
/* Inline editor swapped in when the note text is clicked: bare textarea on
   the paper, saves on change (blur) and swaps back to rendered prose. */
.sticky-note-editor {
    width: 100%;
    min-height: 10rem;
    max-height: 60vh;
    background: transparent;
    border: none;
    resize: vertical;
    font-size: 0.875rem;
    line-height: 1.5;
    color: #38352b;
}
.sticky-note-editor:focus {
    outline: 1px dashed rgba(56, 53, 43, 0.45);
    outline-offset: 4px;
}
/* Note-modal delete: a native <details> as the confirm step (no JS, no
   window.confirm). Closed shows "Delete"; open reveals the inline
   confirmation row and the summary flips to "Cancel". */
.note-delete {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.note-delete summary {
    list-style: none;
}
.note-delete summary::-webkit-details-marker {
    display: none;
}
.note-delete .note-delete-cancel,
.note-delete[open] .note-delete-label {
    display: none;
}
.note-delete[open] .note-delete-cancel {
    display: inline;
}
.sticky-note .prose :where(h1, h2, h3, h4) {
    font-size: 0.85rem;
    font-weight: 700;
    line-height: 1.25;
    margin: 0.15rem 0;
}
.sticky-note .prose :where(p, ul, ol, pre) {
    margin: 0.2rem 0;
}
.sticky-note .prose pre {
    font-size: 0.7rem;
    padding: 0.4rem 0.5rem;
}

/* Copy-to-clipboard button injected into note-modal code blocks by
   code-copy.js. */
.code-copy-pre {
    position: relative;
}
.code-copy-btn {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    font-size: 0.65rem;
    line-height: 1;
    padding: 0.25rem 0.45rem;
    border-radius: 0.25rem;
    background: rgba(255, 255, 255, 0.14);
    color: #e5e7eb;
    border: 1px solid rgba(255, 255, 255, 0.22);
    cursor: pointer;
    transition: background 0.12s ease;
}
.code-copy-btn:hover {
    background: rgba(255, 255, 255, 0.28);
}
.code-copy-btn:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 1px;
}
.sticky-note:nth-child(4n+1) { --sticky-bg: #fff6b8; background: #fff6b8; transform: rotate(-1.5deg); }
.sticky-note:nth-child(4n+2) { --sticky-bg: #ffdce3; background: #ffdce3; transform: rotate(1deg); }
.sticky-note:nth-child(4n+3) { --sticky-bg: #d4f0cf; background: #d4f0cf; transform: rotate(-0.75deg); }
.sticky-note:nth-child(4n+4) { --sticky-bg: #cfe6ff; background: #cfe6ff; transform: rotate(1.5deg); }
.sticky-note:hover,
.sticky-note:focus-visible {
    transform: rotate(0deg) translateY(-2px);
    box-shadow: 0 6px 14px rgba(0, 0, 0, 0.18);
    z-index: 1;
}
/* Dark mode: tone the pastels down so the paper doesn't glare against the
   dark background. One filter dims paper and ink together, keeping the
   nth-child palette as the single source of truth. Media query (not
   [data-theme]) because tailwind darkMode is 'media' — DaisyUI switches to
   the dark theme on prefers-color-scheme, nothing sets a data-theme attr. */
@media (prefers-color-scheme: dark) {
    .sticky-note,
    #noteViewModal .modal-box {
        filter: brightness(0.8) saturate(0.85);
    }
}

/* Home-page todo chips: same paper feel as .sticky-note but a neutral
   index-card white, deliberately NOT the pastel notes palette — pastels
   mean "note", neutral means "todo". Compact rows with tight padding so
   the title gets the width; the tilt is smaller than the notes' because
   a full-width row at 1.5deg reads as broken, not playful. */
.todo-card {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.55rem;
    border-radius: 2px;
    background: #faf8f2;
    color: #38352b;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.14);
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.todo-card:nth-child(odd) { transform: rotate(-0.4deg); }
.todo-card:nth-child(even) { transform: rotate(0.4deg); }
.todo-card:hover {
    transform: rotate(0deg);
    box-shadow: 0 3px 8px rgba(0, 0, 0, 0.18);
}
/* Completed: gray paper, muted ink, no tilt. Declared after the tilt
   rules so equal specificity resolves in its favor. */
.todo-card.todo-done,
.todo-card.todo-done:hover {
    background: #e4e3df;
    color: #8d8b82;
    box-shadow: none;
    transform: none;
}
/* Dark mode gets its own chip palette instead of the notes' dimming
   filter — dimmed off-white reads as flat gray and erases the
   pending/done contrast. Pending stays a step lighter than the page,
   done sinks back toward it. */
@media (prefers-color-scheme: dark) {
    .todo-card {
        background: #343941;
        color: #ddd9cf;
        box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
    }
    .todo-card:hover {
        box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5);
    }
    .todo-card.todo-done,
    .todo-card.todo-done:hover {
        background: #262a30;
        color: #75736c;
        box-shadow: none;
    }
}
