import { createVar, globalStyle, keyframes, style, styleVariants } from '@vanilla-extract/css';
import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
import { DefaultReset, color, config, toRem } from 'folds';

const SpacingVar = createVar();
const SpacingVariant = styleVariants({
  '0': {
    vars: {
      [SpacingVar]: config.space.S0,
    },
  },
  '100': {
    vars: {
      [SpacingVar]: config.space.S100,
    },
  },
  '200': {
    vars: {
      [SpacingVar]: config.space.S200,
    },
  },
  '300': {
    vars: {
      [SpacingVar]: config.space.S300,
    },
  },
  '400': {
    vars: {
      [SpacingVar]: config.space.S400,
    },
  },
  '500': {
    vars: {
      [SpacingVar]: config.space.S500,
    },
  },
});

const highlightAnime = keyframes({
  '0%': {
    backgroundColor: color.Primary.Container,
  },
  '25%': {
    backgroundColor: color.Primary.ContainerActive,
  },
  '50%': {
    backgroundColor: color.Primary.Container,
  },
  '75%': {
    backgroundColor: color.Primary.ContainerActive,
  },
  '100%': {
    backgroundColor: color.Primary.Container,
  },
});
const HighlightVariant = styleVariants({
  true: {
    animation: `${highlightAnime} 2000ms ease-in-out`,
    animationIterationCount: 'infinite',
  },
});

const SelectedVariant = styleVariants({
  true: {
    backgroundColor: color.Surface.ContainerActive,
  },
});

const AutoCollapse = style({
  selectors: {
    [`&+&`]: {
      marginTop: 0,
    },
  },
});

export const MessageBase = recipe({
  base: [
    DefaultReset,
    {
      marginTop: SpacingVar,
      padding: `${config.space.S100} ${config.space.S200} ${config.space.S100} ${config.space.S400}`,
      borderRadius: `0 ${config.radii.R400} ${config.radii.R400} 0`,
    },
  ],
  variants: {
    space: SpacingVariant,
    collapse: {
      true: {
        marginTop: 0,
      },
    },
    autoCollapse: {
      true: AutoCollapse,
    },
    highlight: HighlightVariant,
    selected: SelectedVariant,
    // Bubble (1:1 DM) rows carry NO horizontal row gutter: the centred timeline
    // band (BubbleTimelineBand) owns the edge inset, so messages line up at the
    // exact same 12px/40px gutter as the AI-bot chat. The base S400/S200 padding
    // is the channel/stream layout's avatar + options-bar column only.
    bubble: {
      true: {
        paddingLeft: 0,
        paddingRight: 0,
      },
    },
  },
  defaultVariants: {
    space: '400',
  },
});

export type MessageBaseVariants = RecipeVariants<typeof MessageBase>;

export const AvatarBase = style({
  paddingTop: toRem(4),
  transition: 'transform 200ms cubic-bezier(0, 0.8, 0.67, 0.97)',
  display: 'flex',
  alignSelf: 'start',

  selectors: {
    '&:hover': {
      transform: `translateY(${toRem(-2)})`,
    },
  },
});

export const ModernBefore = style({
  minWidth: toRem(36),
});

export const Username = style({
  overflow: 'hidden',
  whiteSpace: 'nowrap',
  textOverflow: 'ellipsis',
  selectors: {
    'button&': {
      cursor: 'pointer',
    },
    'button&:hover, button&:focus-visible': {
      textDecoration: 'underline',
    },
  },
});

export const UsernameBold = style({
  fontWeight: 550,
});

