@tailwind base;
@tailwind components;
@tailwind utilities;

/* Colour names (PC-369). Every bf-* and semantic-* colour in
   config/tailwind.config.js reads one of these variables. :root holds the
   light values; html.dark holds the dark values from the approved palette
   "Bookfell Dark Palette" (Thomas, 2026-10-21). Space-separated RGB so
   Tailwind can add opacity (bg-bf-gold/30). Top level, outside any
   @layer: the built tailwind.css keeps both blocks.
   PC-374: after html.dark, html { color-scheme: light } states the light
   scheme wherever no dark rule applies, and the prefers-color-scheme block
   repeats html.dark's declarations for html.theme-system (Match my device,
   and signed-out pages outside /admin) on a dark device. Also top level;
   spec/config/dark_palette_spec.rb fails if the two copies differ. */
:root {
  --bf-cream:             250 247 242;  /* #FAF7F2 */
  --bf-surface:           255 255 255;  /* #FFFFFF */
  --bf-creamSoft:         244 239 229;  /* #F4EFE5 */
  --bf-creamWarm:         239 231 214;  /* #EFE7D6 */
  --bf-divider:           232 224 213;  /* #E8E0D5 */
  --bf-dividerDeep:       212 201 181;  /* #D4C9B5 */
  --bf-ink:               44 44 42;  /* #2C2C2A */
  --bf-inkSoft:           63 61 56;  /* #3F3D38 */
  --bf-muteDark:          75 85 99;  /* #4B5563 */
  --bf-mute:              107 114 128;  /* #6B7280 */
  --bf-faint:             156 163 175;  /* #9CA3AF */
  --bf-amber:             133 79 11;  /* #854F0B */
  --bf-amberHover:        162 98 17;  /* #A26211 */
  --bf-amberDeep:         107 63 9;  /* #6B3F09 */
  --bf-gold:              250 199 117;  /* #FAC775 */
  --bf-goldDeep:          228 169 61;  /* #E4A93D */
  --bf-onGold:            107 63 9;  /* #6B3F09 */
  --bf-goldTag:           254 238 214;  /* #FEEED6 */
  --bf-goldTagInk:        107 63 9;  /* #6B3F09 */
  --bf-brick:             121 37 7;  /* #792507 */
  --bf-brickDeep:         94 29 5;  /* #5E1D05 */
  --bf-brickInk:          121 37 7;  /* #792507 */
  --bf-onPhoto:           255 255 255;  /* #FFFFFF */
  --bf-red:               163 45 45;  /* #A32D2D */
  --bf-redFill:           163 45 45;  /* #A32D2D */
  --bf-green:             22 101 52;  /* #166534 */
  --semantic-successFg:   21 128 61;  /* #15803D */
  --semantic-successBg:   220 252 231;  /* #DCFCE7 */
  --semantic-warningFg:   146 64 14;  /* #92400E */
  --semantic-warningBg:   254 243 199;  /* #FEF3C7 */
  --semantic-dangerFg:    163 45 45;  /* #A32D2D */
  --semantic-dangerBg:    252 231 231;  /* #FCE7E7 */
  --semantic-infoFg:      30 64 175;  /* #1E40AF */
  --semantic-infoBg:      219 234 254;  /* #DBEAFE */
  --semantic-plumFg:      91 33 182;  /* #5B21B6 */
  --semantic-plumBg:      237 233 254;  /* #EDE9FE */
  --semantic-blushFg:     157 23 77;  /* #9D174D */
  --semantic-blushBg:     252 231 243;  /* #FCE7F3 */
}
html.dark {
  --bf-cream:             23 21 18;  /* #171512 */
  --bf-surface:           33 30 26;  /* #211E1A */
  --bf-creamSoft:         28 25 22;  /* #1C1916 */
  --bf-creamWarm:         44 39 33;  /* #2C2721 */
  --bf-divider:           58 52 44;  /* #3A342C */
  --bf-dividerDeep:       90 82 70;  /* #5A5246 */
  --bf-ink:               236 229 216;  /* #ECE5D8 */
  --bf-inkSoft:           214 205 191;  /* #D6CDBF */
  --bf-muteDark:          184 175 161;  /* #B8AFA1 */
  --bf-mute:              160 151 138;  /* #A0978A */
  --bf-faint:             133 125 113;  /* #857D71 */
  --bf-amber:             228 170 98;  /* #E4AA62 */
  --bf-amberHover:        239 190 126;  /* #EFBE7E */
  --bf-amberDeep:         217 155 78;  /* #D99B4E */
  --bf-gold:              250 199 117;  /* #FAC775 */
  --bf-goldDeep:          228 169 61;  /* #E4A93D */
  --bf-onGold:            107 63 9;  /* #6B3F09 */
  --bf-goldTag:           58 45 18;  /* #3A2D12 */
  --bf-goldTagInk:        244 199 106;  /* #F4C76A */
  --bf-brick:             176 74 43;  /* #B04A2B */
  --bf-brickDeep:         149 60 33;  /* #953C21 */
  --bf-brickInk:          224 135 106;  /* #E0876A */
  --bf-onPhoto:           255 255 255;  /* #FFFFFF */
  --bf-red:               240 144 138;  /* #F0908A */
  --bf-redFill:           184 64 59;  /* #B8403B */
  --bf-green:             130 212 157;  /* #82D49D */
  --semantic-successFg:   134 224 164;  /* #86E0A4 */
  --semantic-successBg:   23 48 32;  /* #173020 */
  --semantic-warningFg:   244 199 106;  /* #F4C76A */
  --semantic-warningBg:   58 45 18;  /* #3A2D12 */
  --semantic-dangerFg:    244 154 144;  /* #F49A90 */
  --semantic-dangerBg:    61 31 29;  /* #3D1F1D */
  --semantic-infoFg:      156 190 247;  /* #9CBEF7 */
  --semantic-infoBg:      23 40 67;  /* #172843 */
  --semantic-plumFg:      201 176 247;  /* #C9B0F7 */
  --semantic-plumBg:      42 33 66;  /* #2A2142 */
  --semantic-blushFg:     244 168 207;  /* #F4A8CF */
  --semantic-blushBg:     58 28 45;  /* #3A1C2D */
  color-scheme: dark;
}
html { color-scheme: light; }
@media (prefers-color-scheme: dark) {
  html.theme-system {
    --bf-cream:             23 21 18;  /* #171512 */
    --bf-surface:           33 30 26;  /* #211E1A */
    --bf-creamSoft:         28 25 22;  /* #1C1916 */
    --bf-creamWarm:         44 39 33;  /* #2C2721 */
    --bf-divider:           58 52 44;  /* #3A342C */
    --bf-dividerDeep:       90 82 70;  /* #5A5246 */
    --bf-ink:               236 229 216;  /* #ECE5D8 */
    --bf-inkSoft:           214 205 191;  /* #D6CDBF */
    --bf-muteDark:          184 175 161;  /* #B8AFA1 */
    --bf-mute:              160 151 138;  /* #A0978A */
    --bf-faint:             133 125 113;  /* #857D71 */
    --bf-amber:             228 170 98;  /* #E4AA62 */
    --bf-amberHover:        239 190 126;  /* #EFBE7E */
    --bf-amberDeep:         217 155 78;  /* #D99B4E */
    --bf-gold:              250 199 117;  /* #FAC775 */
    --bf-goldDeep:          228 169 61;  /* #E4A93D */
    --bf-onGold:            107 63 9;  /* #6B3F09 */
    --bf-goldTag:           58 45 18;  /* #3A2D12 */
    --bf-goldTagInk:        244 199 106;  /* #F4C76A */
    --bf-brick:             176 74 43;  /* #B04A2B */
    --bf-brickDeep:         149 60 33;  /* #953C21 */
    --bf-brickInk:          224 135 106;  /* #E0876A */
    --bf-onPhoto:           255 255 255;  /* #FFFFFF */
    --bf-red:               240 144 138;  /* #F0908A */
    --bf-redFill:           184 64 59;  /* #B8403B */
    --bf-green:             130 212 157;  /* #82D49D */
    --semantic-successFg:   134 224 164;  /* #86E0A4 */
    --semantic-successBg:   23 48 32;  /* #173020 */
    --semantic-warningFg:   244 199 106;  /* #F4C76A */
    --semantic-warningBg:   58 45 18;  /* #3A2D12 */
    --semantic-dangerFg:    244 154 144;  /* #F49A90 */
    --semantic-dangerBg:    61 31 29;  /* #3D1F1D */
    --semantic-infoFg:      156 190 247;  /* #9CBEF7 */
    --semantic-infoBg:      23 40 67;  /* #172843 */
    --semantic-plumFg:      201 176 247;  /* #C9B0F7 */
    --semantic-plumBg:      42 33 66;  /* #2A2142 */
    --semantic-blushFg:     244 168 207;  /* #F4A8CF */
    --semantic-blushBg:     58 28 45;  /* #3A1C2D */
    color-scheme: dark;
  }
}

