// shared.jsx — Tiber Course shared tokens + base components
const C = {
  parchment:     '#EDE8DF',
  linen:         '#E4DED4',
  linenDeep:     '#D6CFC3',
  ink:           '#1A1917',
  ash:           '#4F4B45',   // darker body for readability
  dust:          '#6B655E',   // darker meta/labels for readability
  textInverse:   '#F5F3EE',
  brass:         '#6B2E2E',
  brassDim:      'rgba(107,46,46,0.14)',
  brassLight:    '#B97070',                  // lighter burgundy for dark-bg use
  brassLightDim: 'rgba(185,112,112,0.18)',   // dim variant on dark bg
  river:         '#3D6B6B',
  riverDim:      'rgba(61,107,107,0.12)',
  border:        'rgba(26,25,23,0.14)',
  borderStrong:  'rgba(26,25,23,0.26)',
  borderSubtle:  'rgba(26,25,23,0.08)',
  shadowLift:    '0 1px 3px rgba(26,25,23,0.06), 0 1px 2px rgba(26,25,23,0.04)',
  shadowCard:    '0 4px 16px rgba(26,25,23,0.08), 0 1px 4px rgba(26,25,23,0.04)',
};

const F = {
  display: "'Playfair Display', Georgia, serif",
  sans:    "'DM Sans', system-ui, sans-serif",
  body:    "'Inter', system-ui, sans-serif",
  mono:    "'JetBrains Mono', monospace",
};

const Btn = ({ primary, children, onClick, style, small }) => {
  const [hov, setHov] = React.useState(false);
  return (
    <button onClick={onClick}
      onMouseEnter={() => setHov(true)}
      onMouseLeave={() => setHov(false)}
      style={{
        background: primary ? (hov ? '#2e2c2a' : C.ink) : (hov ? 'rgba(26,25,23,0.05)' : 'transparent'),
        color: primary ? C.textInverse : C.ink,
        border: primary ? `1px solid ${C.ink}` : `1px solid ${C.border}`,
        borderRadius: 9999,
        padding: small ? '8px 20px' : '12px 28px',
        cursor: 'pointer',
        fontFamily: F.sans,
        fontSize: small ? 10 : 11,
        fontWeight: 500,
        letterSpacing: '2px',
        textTransform: 'uppercase',
        transition: 'all 0.2s ease',
        whiteSpace: 'nowrap',
        display: 'inline-flex',
        alignItems: 'center',
        justifyContent: 'center',
        textAlign: 'center',
        ...style,
      }}>{children}</button>
  );
};

const BrassBtn = ({ children, onClick, style, light }) => {
  const [hov, setHov] = React.useState(false);
  const base = light ? C.brass : C.brass;
  const hoverColor = light ? '#552323' : '#552323';
  return (
    <button onClick={onClick}
      onMouseEnter={() => setHov(true)}
      onMouseLeave={() => setHov(false)}
      style={{
        background: hov ? hoverColor : base,
        color: '#fff',
        border: 'none',
        borderRadius: 9999,
        padding: '12px 28px',
        cursor: 'pointer',
        fontFamily: F.sans,
        fontSize: 11, fontWeight: 500,
        letterSpacing: '2px', textTransform: 'uppercase',
        display: 'inline-flex',
        alignItems: 'center',
        justifyContent: 'center',
        textAlign: 'center',
        transition: 'background 0.2s ease',
        ...style,
      }}>{children}</button>
  );
};

const Label = ({ children, color, mb }) => (
  <div style={{
    fontFamily: F.sans, fontSize: 11, fontWeight: 600,
    letterSpacing: '1.8px', textTransform: 'uppercase',
    color: color || C.dust, marginBottom: mb !== undefined ? mb : 14,
  }}>{children}</div>
);

const SectionWrap = ({ children, style, dark, linen }) => (
  <section style={{
    background: dark ? C.ink : linen ? C.linen : C.parchment,
    borderTop: `1px solid ${dark ? 'rgba(237,232,223,0.08)' : C.borderSubtle}`,
    ...style,
  }}>
    <div style={{ maxWidth: 1100, margin: '0 auto', padding: 'clamp(56px, 10vw, 96px) clamp(20px, 5vw, 40px)' }}>
      {children}
    </div>
  </section>
);

const HR = ({ style }) => (
  <div style={{ height: 1, background: C.border, ...style }} />
);