// Stream layout (DM «VS Code chat» redesign — see
// docs/plans/dm_stream_vscode_redesign.md).
//
// Rail-first layout, expressed as a 2-track CSS grid (the rail + dot hug
// the screen edge; the nick and timestamp share the header line):
//
//   ┌─G─┬─dot─┬─G─┬───── content (1fr) ────────────────────┐
//   row      │            │ nick · time   ← header line     row
//   left     rail         │ bubble / text                  right
//
// where G = `column-gap` = `padding-left` of the row root. Track 1 is the
// dot column (sized to the dot diameter), anchored `G` off the screen edge
// so the rail reads as a margin-rail down the left side. Track 2 (`1fr`) is
// the content column: the header line (`nick` then a muted `time`) followed
// by the bubble / plain text. The timestamp is no longer its own grid track
// — it rides the header line to the right of the nick, so there's no
// locale-width auto-track to keep in sync any more.
//
// Two gap dimensions, set per-breakpoint via the `compact` recipe
// variant on the row roots:
//
//   - StreamRowPadVar  → screen→dot gap (the row's `padding-left`)
//   - StreamRowGapVar  → dot→content gap (the `column-gap`)
//
// Splitting these lets the dot→content gap tighten on desktop and loosen
// on mobile without disturbing the screen-edge anchor.
//
// Mobile: pad = S200 (8px off the screen edge); gap = S200.
// Desktop: pad = S500 (20px — clears the PageNav rail); gap = S500 / 1.1 ≈
// 18.2 px (desktop dot→content gap shrunk by 1.1× to keep the layout tight
// without dropping a whole token).
const StreamRowPadVar = createVar();
const StreamRowGapVar = createVar();
const StreamRowPadMobile = config.space.S200;
const StreamRowPadDesktop = config.space.S500;
const StreamRowGapMobile = config.space.S200;
const StreamRowGapDesktop = `calc(${config.space.S500} / 1.1)`;

const StreamDotSize = toRem(9);
const StreamSyslineDotSize = toRem(6);
// Slightly larger than message dots so day markers read as the visual
// anchor of the `─── Сегодня ───` line.
const StreamDayDotSize = toRem(12.1);
const StreamRailLineWidth = '1px';
const StreamBubbleBorderWidth = '1px';
// Minimal, all-corners rounding for the incoming bubble + the peer name chip
// (user request — like the VS Code reference, no sharp/notched corner).
const StreamBubbleRadius = toRem(6);
const StreamTimeLineHeight = toRem(13);
const StreamRailBridgeY = config.space.S400;

// Author name (header line) — DM «VS Code chat» redesign
// (docs/plans/dm_stream_vscode_redesign.md). Bold, pure white/black, a step
// larger than chat body (T400 = 15px). The dot + timestamp vertically centre
// on this line, so its line-height drives the rail geometry below.
const StreamNameFontSize = toRem(16);
const StreamNameLineHeight = toRem(20);

// Vertical centre of the author-name line, measured from the row's
// content-area top. The name is now the FIRST child of StreamColumn (track 3)
// with nothing above it, so the centre is simply half its line height — the
// old in-bubble offset (border + padding) no longer applies. The timestamp +
// dot are pushed down by this amount (minus their own half-heights) so all
// three read on one baseline. Rail-end/start heights resolve against it; each
// adds `S100` back to account for the rail's negative `top` offset (it starts
// above the row outer edge).
const StreamHeaderInnerCenterY = `calc(${StreamNameLineHeight} / 2)`;