@layer base {
  body {
    font-family: Inter, system-ui, sans-serif;
  }
  h1, h2, h3, h4, h5, h6 {
    font-family: 'Playfair Display', Georgia, serif;
  }
}

@layer utilities {
  .pt-safe { padding-top: env(safe-area-inset-top); }
  .pb-safe { padding-bottom: env(safe-area-inset-bottom); }
  .pl-safe { padding-left: env(safe-area-inset-left); }
  .pr-safe { padding-right: env(safe-area-inset-right); }
  /* Fixed elements that sit flush above the bottom tab bar: 56px is the
     tab link's min-height, the inset is the nav's pb-safe. */
  .bottom-above-tabs { bottom: calc(56px + env(safe-area-inset-bottom)); }
  /* The library + button: 88px is the 56px above plus a 32px gap, with
     the same inset added (PC-313). */
  .bottom-above-tabs-fab { bottom: calc(88px + env(safe-area-inset-bottom)); }
}

/* Select mode (PC-130). body[data-select-active] is set by
   library_select_controller while Select mode is on: the batch action
   bar replaces the bottom tab bar, row checkboxes are revealed, and
   selected rows get the amber left-border + tint treatment. The
   attribute is removed entirely when inactive. No slide animation —
   the bar appears/disappears instantly (prefers-reduced-motion safe). */