/* ── Truncate ──────────────────────────────────────────────────────────────
   Single-line text that's clipped to its container, with:
   • text-overflow: ellipsis  → the "..." marker
   • mask-image gradient fade → smooth fade-out before the "..."
   • title attribute fallback → full text on hover
   Only applies the fade when the content actually overflows, so short text
   doesn't look prematurely dimmed.

   Usage:
     <Truncate>{course.title}</Truncate>
     <Truncate lines={2}>{course.desc}</Truncate>        // multi-line clamp
     <Truncate style={{ fontSize: 14 }}>...</Truncate>
*/
const Truncate = ({ children, lines = 1, style, title, fade = 26 }) => {
  const ref = React.useRef(null);
  const [over, setOver] = React.useState(false);

  React.useLayoutEffect(() => {
    const el = ref.current;
    if (!el) return;
    const check = () => {
      setOver(
        lines === 1
          ? el.scrollWidth > el.clientWidth + 1
          : el.scrollHeight > el.clientHeight + 1
      );
    };
    check();
    let ro;
    if (typeof ResizeObserver !== 'undefined') {
      ro = new ResizeObserver(check);
      ro.observe(el);
      if (el.parentElement) ro.observe(el.parentElement);
    } else {
      window.addEventListener('resize', check);
    }
    return () => {
      if (ro) ro.disconnect();
      else window.removeEventListener('resize', check);
    };
  }, [children, lines]);

  const fullText = title || (typeof children === 'string' ? children : undefined);

  const baseStyle = lines === 1
    ? {
        display: 'block',
        whiteSpace: 'nowrap',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        maxWidth: '100%',
        minWidth: 0,
        // Soft fade ending at a low (not zero) alpha so the ellipsis stays readable
        maskImage: over
          ? `linear-gradient(to right, #000 calc(100% - ${fade}px), rgba(0,0,0,0.25) 100%)`
          : 'none',
        WebkitMaskImage: over
          ? `linear-gradient(to right, #000 calc(100% - ${fade}px), rgba(0,0,0,0.25) 100%)`
          : 'none',
      }
    : {
        display: '-webkit-box',
        WebkitLineClamp: lines,
        WebkitBoxOrient: 'vertical',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        maxWidth: '100%',
        minWidth: 0,
        wordBreak: 'normal',
        overflowWrap: 'break-word',
        maskImage: over
          ? `linear-gradient(to bottom, #000 calc(100% - 0.9em), rgba(0,0,0,0.25) 100%)`
          : 'none',
        WebkitMaskImage: over
          ? `linear-gradient(to bottom, #000 calc(100% - 0.9em), rgba(0,0,0,0.25) 100%)`
          : 'none',
      };

  return (
    <span
      ref={ref}
      title={over ? fullText : undefined}
      style={{ ...baseStyle, ...style }}
    >{children}</span>
  );
};

/* ── Launch offer (single source of truth for the pre-order promo) ──
   Courses are still in production, so the site takes pre-orders at a launch
   discount. Change these in one place; the banner + pricing copy read from it. */
const OFFER = {
  active: true,
  discountPct: 30,
  endsLabel: '19 October 2026',
  endsShort: '19 Oct 2026',
};

/* Site-wide pre-order announcement bar. Fixed at the very top (height 40); the
   sticky Nav sits just below it (Nav top = 40, page content cleared to 124).
   Clicking it jumps to pricing. */
const AnnouncementBar = ({ onNav }) => {
  if (!OFFER.active) return null;
  const go = () => { if (onNav) onNav('pricing'); };
  return (
    <div
      onClick={go}
      role="button"
      tabIndex={0}
      onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); go(); } }}
      aria-label={`Courses coming soon — pre-order now and save ${OFFER.discountPct}% until ${OFFER.endsLabel}`}
      style={{
        position: 'fixed', top: 0, left: 0, right: 0, height: 40, zIndex: 250,
        background: C.brass, color: C.textInverse, cursor: 'pointer',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        padding: '0 16px', borderBottom: '1px solid rgba(0,0,0,0.14)',
        fontFamily: F.sans, fontWeight: 500, letterSpacing: '0.5px',
        whiteSpace: 'nowrap', overflow: 'hidden',
      }}
    >
      <span className="tc-ann-full" style={{ fontSize: 12 }}>
        Courses coming soon — pre-order now and save {OFFER.discountPct}% until {OFFER.endsLabel}&nbsp;&nbsp;→
      </span>
      <span className="tc-ann-short" style={{ fontSize: 11 }}>
        Pre-order · {OFFER.discountPct}% off till {OFFER.endsShort} →
      </span>
    </div>
  );
};

Object.assign(window, { C, F, Btn, BrassBtn, Label, SectionWrap, HR, Truncate, OFFER, AnnouncementBar });