export const StreamRoot = recipe({
  base: {
    position: 'relative',
    display: 'grid',
    // Two tracks: dot column (auto = dot diameter) | content (1fr). The dot
    // column leads so the rail + dot hug the left screen edge; the nick and
    // timestamp ride the header line inside the content column.
    gridTemplateColumns: 'auto 1fr',
    columnGap: StreamRowGapVar,
    paddingLeft: StreamRowPadVar,
    alignItems: 'flex-start',
    // Vertical padding tuned to ~3px (down from S100/4px) so the
    // bubble-to-bubble gap reads tighter. Per-row contribution to the
    // gap drops from 8px to 7px on each side — combined with
    // MessageBase's S100 top/bot the total bubble↔bubble gap is
    // 4 + 3 + 0 (collapsed marginTop) + 3 + 4 = 14px ≈ S400/1.14.
    paddingTop: toRem(3),
    paddingBottom: toRem(3),
    paddingRight: config.space.S400,
  },
  variants: {
    compact: {
      false: {
        vars: {
          [StreamRowPadVar]: StreamRowPadDesktop,
          [StreamRowGapVar]: StreamRowGapDesktop,
        },
      },
      // Mobile: cancel MessageBase's S400/S200 horizontal padding so the
      // row spans edge-to-edge, then padding-left alone provides the
      // minimal screen→time anchor inside the row.
      true: {
        vars: {
          [StreamRowPadVar]: StreamRowPadMobile,
          [StreamRowGapVar]: StreamRowGapMobile,
        },
        marginLeft: `calc(-1 * ${config.space.S400})`,
        marginRight: `calc(-1 * ${config.space.S200})`,
        paddingRight: 0,
      },
    },
    // Same-minute continuation row (dot/name/time hidden): tighten the stack.
    // Drop the top padding and pull up by S100, so collapsed bodies sit ~7px
    // apart vs the ~14px gap between distinct minute groups. The rail bridge
    // (±S400) dwarfs this, so the rail line stays unbroken through the cluster.
    collapsed: {
      true: {
        paddingTop: 0,
        marginTop: `calc(-1 * ${config.space.S100})`,
      },
    },
  },
  defaultVariants: { compact: false, collapsed: false },
});

// DotColumn — grid track 1. Always StreamDotSize wide regardless of row
// type so message / sysline / day-divider rows all hit the same vertical
// X for the rail line — otherwise the rail would visibly kink at row
// boundaries with different dot sizes. Stretches to row content height
// so the rail abs-child can extend through the row.
export const StreamDotColumn = style({
  position: 'relative',
  alignSelf: 'stretch',
  width: StreamDotSize,
});

// Per-row rail segment. Lives inside DotColumn and is horizontally
// centred on the dot column (= the dot's centre). Top/bottom extend
// `bridge + row.paddingTop/Bottom` past the DotColumn bounds so consecutive
// rails meet across the row's vertical padding plus MessageBase's marginTop.
// DotColumn stretches to the row content area, so its top/bottom = row
// content top/bottom; we extend past those to reach row outer ± bridge.
export const StreamRail = style({
  position: 'absolute',
  top: `calc(-1 * (${StreamRailBridgeY} + ${config.space.S100}))`,
  bottom: `calc(-1 * (${StreamRailBridgeY} + ${config.space.S100}))`,
  left: '50%',
  transform: 'translateX(-50%)',
  width: StreamRailLineWidth,
  background: 'var(--vojo-timeline-rail)',
  pointerEvents: 'none',
  zIndex: 0,
});

// Rail-end / rail-start variants — stop the rail at the dot rather than
// extending past it. `StreamHeaderInnerCenterY` is the Y from DotColumn
// top to the dot centre (DotColumn top = row content top = excludes
// row.paddingTop); subtract the dot radius so the rail meets the dot edge.
export const StreamRailEnd = style({
  bottom: 'auto',
  // Top stays at the negative bridge offset; height takes the rail down
  // to the dot's centre line.
  height: `calc(${StreamRailBridgeY} + ${config.space.S100} + ${StreamHeaderInnerCenterY})`,
});

export const StreamRailStart = style({
  top: StreamHeaderInnerCenterY,
});

export const StreamRailSingle = style({
  display: 'none',
});

// Two-layer span: outer Halo paints a solid bg disk + ring that masks the
// rail line behind the dot regardless of opacity; inner Fill carries the
// author colour and the read-state opacity.
//
// Halo is a regular in-flow grid-cell child (no `position: absolute`)
// that sits in DotColumn. Its block-axis position inside the column is
// controlled per-row-type by composed classes below (Header* for message
// rows, Sysline* for syslines). `display: block` is REQUIRED — the
// rendered element is a `<span>`, and `width` / `height` / `margin-top`
// are silently ignored on inline-display elements (so the dot would
// collapse to zero size without it). Sysline / day-row variants override
// to `position: absolute`, which auto-blockifies; message rows rely on
// this declaration.
export const StreamDotHalo = style({
  display: 'block',
  width: StreamDotSize,
  height: StreamDotSize,
  borderRadius: '50%',
  background: color.SurfaceVariant.Container,
  boxShadow: `0 0 0 3px ${color.SurfaceVariant.Container}`,
  pointerEvents: 'none',
  position: 'relative',
  zIndex: 2,
});