.bf-batch-action-bar { display: none; }
body[data-select-active] .bf-batch-action-bar { display: block; }
body[data-select-active] [data-test-id="bottom-tab-bar"] { display: none; }

.bf-select-checkbox { display: none; }
[data-select-active] .bf-select-checkbox { display: inline-block; }

/* PC-192: the mockup's amber check-square (library.jsx). A native
   <input type="checkbox"> restyled with appearance:none rather than a
   role=checkbox span — Playwright's .check() needs a real control, and the
   native element keeps the keyboard/AT model for free. :indeterminate is
   driven by the select-all box when only some of the page's rows are
   selected. */
.bf-select-checkbox {
  -webkit-appearance: none;
  appearance: none;
  border-radius: 6px;
  border: 2px solid rgb(var(--bf-mute));
  background: transparent;
  cursor: pointer;
}
.bf-select-checkbox:checked {
  border-color: rgb(var(--bf-amber));
  background-color: rgb(var(--bf-amber));
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
  background-size: 70% 70%;
  background-position: center;
  background-repeat: no-repeat;
}
.bf-select-checkbox:indeterminate {
  border-color: rgb(var(--bf-amber));
  background-color: rgb(var(--bf-amber));
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke='white' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M6 12h12'/%3E%3C/svg%3E");
  background-size: 70% 70%;
  background-position: center;
  background-repeat: no-repeat;
}
/* PC-371: in dark, an unchecked box's border is bf-faint, 4.09:1 on the
   dark bf-surface of the row list. */
