
/* Image picker cache-hit banner — shown when the result came from
   localStorage rather than the worker. Reassures the user that nothing
   is being re-fetched. Added 2026-05-01 with image cache. */
.image-picker-cache-hint{padding:6px 10px;margin:0 0 8px;border-radius:6px;
  background:rgba(232,168,56,0.12);color:var(--amber-dark);font-size:0.75rem;
  font-weight:600;display:inline-block}

/* Tile Grid */
.tile-grid{display:grid;gap:8px;padding:12px;min-height:350px;transition:all 0.3s}
/* cols-1 through cols-6 (Task #70). Settings dropdown exposes the
   full 1–6 range; quick toolbar still only has 2/3/4 for one-click
   common cases. 1-col is for visual neglect / single-switch scanning;
   5/6-col is for tablet-mounted users with high cognition + small
   hand-target needs. */
.tile-grid.cols-1{grid-template-columns:repeat(1,1fr)}
.tile-grid.cols-2{grid-template-columns:repeat(2,1fr)}
.tile-grid.cols-3{grid-template-columns:repeat(3,1fr)}
.tile-grid.cols-4{grid-template-columns:repeat(4,1fr)}
.tile-grid.cols-5{grid-template-columns:repeat(5,1fr)}
.tile-grid.cols-6{grid-template-columns:repeat(6,1fr)}
/* 2026-07-01 (Luke): tile-count control offers up to 10 across. */
.tile-grid.cols-7{grid-template-columns:repeat(7,1fr)}
.tile-grid.cols-8{grid-template-columns:repeat(8,1fr)}
.tile-grid.cols-9{grid-template-columns:repeat(9,1fr)}
.tile-grid.cols-10{grid-template-columns:repeat(10,1fr)}

/* Tiles — matches Android TinkySpeak app rendering */
.tile{border-radius:var(--radius-md);text-align:center;cursor:grab;
  display:flex;flex-direction:column;align-items:stretch;justify-content:stretch;
  border:2px solid transparent;transition:all var(--transition);position:relative;
  min-height:100px;user-select:none;-webkit-user-select:none;overflow:hidden}
.tile:hover{transform:translateY(-2px);box-shadow:var(--shadow-md);border-color:rgba(0,0,0,0.08)}
.tile.selected{border-color:var(--amber);box-shadow:0 0 0 3px rgba(232,168,56,0.2)}
.tile.dragging{opacity:0.5;transform:scale(0.95)}
.tile-emoji-area{flex:1;display:flex;align-items:center;justify-content:center;padding:0.4rem;min-height:60px;position:relative;container-type:size}
/* 2026-06-19 (Luke): emoji were tiny — a fixed 2.2rem floated in big tiles
   while the runtime view used 4rem+. Scale the glyph to the tile via a
   container query (cqmin = % of the cell's smaller side) so a big tile gets
   a big emoji and a small tile stays proportional. rem floor/ceiling keep it
   sane where container queries aren't supported. */
/* --emoji-scale (default 1) is set inline by the renderer from tile.emojiSize
   so the per-tile Emoji Size slider actually resizes the glyph. font-size
   reliably scales; the old transform:scale was dropped on this span. (2026-06-21) */
.tile-emoji{font-size:calc(clamp(2.2rem, 52cqmin, 4.4rem) * var(--emoji-scale, 1));line-height:1}
/* Symbol-library images get the same generous fill (was capped tight).
   --emoji-scale (the Icon Size slider, set inline by the renderer) scales the
   PICTURE too, not just emoji — Luke 2026-06-21: "make the tile images grow
   bigger or smaller". A full-image tile (.tile--full-image) ignores this and
   covers the whole tile. */