// Sysline dot — smaller, abs-positioned at DotColumn centre so it
// vertically centres on the sysline row regardless of DotColumn height.
export const StreamSyslineDotHalo = style({
  position: 'absolute',
  top: '50%',
  left: '50%',
  transform: 'translate(-50%, -50%)',
  width: StreamSyslineDotSize,
  height: StreamSyslineDotSize,
});

// Sysline rail-end / rail-start — sysline DotColumn doesn't stretch, so
// the rail extends from the dot centre by bridge + half DotColumn height.
// We can't read DotColumn height at compile time, but `50%` of the rail's
// own absolute-positioned containing block (= DotColumn) gives the same
// effect.
// Sysline rail-end / rail-start variants. The rail's containing block is
// DotColumn (which stretches via `align-self: stretch` regardless of the
// parent's `align-items`). Heights/tops are measured from DotColumn's
// padding-edge top — `top: -(bridge + S100)` lifts the rail's top-zero
// position by `bridge + paddingTop` past the row outer edge, so the
// terminating end must add `S100` back when capping at the dot centre
// (which is at `50%` of DotColumn under `align-items: center`).
export const StreamSyslineRailEnd = style({
  bottom: 'auto',
  height: `calc(${StreamRailBridgeY} + ${config.space.S100} + 50%)`,
});

export const StreamSyslineRailStart = style({
  top: '50%',
});

// Message-row dot in DotColumn. Pushes the dot down by the same Y the
// bubble's header text occupies, so the dot centres on the Username line.
// Set as marginTop because Halo is in-flow.
export const StreamHeaderDotHalo = style({
  marginTop: `calc(${StreamHeaderInnerCenterY} - (${StreamDotSize} / 2))`,
  // Smooth the prominent→neutral shrink when a read (green) dot demotes back to
  // the own white resting state after the peer replies — the width/height are
  // set inline in Stream.tsx, this just animates the change.
  transition: 'width 220ms ease, height 220ms ease',
});

export const StreamDotFill = style({
  position: 'absolute',
  inset: 0,
  borderRadius: '50%',
  pointerEvents: 'none',
  // Smooth read↔unread fade + the green→white recolour — without it the state
  // flips read as a hard step.
  transition: 'opacity 220ms ease, filter 220ms ease, background-color 220ms ease',
});

// Wrapper for grid track 2 — holds the header line (nick + time), the bubble
// and the reactions row stacked. `min-width: 0` lets fit-content bubbles shrink
// below their content's natural size when the grid track is narrower than the
// bubble (otherwise they'd overflow).
export const StreamColumn = style({
  gridColumn: 2,
  minWidth: 0,
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'flex-start',
});

// Reactions row sits below the bubble, outside its bg/border, so reactions
// read as floating chips. Aligns to the bubble's left edge by inheriting
// `align-items: flex-start` from StreamColumn. The inline marginTop on the
// `<Reactions>` callsites in RoomTimeline supplies the spacing — flex
// columns don't collapse margins so a wrapper margin would stack.
export const StreamReactions = style({
  maxWidth: '100%',
  minWidth: 0,
});

// M3a: thread summary pill slot, sits between bubble and reactions in
// the same column. The card itself owns its margins/spacing — the
// wrapper just contributes `align-items: flex-start` from StreamColumn
// so the pill aligns to the bubble's left edge.
export const StreamThreadSummary = style({
  maxWidth: '100%',
  minWidth: 0,
});