html.dark .bf-select-checkbox:not(:checked):not(:indeterminate) {
  border-color: rgb(var(--bf-faint));
}
/* PC-371: the check and dash above are data URIs whose stroke is written
   out as text, not a colour variable, so dark mode repeats each URI with
   %23171512 (#171512, the html.dark value of --bf-cream). */
html.dark .bf-select-checkbox:checked {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23171512' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}
html.dark .bf-select-checkbox:indeterminate {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke='%23171512' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M6 12h12'/%3E%3C/svg%3E");
}

/* PC-192: the Select-all header row, revealed with the rest of Select mode.
   Mirrors .bf-batch-action-bar's hide/show pair. */
.bf-select-header { display: none; }
[data-select-active] .bf-select-header { display: flex; }

/* PC-192: the Select pill becomes "Done" in Select mode — amber outline per
   the mockup, and a CSS-only label swap so there is no JS text mutation for a
   Turbo snapshot to restore out of sync. */
[data-select-active] [data-test-id="library-select-pill"] {
  background-color: transparent;
  color: rgb(var(--bf-amber));
  border: 1px solid rgb(var(--bf-amber));
}
[data-select-active] [data-library-select-target="selectLabel"] { display: none; }
[data-library-select-target="doneLabel"] { display: none; }
[data-select-active] [data-library-select-target="doneLabel"] { display: inline; }

[data-select-active] [data-book-row][data-selected="true"] {
  border-left: 2px solid rgb(var(--bf-amber));
  background-color: rgb(var(--bf-amber) / 0.08);
}

/* PC-329: a run of bubbles from one person shows the time once, on its last
   bubble, and the other person's avatar once, beside it. A day divider
   (bf-run-break) or a card ends a run. */
.bf-run-them:has(+ .bf-run-them:not(.bf-run-break)) .bf-run-tail,
.bf-run-me:has(+ .bf-run-me:not(.bf-run-break)) .bf-run-tail { display: none; }
.bf-run-them:has(+ .bf-run-them:not(.bf-run-break)) .bf-run-avatar { visibility: hidden; }
/* PC-329: the Messages list's empty line gives way to the first row. */
.bf-rows-wrap:has([data-test-id="conversation-row"]) .bf-rows-empty { display: none; }
/* PC-329 Phase B: the thread's empty state gives way to the first item,
   including one appended live. */
.bf-thread-wrap:has(.bf-run-me, .bf-run-them, .bf-run-event) [data-test-id="chat-empty"] { display: none; }

@keyframes bf-sheet-slide-up {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}
@keyframes bf-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes bf-slide-in-from-right {
  from { transform: translateX(100%); }
  to   { transform: translateX(0); }
}

@media (prefers-reduced-motion: no-preference) {
  .bf-anim-sheet-slide-up      { animation: bf-sheet-slide-up 220ms cubic-bezier(0.32, 0.72, 0, 1); }
  .bf-anim-fade-in             { animation: bf-fade-in 160ms ease-out; }
  .bf-anim-slide-in-from-right { animation: bf-slide-in-from-right 220ms cubic-bezier(0.32, 0.72, 0, 1); }
}
@media (prefers-reduced-motion: reduce) {
  .bf-anim-sheet-slide-up,
  .bf-anim-fade-in,
  .bf-anim-slide-in-from-right { animation: bf-fade-in 120ms ease-out; }
}

/* PC-185 (folds in PC-152): barcode scanning is a phone feature (rear,
   autofocus camera). On pointer-fine devices the front webcam can't focus
   on a barcode, so hide every scan affordance and show an explanatory note.
   Plain classes so they always compile regardless of content scanning. */
.scan-affordance { display: none; }
.scan-desktop-note { display: block; }

@media (pointer: coarse) {
  .scan-affordance { display: flex; }
  .scan-desktop-note { display: none; }
}