.tile-emoji-area .tile-image{
  max-width:calc(92% * var(--emoji-scale, 1));
  max-height:calc(92% * var(--emoji-scale, 1));
}
/* Wave 16.B.17 (2026-05-11) — Luke flagged the symbol-library
   images were covering the whole tile and hiding the label.
   Root cause: `position:absolute;inset:0` + `object-fit:cover`
   stretched + cropped every image edge-to-edge over the label bar.
   New rule: image lives INSIDE the .tile-emoji-area (in the flow),
   not absolutely positioned over the tile. `object-fit:contain`
   keeps aspect ratio so library art doesn't get distorted. Caps
   at 100% of the area so the label bar always stays visible. */
.tile-image{
  max-width:100%;max-height:100%;
  width:auto;height:auto;
  object-fit:contain;
  display:block;
  /* Above the emoji fallback (which sits at z-index:0 in the
     same flex cell) but below the badge / delete button. */
  position:relative;z-index:1;
}
/* 2026-06-08 — PHOTO tiles (Pixabay/Google) fill the cell with a square
   crop instead of letterboxing. Real photos look broken with `contain`
   (whitespace bars); pictograms/emoji KEEP `contain` — this only targets
   the .tile-image--photo modifier the renderer adds for photo providers.
   Stays inside .tile-emoji-area so the label bar is never covered (the
   Wave 16.B.17 `cover` bug was absolute-positioning over the whole tile,
   which we do NOT do here). */
.tile-image.tile-image--photo{
  width:100%;height:100%;
  object-fit:cover;
  border-radius:6px;
}
/* ── FULL-BLEED IMAGE (Luke 2026-06-20: "I need an option to become the full
   tile — the image wraps around the whole tile, not a small image inside").
   The photo fills the ENTIRE tile; the label bar is overlaid as a caption strip
   at the bottom so the word stays readable (AAC-safe — this is the deliberate
   difference from the Wave 16.B.17 bug, which REMOVED the label). Opt-in per
   tile via the .tile--full-image class (set by the renderers when
   tile.imageFill is on). The emoji-area drops position:relative so the image's
   absolute box resolves to the .tile (which is position:relative;overflow:hidden
   with rounded corners → the photo wraps the tile cleanly). */
.tile--full-image .tile-emoji-area{ position:static; padding:0; }
.tile--full-image .tile-image{
  position:absolute; inset:0; z-index:0;
  width:100%; height:100%; max-width:none; max-height:none;
  object-fit:cover; border-radius:0; padding:0; background:transparent;
}
.tile--full-image .tile-label-bar{
  position:absolute; left:0; right:0; bottom:0; z-index:2;
  background:rgba(0,0,0,0.5);
}
/* White SYMBOL CARD (Luke 2026-06-09) — OPT-IN, applied ONLY to line-art
   pictogram LIBRARIES (the render adds .tile-image--symbol when imageProvider is
   arasaac/mulberry/sclera/opensymbols/openmoji). They're drawn FOR a white
   background; without a card they float as pale figures on the coloured tile
   ("our logo is fucked up"). Photos / kept-original crops stay PLAIN .tile-image
   (no card, no change) so a photo is never boxed (Codex 2026-06-09). */