export const StreamBubble = recipe({
  base: {
    // Incoming (peer) bubble — filled with the SAME surface as the composer
    // card (`color.Surface.Container`, see RoomView.css.ts) so the bubble and
    // the input form read as one material; it sits a step darker than the
    // `SurfaceVariant.Container` page background. Minimal rounding on ALL four
    // corners (no sharp/notched corner). Own messages override to a no-chrome
    // plain block below.
    backgroundColor: color.Surface.Container,
    color: color.SurfaceVariant.OnContainer,
    border: `${StreamBubbleBorderWidth} solid ${color.Surface.ContainerLine}`,
    borderRadius: StreamBubbleRadius,
    // Padding bumped ~1.1× (user request) so the bubble reads a touch larger
    // around the text: vertical 8→8.8px, horizontal 15→16.5px.
    paddingTop: toRem(8.8),
    paddingBottom: toRem(8.8),
    paddingLeft: toRem(16.5),
    paddingRight: toRem(16.5),
    minWidth: 0,
    maxWidth: toRem(720),
    position: 'relative',
    zIndex: 1,
  },
  variants: {
    // Own messages render as plain text on the chat background — no fill,
    // no border, no rounding, flush-left beneath the author name and
    // spanning the message column like a paragraph (user points 2 + 4).
    own: {
      true: {
        backgroundColor: 'transparent',
        border: 'none',
        borderRadius: 0,
        paddingLeft: 0,
        paddingRight: 0,
        paddingTop: 0,
        paddingBottom: 0,
        display: 'block',
        width: '100%',
        maxWidth: '100%',
      },
      false: {},
    },
    // Placeholder so the compound variants below can target the breakpoint.
    compact: { true: {}, false: {} },
    // Image / video: bubble becomes a transparent shell so the
    // StreamMediaImage child supplies the visible chrome. `display: block,
    // width: 100%` (NOT fit-content) so the child's `max-width: 100%` has a
    // definite width to clamp against — fit-content would make the chain
    // circular and overflow on narrow screens.
    mediaMode: { true: {} },
  },
  // Compounds are emitted after the variant classes, so they win the cascade.
  compoundVariants: [
    // Peer bubble width — fit the text on BOTH web/desktop and native/mobile
    // (the user settled on «по размеру текста» everywhere; this supersedes the
    // earlier native-stretch tweak). Mobile only tightens the horizontal
    // padding for narrow viewports.
    {
      variants: { own: false, compact: false, mediaMode: false },
      style: { display: 'inline-block', width: 'fit-content', maxWidth: '100%' },
    },
    {
      variants: { own: false, compact: true, mediaMode: false },
      style: {
        display: 'inline-block',
        width: 'fit-content',
        maxWidth: '100%',
        // Tighter horizontal padding on narrow viewports (12 × 1.1 ≈ 13.2px).
        paddingLeft: toRem(13.2),
        paddingRight: toRem(13.2),
      },
    },
    // Media shell wins over both the peer fill and the width rules above
    // (incl. own's lifted max-width) — keeps image/video bubbles capped at
    // the same 720px as before regardless of own/peer.
    {
      variants: { mediaMode: true },
      style: {
        backgroundColor: 'transparent',
        border: 'none',
        borderRadius: 0,
        padding: 0,
        display: 'block',
        width: '100%',
        maxWidth: toRem(720),
      },
    },
  ],
  defaultVariants: {
    own: false,
    compact: false,
    mediaMode: false,
  },
});

// Header line — sits ABOVE the bubble as the first child of StreamColumn
// (track 2), aligned on the dot baseline. Holds the bold author nick followed
// by a muted timestamp (rail+dot → nick → time, per the latest request). Bold,
// a step larger than chat body; the nick colour comes from the own/peer
// modifier classes below (own = white/black, peer = lavender). The inner Username
// button inherits colour/size/weight from here; the timestamp carries its own
// explicit muted colour so it does NOT pick up the nick tint.
export const StreamName = style({
  position: 'relative',
  // Symmetric top-left corner (user request): the vertical gap from the nick
  // down to the bubble equals the horizontal gap from the bubble's left edge
  // out to the timerail = column-gap + dot radius. Inherits StreamRowGapVar
  // from StreamRoot, so it tracks the per-breakpoint gap. Only applies on run
  // heads (continuations render no nick), so continuations stay tight.
  marginBottom: `calc(${StreamRowGapVar} + (${StreamDotSize} / 2))`,
  fontSize: StreamNameFontSize,
  lineHeight: StreamNameLineHeight,
  minHeight: StreamNameLineHeight,
  fontWeight: 700,
  display: 'flex',
  alignItems: 'center',
  gap: toRem(8),
  flexWrap: 'nowrap',
  minWidth: 0,
  // Bound the label to the message column so a long display name truncates
  // (ellipsis) instead of overflowing the row. StreamColumn uses
  // `align-items: flex-start`, so without this the flex container would
  // content-size past the column edge.
  maxWidth: '100%',
});

// Own / peer nick colour. Applied alongside StreamName so the inner Username
// inherits the tint. Own = the strong neutral «white» (white dark / black
// light); peer = the Dawn brand purple (lavender accent). Both bind to CSS
// vars (see index.css) so they stay tunable.
export const StreamNameOwn = style({
  color: 'var(--vojo-stream-name-own)',
});
export const StreamNamePeer = style({
  color: 'var(--vojo-stream-name-peer)',
});

// Let the nick child shrink so css.Username's ellipsis engages on long display
// names — replaces the old `<Text truncate>` wrapper. Must be a globalStyle:
// vanilla-extract `style()` selectors may only target `&`. The timestamp child
// opts out of shrinking via `flex-shrink: 0` in StreamHeaderTime below.
globalStyle(`${StreamName} > *`, {
  minWidth: 0,
});

// Header timestamp — inline on the header line, to the RIGHT of the nick
// (rail+dot → nick → time). Centred against the taller nick by the header
// row's `align-items: center`, so no manual baseline offset is needed. Carries
// its own explicit muted colour so the nick's green/lavender tint does not
// bleed onto it, and `flex-shrink: 0` so the nick truncates first.
export const StreamHeaderTime = style({
  flexShrink: 0,
  fontSize: toRem(11),
  lineHeight: StreamTimeLineHeight,
  fontVariantNumeric: 'tabular-nums',
  fontWeight: 400,
  color: color.Surface.OnContainer,
  opacity: 0.55,
  whiteSpace: 'nowrap',
  fontFamily:
    '"JetBrains Mono Variable", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
});

globalStyle(`${StreamHeaderTime} time`, {
  display: 'block',
  fontFamily: 'inherit',
  fontSize: toRem(11),
  lineHeight: StreamTimeLineHeight,
});

// Sysline — thin single-line state-event row inside Stream layout.
// Composes with StreamRoot so the dot / content tracks line up vertically
// with message rows above and below (dot column | body). Override align-items
// to center because the sysline content is one line tall and reads best
// vertically centred.
export const StreamSysline = style({
  alignItems: 'center',
  paddingTop: toRem(2),
  paddingBottom: toRem(2),
});

// System lines (room name/topic/avatar changes, hidden-event dev rows) read
// as muted, understated «Thinking»-style notes (user point 10) — soft grey,
// italic, in the regular sans (not the mono timestamp face) so they recede
// behind real messages instead of reading as another bubble.
export const StreamSyslineBody = style({
  fontSize: toRem(12),
  color: color.Surface.OnContainer,
  opacity: 0.5,
  fontStyle: 'italic',
  whiteSpace: 'nowrap',
  overflow: 'hidden',
  textOverflow: 'ellipsis',
  minWidth: 0,
});