.tile-image--symbol{ background:#fff;border-radius:10px;padding:5px;box-sizing:border-box; }
.tile-image-badge{position:absolute;bottom:2px;right:2px;width:20px;height:20px;border-radius:50%;
  background:rgba(0,0,0,0.45);color:#fff;font-size:10px;display:flex;align-items:center;
  justify-content:center;cursor:pointer;opacity:0;transition:opacity 0.15s;z-index:2}
.tile:hover .tile-image-badge{opacity:1}
.tile-image-badge:hover{background:rgba(0,0,0,0.7)}
.tile-label-bar{padding:0.35rem 0.4rem;text-align:center;
  background:rgba(0,0,0,0.25);backdrop-filter:blur(2px)}
.tile-label{font-size:0.7rem;font-weight:800;color:#fff;line-height:1.15;word-break:break-word;
  text-transform:uppercase;letter-spacing:0.03em;text-shadow:0 1px 2px rgba(0,0,0,0.3)}
.tile-delete{position:absolute;top:4px;right:4px;width:20px;height:20px;border-radius:50%;
  background:rgba(0,0,0,0.3);color:#fff;font-size:0.65rem;display:flex;align-items:center;
  justify-content:center;opacity:0;transition:opacity var(--transition);z-index:2}
.tile:hover .tile-delete{opacity:1}
.tile-delete:hover{background:var(--fk-neg)}

/* Fitzgerald colors on tiles — vivid backgrounds like Android app.
   This is the DEFAULT "Fill" tile style. The two alternate styles
   (Outline / Light Fill) are opt-in via a .tile-grid modifier class
   set by tile-style-picker.js — mirrors TinkyTown's multi-style system
   so authors pick a look that suits their user (low-vision = outline
   high-contrast; sensory-sensitive = light fill). */
.tile-verb{background:var(--fk-verb)}
.tile-noun{background:var(--fk-noun)}
.tile-adj{background:var(--fk-adj)}
.tile-social{background:var(--fk-social)}
.tile-question{background:var(--fk-question)}
.tile-misc{background:#B8A9C9}
.tile-neg{background:var(--fk-neg)}

/* ─── Per-tile EXPLICIT color (AI palette / manual 🎨 swatch) ─────────
   Renderers (shared/tile-fill.js) set inline --tile-color (6-hex fill)
   and --tile-ink (a WCAG-AA black/white label color chosen for the bar).
   Applied ONLY in the default Fill mode — scoped with
   :not(.ts-outline):not(.ts-lightfill) so the Outline / Light Fill
   accessibility modes keep their muted look. (An earlier opaque inline
   background beat those modes' non-!important label rules and produced
   dark-on-dark labels — Codex review 2026-06-21.) The label bar darkens
   the same 28% the ink was computed against, for a deliberate two-tone. */
/* :not(.tile--full-image) — a full-bleed PHOTO tile keeps its translucent
   black caption scrim (rule ~line 84) so the photo stays visible and the
   white caption AAC-readable; an opaque per-tile color bar would defeat
   that (Codex r2). The 28% darken matches the device's rgba(0,0,0,0.28)
   label scrim (use.html) for builder↔device two-tone parity. */
.tile-grid:not(.ts-outline):not(.ts-lightfill) .tile[style*="--tile-color"]:not(.tile--full-image){
  background:var(--tile-color) !important;
}
.tile-grid:not(.ts-outline):not(.ts-lightfill) .tile[style*="--tile-color"]:not(.tile--full-image) .tile-label-bar{
  background:color-mix(in srgb, var(--tile-color) 72%, #000) !important;
}
.tile-grid:not(.ts-outline):not(.ts-lightfill) .tile[style*="--tile-ink"]:not(.tile--full-image) .tile-label{
  color:var(--tile-ink) !important;
  text-shadow:none;
}

/* ─── Tile-style: OUTLINE ────────────────────────────────────────────
   White tile, category color traces the border + label text. No dark
   label bar. Highest contrast against the light canvas. Category
   classes above are specificity (0,1,0); these (0,2,0) win without
   !important on border/label (bg uses !important to beat the class bg). */
.tile-grid.ts-outline .tile{background:#fff !important;border:2px solid #CBD5E1}
.tile-grid.ts-outline .tile-verb{border-color:var(--fk-verb)}
.tile-grid.ts-outline .tile-noun{border-color:var(--fk-noun)}
.tile-grid.ts-outline .tile-adj{border-color:var(--fk-adj)}
.tile-grid.ts-outline .tile-social{border-color:var(--fk-social)}
.tile-grid.ts-outline .tile-question{border-color:var(--fk-question)}
.tile-grid.ts-outline .tile-misc{border-color:#B8A9C9}
.tile-grid.ts-outline .tile-neg{border-color:var(--fk-neg)}
.tile-grid.ts-outline .tile-label-bar{background:transparent;backdrop-filter:none}
.tile-grid.ts-outline .tile-label{color:#1A1A2E;text-shadow:none}

/* ─── Tile-style: LIGHT FILL ─────────────────────────────────────────
   Soft pastel tint (the --fk-*-bg vars) + thin colored border + dark
   label. Lower visual intensity for sensory-sensitive users while
   keeping the category color cue. */
.tile-grid.ts-lightfill .tile{border:1px solid #E2E8F0}
.tile-grid.ts-lightfill .tile-verb{background:var(--fk-verb-bg) !important;border-color:var(--fk-verb)}
.tile-grid.ts-lightfill .tile-noun{background:var(--fk-noun-bg) !important;border-color:var(--fk-noun)}
.tile-grid.ts-lightfill .tile-adj{background:var(--fk-adj-bg) !important;border-color:var(--fk-adj)}
.tile-grid.ts-lightfill .tile-social{background:var(--fk-social-bg) !important;border-color:var(--fk-social)}
.tile-grid.ts-lightfill .tile-question{background:var(--fk-question-bg) !important;border-color:var(--fk-question)}
.tile-grid.ts-lightfill .tile-misc{background:var(--fk-misc-bg) !important;border-color:#B8A9C9}
.tile-grid.ts-lightfill .tile-neg{background:var(--fk-neg-bg) !important;border-color:var(--fk-neg)}
.tile-grid.ts-lightfill .tile-label-bar{background:rgba(0,0,0,0.06);backdrop-filter:none}
.tile-grid.ts-lightfill .tile-label{color:#1A1A2E;text-shadow:none}

/* Drop zone indicator */
.tile-drop-zone{border:2px dashed var(--amber);border-radius:var(--radius-md);
  min-height:90px;display:flex;align-items:center;justify-content:center;
  color:var(--amber);font-size:0.75rem;font-weight:600;opacity:0;transition:opacity 0.2s}
.tile-drop-zone.visible{opacity:1}

/* Add Tile Button */
.add-tile-btn{border:2px dashed var(--border);border-radius:var(--radius-md);min-height:90px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0.3rem;
  color:var(--ink-muted);transition:all var(--transition);cursor:pointer;font-size:0.75rem;font-weight:600;
  position:relative}
.add-tile-btn:hover{border-color:var(--amber);color:var(--amber);background:rgba(232,168,56,0.04)}
.add-tile-btn .plus{font-size:1.5rem;line-height:1}
/* Per-cell add affordance (2026-06-22, Luke): a "+" that fills each empty
   cell of a partial row so there's always an obvious "add here". Subtler
   than the labeled trailing button (no text, lighter dashed border) so a
   row of them reads as quiet placeholders, not a wall of CTAs. Square-ish
   via the same min-height as tiles so the grid stays even. Builder-only —
   the runtime hides .add-tile-btn entirely. */
.add-tile-cell{min-height:90px;padding:0;border-color:rgba(0,0,0,0.14);
  background:rgba(0,0,0,0.015);color:var(--ink-muted)}
.add-tile-cell .plus{font-size:1.6rem;opacity:0.7}
.add-tile-cell:hover .plus{opacity:1}
/* ▾ split-button caret — the main button adds a tile in ONE tap (Luke
   2026-06-21: "Add Tile shouldn't make me press it twice"); this caret opens
   the full AddMenu (Folder / Action / Sub-board) so those stay reachable.
   Kept ALONGSIDE .add-tile-cell (2026-06-24 reconcile): empty cells fill the
   gaps, the caret enriches the trailing button — different spots. */
.add-tile-more{position:absolute;top:5px;right:5px;width:24px;height:24px;border:none;
  background:rgba(0,0,0,0.06);color:var(--ink-muted);border-radius:6px;font-size:0.8rem;line-height:1;
  cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0}
.add-tile-more:hover{background:var(--amber);color:#fff}