// Sysline timestamp — trailing the body on the same line (content → time),
// mirroring the message header's nick → time order now that the dot leads the
// row. Muted mono, never shrinks, so the italic body truncates first.
export const StreamSyslineTime = style({
  flexShrink: 0,
  fontSize: toRem(11),
  lineHeight: StreamTimeLineHeight,
  fontVariantNumeric: 'tabular-nums',
  color: color.Surface.OnContainer,
  opacity: 0.45,
  whiteSpace: 'nowrap',
  fontFamily:
    '"JetBrains Mono Variable", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
});

globalStyle(`${StreamSyslineTime} time`, {
  display: 'block',
  fontFamily: 'inherit',
  fontSize: toRem(11),
  lineHeight: StreamTimeLineHeight,
});

// Day divider row — uses the SAME 2-track grid as message rows (dot column |
// content) so its dot and rail land on the exact same X as the dots/rails above
// and below it (otherwise the rail would visibly kink at the day boundary).
// Track 1 is the dot column (fixed dot width, anchored by the same paddingLeft),
// so the dots align by construction — no invisible time placeholder needed.
export const StreamDayRoot = recipe({
  base: {
    position: 'relative',
    display: 'grid',
    gridTemplateColumns: 'auto 1fr',
    columnGap: StreamRowGapVar,
    paddingLeft: StreamRowPadVar,
    alignItems: 'center',
    paddingTop: toRem(10),
    paddingBottom: toRem(10),
    paddingRight: config.space.S400,
  },
  variants: {
    compact: {
      false: {
        vars: {
          [StreamRowPadVar]: StreamRowPadDesktop,
          [StreamRowGapVar]: StreamRowGapDesktop,
        },
      },
      true: {
        vars: {
          [StreamRowPadVar]: StreamRowPadMobile,
          [StreamRowGapVar]: StreamRowGapMobile,
        },
        marginLeft: `calc(-1 * ${config.space.S400})`,
        marginRight: `calc(-1 * ${config.space.S200})`,
        paddingRight: 0,
      },
    },
  },
  defaultVariants: { compact: false },
});

export const StreamDayLabel = style({
  fontSize: toRem(11),
  textTransform: 'uppercase',
  letterSpacing: toRem(1),
  fontWeight: 600,
  color: color.Surface.OnContainer,
  opacity: 0.55,
  whiteSpace: 'nowrap',
  flexShrink: 0,
  fontFamily:
    '"JetBrains Mono Variable", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
});

// Day-divider dot — abs-positioned in DotColumn so it centres on the
// (StreamDotSize-wide) column regardless of its own larger size; the
// extra 3.1 px (= dayDot 12.1 - StreamDot 9) overflows symmetrically into
// the columnGap on each side.
export const StreamDayDot = style({
  position: 'absolute',
  top: '50%',
  left: '50%',
  transform: 'translate(-50%, -50%)',
  width: StreamDayDotSize,
  height: StreamDayDotSize,
  borderRadius: '50%',
  background: color.Primary.MainHover,
  boxShadow: `0 0 0 4px ${color.SurfaceVariant.Container}`,
  pointerEvents: 'none',
  zIndex: 2,
});

// `─── Сегодня ───` line — grid item in track 2. Flex row with two flex:1
// hairlines and the label centered between them. The dot's overflow into
// the columnGap means the leftmost line segment naturally starts past
// the dot's right edge.
export const StreamDayLineWrap = style({
  display: 'flex',
  alignItems: 'center',
  gap: config.space.S300,
});

export const StreamDayLineSegment = style({
  flex: 1,
  height: 1,
  background: 'var(--vojo-timeline-rail)',
  minWidth: toRem(8),
});

export const MessageTextBody = recipe({
  base: {
    wordBreak: 'break-word',
  },
  variants: {
    preWrap: {
      true: {
        whiteSpace: 'pre-wrap',
      },
    },
    jumboEmoji: {
      true: {
        fontSize: '1.504em',
        lineHeight: '1.4962em',
      },
    },
    emote: {
      true: {
        color: color.Success.Main,
        fontStyle: 'italic',
      },
    },
  },
});

export type MessageTextBodyVariants = RecipeVariants<typeof MessageTextBody>;
