#!/usr/bin/env python3
"""
Fibo – Logistics Deck v3

FRAMEWORK:
  Design elements  → copy investor deck EXACTLY (positions, sizes, colors, frame)
  Content direction → RTL only (text align=END, direction=RTL, steps reversed)

KEY FIXES FROM v2:
  - Step circles REVERSED: 01 on RIGHT, 05 on LEFT, arrows ← 
  - Blue rule LEFT-aligned (design element, copy investor deck)
  - Title LEFT-positioned (x=MARGIN), text RIGHT-aligned within box
  - All text align=END + direction=RTL
  - Font: Heebo
  - No background fills anywhere — pure white
"""

import json, time
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build as gapi_build

# ── Auth ──────────────────────────────────────────────────────────────────────
with open('/root/.config/gws/credentials.json') as f:
    d = json.load(f)
if 'installed' in d:
    d = d['installed']

creds = Credentials(
    token=None, refresh_token=d['refresh_token'],
    token_uri='https://oauth2.googleapis.com/token',
    client_id=d['client_id'], client_secret=d['client_secret'],
    scopes=['https://www.googleapis.com/auth/presentations',
            'https://www.googleapis.com/auth/drive']
)
svc = gapi_build('slides', 'v1', credentials=creds)

# ── Constants (from investor deck analysis) ───────────────────────────────────
W, H    = 9144000, 5143500          # EMU 16:9
MARGIN  = int(W * 0.05)             # 5% = 457,200 EMU
BLUE    = {'red': 0.082, 'green': 0.396, 'blue': 0.753}   # #1565C0
WHITE   = {'red': 1.0,   'green': 1.0,   'blue': 1.0}
BLACK   = {'red': 0.102, 'green': 0.102, 'blue': 0.102}   # #1A1A1A
DGRAY   = {'red': 0.259, 'green': 0.259, 'blue': 0.259}   # #424242
MGRAY   = {'red': 0.600, 'green': 0.600, 'blue': 0.600}   # #999999
LGRAY   = {'red': 0.878, 'green': 0.878, 'blue': 0.878}   # #E0E0E0
BLUELT  = {'red': 0.937, 'green': 0.949, 'blue': 0.980}   # very light blue tint

def cm(c):  return int(c * 360000)
def pt(p):  return int(p * 12700)

def sz(w, h): return {'width':  {'magnitude': w, 'unit': 'EMU'},
                      'height': {'magnitude': h, 'unit': 'EMU'}}
def tf(x, y): return {'scaleX': 1, 'scaleY': 1,
                      'translateX': x, 'translateY': y, 'unit': 'EMU'}

def c_rgb(c):    return {'rgbColor': c}          # for solidFill.color
def c_fg(c):     return {'opaqueColor': {'rgbColor': c}}  # for foregroundColor

# ── Primitives ────────────────────────────────────────────────────────────────

def txt(R, sid, oid, text, x, y, w, h,
        size=16, bold=False, italic=False, color=None,
        align='END', font='Heebo', rtl=True):
    if color is None: color = BLACK
    R += [
        {'createShape': {
            'objectId': oid, 'shapeType': 'TEXT_BOX',
            'elementProperties': {
                'pageObjectId': sid,
                'size': sz(w, h), 'transform': tf(x, y)
            }
        }},
        {'insertText': {'objectId': oid, 'text': text, 'insertionIndex': 0}},
        {'updateTextStyle': {
            'objectId': oid,
            'style': {
                'bold': bold, 'italic': italic,
                'fontSize': {'magnitude': size, 'unit': 'PT'},
                'foregroundColor': c_fg(color),
                'fontFamily': font,
            },
            'fields': 'bold,italic,fontSize,foregroundColor,fontFamily',
            'textRange': {'type': 'ALL'}
        }},
        {'updateParagraphStyle': {
            'objectId': oid,
            'style': {
                'alignment': align,
                'direction': 'RIGHT_TO_LEFT' if rtl else 'LEFT_TO_RIGHT'
            },
            'fields': 'alignment,direction',
            'textRange': {'type': 'ALL'}
        }},
    ]

def box(R, sid, oid, x, y, w, h, fill, border_color=None):
    bc = border_color if border_color else fill
    R += [
        {'createShape': {
            'objectId': oid, 'shapeType': 'RECTANGLE',
            'elementProperties': {
                'pageObjectId': sid,
                'size': sz(w, h), 'transform': tf(x, y)
            }
        }},
        {'updateShapeProperties': {
            'objectId': oid,
            'shapeProperties': {
                'shapeBackgroundFill': {'solidFill': {'color': c_rgb(fill)}},
                'outline': {
                    'outlineFill': {'solidFill': {'color': c_rgb(bc)}},
                    'weight': {'magnitude': 1, 'unit': 'PT'}
                }
            },
            'fields': 'shapeBackgroundFill,outline'
        }},
    ]

def circ(R, sid, oid, x, y, d, fill):
    R += [
        {'createShape': {
            'objectId': oid, 'shapeType': 'ELLIPSE',
            'elementProperties': {
                'pageObjectId': sid,
                'size': sz(d, d), 'transform': tf(x, y)
            }
        }},
        {'updateShapeProperties': {
            'objectId': oid,
            'shapeProperties': {
                'shapeBackgroundFill': {'solidFill': {'color': c_rgb(fill)}},
                'outline': {
                    'outlineFill': {'solidFill': {'color': c_rgb(fill)}},
                    'weight': {'magnitude': 1, 'unit': 'PT'}
                }
            },
            'fields': 'shapeBackgroundFill,outline'
        }},
    ]

def bg(R, sid):
    R.append({'updatePageProperties': {
        'objectId': sid,
        'pageProperties': {'pageBackgroundFill': {'solidFill': {'color': c_rgb(WHITE)}}},
        'fields': 'pageBackgroundFill'
    }})

# ── Shared slide chrome (investor deck design — COPIED exactly) ───────────────

def frame(R, sid):
    """3-sided frame: left + right + bottom. 5px. Blue. No top."""
    t = pt(4)
    box(R, sid, f'{sid}_fl', 0,       0,   t, H,   BLUE)
    box(R, sid, f'{sid}_fr', W - t,   0,   t, H,   BLUE)
    box(R, sid, f'{sid}_fb', 0,   H - t,   W, t,   BLUE)

def slidenum(R, sid, n):
    """Slide number — bottom right, small gray, LTR."""
    txt(R, sid, f'{sid}_sn', f'{n:02d}',
        W - MARGIN - cm(2), H - cm(1.6), cm(2), cm(1.2),
        size=13, color=MGRAY, align='END', rtl=False)

def title_block(R, sid, title_text, y0=cm(0.9)):
    """
    Title + blue rule.
    Title: LEFT-positioned text box (x=MARGIN), FULL-WIDTH, text RIGHT-aligned.
    Blue rule: LEFT-aligned under title (design element, copy investor deck).
    """
    title_h = pt(68)
    txt(R, sid, f'{sid}_ttl', title_text,
        MARGIN, y0, W - 2 * MARGIN, title_h,
        size=50, bold=True, color=BLACK, align='END')
    # Blue rule — LEFT-aligned, 22% width (investor deck measurement)
    rule_w = int(W * 0.22)
    box(R, sid, f'{sid}_rule',
        MARGIN, y0 + title_h + cm(0.1),
        rule_w, pt(4), BLUE)

def content_top(y0=cm(0.9)):
    """Y position of first content line, below title block."""
    return y0 + pt(68) + cm(0.15) + pt(4) + cm(0.4)

# ── Slide builders ────────────────────────────────────────────────────────────

def slide_cover(R, data):
    sid = data['id']
    bg(R, sid); frame(R, sid); slidenum(R, sid, data['num'])
    # Large title — right-aligned
    txt(R, sid, f'{sid}_t1', data['title'],
        MARGIN, cm(3.0), W - 2 * MARGIN, pt(80),
        size=58, bold=True, color=BLACK, align='END')
    # Subtitle in blue
    txt(R, sid, f'{sid}_t2', data['subtitle'],
        MARGIN, cm(6.0), W - 2 * MARGIN, pt(80),
        size=58, bold=True, color=BLUE, align='END')
    # Blue rule — LEFT-aligned under subtitle
    box(R, sid, f'{sid}_rule',
        MARGIN, cm(8.9), int(W * 0.22), pt(4), BLUE)
    # Tagline
    txt(R, sid, f'{sid}_tag', data['tagline'],
        MARGIN, cm(9.4), W - 2 * MARGIN, pt(32),
        size=21, color=DGRAY, align='END')
    # Date
    txt(R, sid, f'{sid}_dt', data['date'],
        MARGIN, cm(11.0), W - 2 * MARGIN, pt(22),
        size=14, color=MGRAY, align='END')


def slide_bullets(R, data):
    sid = data['id']
    bg(R, sid); frame(R, sid); slidenum(R, sid, data['num'])
    title_block(R, sid, data['title'])
    cy = content_top()

    for j, p in enumerate(data['points']):
        # Bullet dot — on the RIGHT side (RTL)
        dot_x = W - MARGIN - pt(6)
        box(R, sid, f'{sid}_dot{j}',
            dot_x, cy + j * pt(34) + pt(10), pt(6), pt(6), BLACK)
        txt(R, sid, f'{sid}_p{j}', p,
            MARGIN, cy + j * pt(34), W - 2 * MARGIN - pt(18), pt(30),
            size=19, color=BLACK, align='END')

    if 'kicker' in data:
        ky = cy + len(data['points']) * pt(34) + cm(0.4)
        box(R, sid, f'{sid}_kr', MARGIN, ky, int(W * 0.22), pt(3), LGRAY)
        txt(R, sid, f'{sid}_kk', data['kicker'],
            MARGIN, ky + cm(0.2), W - 2 * MARGIN, pt(28),
            size=17, italic=True, color=BLUE, align='END')


def slide_stats(R, data):
    sid = data['id']
    bg(R, sid); frame(R, sid); slidenum(R, sid, data['num'])
    title_block(R, sid, data['title'])
    cy = content_top()

    stats = data['stats']
    # RTL order: first stat on RIGHT, last on LEFT
    # Reverse them so right-to-left reading order is preserved
    stats_rtl = list(reversed(stats))
    col_w = (W - 2 * MARGIN) // len(stats_rtl)

    for j, (num, label) in enumerate(stats_rtl):
        x = MARGIN + j * col_w
        if j > 0:
            box(R, sid, f'{sid}_dv{j}', x, cy, pt(1), pt(60) + cm(0.4), LGRAY)
        txt(R, sid, f'{sid}_sn{j}', num,
            x, cy, col_w - cm(0.3), pt(55),
            size=42, bold=True, color=BLUE, align='END')
        txt(R, sid, f'{sid}_sl{j}', label,
            x, cy + pt(58), col_w - cm(0.3), pt(28),
            size=15, color=DGRAY, align='END')

    if 'body' in data:
        by = cy + pt(58) + pt(28) + cm(0.4)
        box(R, sid, f'{sid}_hr', MARGIN, by - cm(0.1), W - 2 * MARGIN, pt(1), LGRAY)
        txt(R, sid, f'{sid}_body', data['body'],
            MARGIN, by + cm(0.1), W - 2 * MARGIN, pt(50),
            size=15, color=DGRAY, align='END')


def slide_steps(R, data):
    """
    *** KEY RTL FIX ***
    Steps REVERSED: 01 on the RIGHT, 05 on the LEFT.
    Arrows point LEFT (←).
    Text: RIGHT-aligned, RTL direction.
    Visual style: copied from investor deck.
    """
    sid = data['id']
    bg(R, sid); frame(R, sid); slidenum(R, sid, data['num'])
    title_block(R, sid, data['title'])

    steps = data['steps']  # list of (num, name, desc)
    n = len(steps)

    # Circle X positions from investor deck analysis: [12%, 31%, 50%, 69%, 88%]
    # REVERSED for RTL: step 01 → 88% (rightmost), step 05 → 12% (leftmost)
    cx_pcts = [0.88, 0.69, 0.50, 0.31, 0.12]  # RTL: step[0]=01 on right
    circ_d  = cm(1.75)   # ~75px diameter
    circ_y  = cm(4.2)    # Y top of circles
    name_y  = circ_y + circ_d + cm(0.45)
    desc_y  = name_y + pt(32)
    col_hw  = cm(2.6)    # half-width of text column under each circle

    # Horizontal connecting line behind circles (full span)
    lx1 = int(W * cx_pcts[-1])   # left edge (05 circle center)
    lx2 = int(W * cx_pcts[0])    # right edge (01 circle center)
    box(R, sid, f'{sid}_hl',
        lx1, circ_y + circ_d // 2 - pt(1),
        lx2 - lx1, pt(2), LGRAY)

    for j, (num, name, desc) in enumerate(steps):
        cx = int(W * cx_pcts[j])
        ox = cx - circ_d // 2

        # Circle
        circ(R, sid, f'{sid}_c{j}', ox, circ_y, circ_d, BLUE)
        # Number — LTR (numbers are always LTR), centered
        txt(R, sid, f'{sid}_cn{j}', num,
            ox + pt(4), circ_y + pt(10), circ_d - pt(8), circ_d - pt(18),
            size=24, bold=True, color=WHITE, align='CENTER', rtl=False)

        # Arrow between circles — pointing LEFT (←) = RTL flow
        if j < n - 1:
            # Arrow goes between circle j (right) and circle j+1 (left)
            # Midpoint between cx_pcts[j] and cx_pcts[j+1]
            ax = int(W * (cx_pcts[j] + cx_pcts[j + 1]) / 2) - pt(10)
            txt(R, sid, f'{sid}_ar{j}', '←',
                ax, circ_y + pt(16), pt(28), pt(28),
                size=20, color=MGRAY, align='CENTER', rtl=False)

        # Step name — right-aligned, RTL
        txt(R, sid, f'{sid}_nm{j}', name,
            cx - col_hw, name_y, col_hw * 2, pt(30),
            size=20, bold=True, color=BLACK, align='END')
        # Step description — right-aligned, RTL
        txt(R, sid, f'{sid}_ds{j}', desc,
            cx - col_hw, desc_y, col_hw * 2, pt(58),
            size=14, color=DGRAY, align='END')


def slide_comparison(R, data):
    """RTL table: criterion column on RIGHT, FiBo on LEFT."""
    sid = data['id']
    bg(R, sid); frame(R, sid); slidenum(R, sid, data['num'])
    title_block(R, sid, data['title'])
    cy = content_top()

    # Columns RTL order: FiBo | RFID | GPS | Barcode | Criterion
    header = data['header']
    rows   = data['rows']
    nc     = len(header)
    crit_w = cm(4.8)
    data_w = (W - 2 * MARGIN - crit_w) // (nc - 1)
    row_h  = pt(38)

    # Col x positions: data cols left to right, criterion on far right
    col_x = [MARGIN + i * data_w for i in range(nc - 1)] + [W - MARGIN - crit_w]

    # Header row
    box(R, sid, f'{sid}_hbg', MARGIN, cy, W - 2 * MARGIN, row_h, BLUE)
    for ci, (lbl, x) in enumerate(zip(header, col_x)):
        cw = crit_w if ci == nc - 1 else data_w
        txt(R, sid, f'{sid}_h{ci}', lbl,
            x + cm(0.1), cy + pt(4), cw - cm(0.2), row_h - pt(6),
            size=14, bold=True, color=WHITE, align='END')

    # Data rows
    for ri, row in enumerate(rows):
        ry = cy + (ri + 1) * row_h
        if ri % 2 == 0:
            box(R, sid, f'{sid}_rb{ri}', MARGIN, ry, W - 2 * MARGIN, row_h, BLUELT)
        for ci, (cell, x) in enumerate(zip(row, col_x)):
            cw = crit_w if ci == nc - 1 else data_w
            if ci == 0:       # FiBo column — blue ✓
                c = BLUE if cell == '✓' else MGRAY
                b = cell == '✓'
            elif ci == nc - 1:  # Criterion — black
                c, b = BLACK, False
            else:
                c = DGRAY if cell == '✓' else MGRAY
                b = False
            txt(R, sid, f'{sid}_r{ri}c{ci}', cell,
                x + cm(0.1), ry + pt(4), cw - cm(0.2), row_h - pt(6),
                size=14, bold=b, color=c, align='END')


def slide_cards(R, data):
    """3×2 card grid."""
    sid = data['id']
    bg(R, sid); frame(R, sid); slidenum(R, sid, data['num'])
    title_block(R, sid, data['title'])
    cy = content_top()

    cards = data['cards']
    gap   = cm(0.35)
    cw    = (W - 2 * MARGIN - 2 * gap) // 3
    ch    = cm(3.1)

    for j, (icon, name, desc) in enumerate(cards):
        col = j % 3
        row = j // 3
        x = MARGIN + col * (cw + gap)
        y = cy + row * (ch + gap)
        # Border-only card
        R.append({'createShape': {
            'objectId': f'{sid}_cb{j}', 'shapeType': 'RECTANGLE',
            'elementProperties': {'pageObjectId': sid, 'size': sz(cw, ch), 'transform': tf(x, y)}
        }})
        R.append({'updateShapeProperties': {
            'objectId': f'{sid}_cb{j}',
            'shapeProperties': {
                'shapeBackgroundFill': {'solidFill': {'color': c_rgb(WHITE)}},
                'outline': {'outlineFill': {'solidFill': {'color': c_rgb(LGRAY)}},
                            'weight': {'magnitude': 1, 'unit': 'PT'}}
            },
            'fields': 'shapeBackgroundFill,outline'
        }})
        txt(R, sid, f'{sid}_ci{j}', icon,
            x + cm(0.3), y + cm(0.3), cw - cm(0.6), cm(0.85),
            size=20, align='END', rtl=False)
        txt(R, sid, f'{sid}_cn{j}', name,
            x + cm(0.3), y + cm(0.3), cw - cm(0.6), cm(0.85),
            size=16, bold=True, color=BLACK, align='END')
        txt(R, sid, f'{sid}_cd{j}', desc,
            x + cm(0.3), y + cm(1.3), cw - cm(0.6), cm(1.6),
            size=13, color=DGRAY, align='END')


def slide_table(R, data):
    """Generic table slide."""
    sid = data['id']
    bg(R, sid); frame(R, sid); slidenum(R, sid, data['num'])
    title_block(R, sid, data['title'])
    cy = content_top()

    header = data['header']
    rows   = data['rows']
    nc     = len(header)
    row_h  = pt(46)
    col_w  = (W - 2 * MARGIN) // nc
    col_x  = [MARGIN + i * col_w for i in range(nc)]

    box(R, sid, f'{sid}_hbg', MARGIN, cy, W - 2 * MARGIN, row_h, BLUE)
    for ci, (lbl, x) in enumerate(zip(header, col_x)):
        txt(R, sid, f'{sid}_h{ci}', lbl,
            x + cm(0.2), cy + pt(6), col_w - cm(0.4), row_h - pt(10),
            size=15, bold=True, color=WHITE, align='END')

    for ri, row in enumerate(rows):
        ry = cy + (ri + 1) * row_h
        if ri % 2 == 0:
            box(R, sid, f'{sid}_rb{ri}', MARGIN, ry, W - 2 * MARGIN, row_h, BLUELT)
        for ci, (cell, x) in enumerate(zip(row, col_x)):
            c = BLUE if ci == 1 else BLACK   # price column in blue
            txt(R, sid, f'{sid}_r{ri}c{ci}', cell,
                x + cm(0.2), ry + pt(6), col_w - cm(0.4), row_h - pt(10),
                size=14, color=c, align='END')

    if 'kicker' in data:
        ky = cy + (len(rows) + 1) * row_h + cm(0.4)
        txt(R, sid, f'{sid}_kk', data['kicker'],
            MARGIN, ky, W - 2 * MARGIN, pt(28),
            size=16, italic=True, color=BLUE, align='END')


def slide_team(R, data):
    sid = data['id']
    bg(R, sid); frame(R, sid); slidenum(R, sid, data['num'])
    title_block(R, sid, data['title'])
    cy = content_top()

    members = data['members']
    cw = (W - 2 * MARGIN - cm(1)) // 2

    for j, (name, role, bio) in enumerate(members):
        x = MARGIN + j * (cw + cm(1))
        d = cm(1.6)
        # Avatar on the RIGHT of the card (RTL)
        circ(R, sid, f'{sid}_av{j}', x + cw - d, cy, d, BLUE)
        txt(R, sid, f'{sid}_ai{j}', name[0],
            x + cw - d + pt(4), cy + pt(6), d - pt(8), d - pt(12),
            size=22, bold=True, color=WHITE, align='CENTER', rtl=False)
        txt(R, sid, f'{sid}_nm{j}', name,
            x, cy, cw - d - cm(0.3), cm(0.9),
            size=22, bold=True, color=BLACK, align='END')
        txt(R, sid, f'{sid}_rl{j}', role,
            x, cy + cm(1.0), cw, cm(0.75),
            size=15, color=BLUE, align='END')
        box(R, sid, f'{sid}_dv{j}', x, cy + cm(1.85), cw, pt(1), LGRAY)
        txt(R, sid, f'{sid}_bi{j}', bio,
            x, cy + cm(2.05), cw, cm(2.5),
            size=14, color=DGRAY, align='END')

    if 'note' in data:
        ny = cy + cm(4.8)
        box(R, sid, f'{sid}_nr', MARGIN, ny - cm(0.1), W - 2 * MARGIN, pt(1), LGRAY)
        txt(R, sid, f'{sid}_nt', data['note'],
            MARGIN, ny + cm(0.05), W - 2 * MARGIN, pt(26),
            size=14, color=MGRAY, align='END')


def slide_cta(R, data):
    sid = data['id']
    bg(R, sid); frame(R, sid); slidenum(R, sid, data['num'])
    title_block(R, sid, data['title'])
    cy = content_top()

    for j, p in enumerate(data['offer']):
        # Bullet dot on the RIGHT (RTL)
        dot_x = W - MARGIN - pt(7)
        box(R, sid, f'{sid}_dt{j}',
            dot_x, cy + j * pt(36) + pt(12), pt(7), pt(7), BLUE)
        txt(R, sid, f'{sid}_o{j}', p,
            MARGIN, cy + j * pt(36), W - 2 * MARGIN - pt(18), pt(32),
            size=19, color=BLACK, align='END')

    ky = cy + len(data['offer']) * pt(36) + cm(0.5)
    box(R, sid, f'{sid}_kb', MARGIN, ky, W - 2 * MARGIN, pt(58), BLUE)
    txt(R, sid, f'{sid}_kk', data['kicker'],
        MARGIN + cm(0.4), ky + pt(8), W - 2 * MARGIN - cm(0.8), pt(44),
        size=17, bold=True, color=WHITE, align='END')


# ── Slide data ────────────────────────────────────────────────────────────────

SLIDES = [
  {'id':'sld_01','num':1,'type':'cover',
   'title':'החבילה יודעת איפה היא.',
   'subtitle':'תמיד.',
   'tagline':'FiBo — מעקב לוגיסטי פסיבי בזמן אמת',
   'date':'מרץ 2026'},

  {'id':'sld_02','num':2,'type':'bullets',
   'title':'הלוגיסטיקה פועלת על סריקות ידניות',
   'points':[
     'כל חבילה שלא נסרקת — היא כתם עיוור במערכת',
     '1%–3% מהחבילות אובדות או מתעכבות מדי שנה',
     'המייל האחרון: יקר ביותר — ונראה הכי פחות',
     'תורות, טעויות אנוש, ואחריות שנעלמת בין ידיים',
   ],
   'kicker':'כל סריקה שלא בוצעה — פרצה במידע, עיכוב בלקוח, ועלות שמישהו משלם.'},

  {'id':'sld_03','num':3,'type':'stats',
   'title':'שוק שמחכה לפתרון',
   'stats':[('$500B+','שוק הלוגיסטיקה העולמי'),('1–3%','חבילות אבודות / מאוחרות'),
            ('+$700M','הפסד שנתי — ניו יורק'),('€700M','הפסד שנתי — פריז')],
   'body':'מרבית מערכות המעקב הקיימות מבוססות על ברקוד או NFC קצר טווח — כל פעולה שלא בוצעה היא נתון שאבד.'},

  {'id':'sld_04','num':4,'type':'bullets',
   'title':'מדבקת FiBo על כל חבילה.',
   'points':[
     'פרוטוקול MFC (Medium Field Communication) — טווח 50 ס״מ',
     'פסיבי לחלוטין — אין צורך בסריקה, בלחיצה, בפעולה כלשהי',
     'מוצפן ברמת מערכת — פועל גם ללא אינטרנט',
     'API פתוח — משתלב ישירות עם WMS / ERP קיים',
   ],
   'kicker':'החבילה נכנסת לטווח האנטנה → המערכת מזהה, מתעדת, מעדכנת. אפס חיכוך.'},

  {'id':'sld_05','num':5,'type':'steps',
   'title':'ככה זה עובד',
   'steps':[
     ('01','מדבקת FiBo',    'מודבקת על החבילה — פסיבית, קטנה, ללא סוללה'),
     ('02','אנטנת זיהוי',   'מותקנת בנקודות ביקורת — מחסן, רציף, רכב'),
     ('03','זיהוי אוטומטי', 'עוברת בטווח 50 ס״מ → אירוע נרשם אוטומטית'),
     ('04','שרת מקומי',     'מעבד נתונים locally, ללא תלות ברשת חיצונית'),
     ('05','דשבורד + API',  'נתוני מיקום בזמן אמת → WMS, ERP, וכל מערכת'),
   ]},

  {'id':'sld_06','num':6,'type':'bullets',
   'title':'מה אתם רואים',
   'points':[
     'כל חבילה, כל מיקום, היסטוריית מסלול מלאה',
     'התראות חריגה — עיכוב, סטייה ממסלול, עצירה',
     'ניתוח דפוסי תנועה לשיפור תהליכים',
     'אינטגרציה מלאה עם מערכות קיימות דרך API פתוח',
   ],
   'kicker':'ממשק אחד. מידע שלם. בזמן אמת.'},

  {'id':'sld_07','num':7,'type':'comparison',
   'title':'FiBo מול החלופות',
   # RTL: FiBo leftmost column (displayed first for RTL reader = most prominent)
   # Criterion on RIGHT
   'header':('FiBo','שער RFID','GPS','ברקוד','קריטריון'),
   'rows':[
     ('✓','✗','✗','✗','פסיבי — ללא פעולה'),
     ('✓','✓','✗','✓','ללא סוללה בתג'),
     ('✓','✗','✓','✓','ללא שערים יקרים'),
     ('✓','✓','✗','✗','טווח 50 ס״מ בתנועה'),
     ('✓','✗','✗','✓','עובד offline'),
     ('✓','חלקי','חלקי','חלקי','API פתוח'),
   ]},

  {'id':'sld_08','num':8,'type':'cards',
   'title':'איפה FiBo עובד',
   'cards':[
     ('🏭','מחסן',         'כניסה / יציאה אוטומטית, ספירת מלאי בזמן אמת'),
     ('🚚','הובלה',        'עדכוני טעינה / פריקה ללא נהג, ניטור רכב'),
     ('📦','מייל אחרון',   'אישור מסירה אוטומטי, ללא חתימה דיגיטלית'),
     ('❄️','שרשרת קרה',   'תיעוד כל נקודת מעבר לצרכי תאימות'),
     ('🔁','החזרות',       'קבלה ומיון אוטומטיים — בלי צוואר בקבוק'),
     ('💎','סחורה יקרת ערך','ניטור צמוד, התראות חריגה בזמן אמת'),
   ]},

  {'id':'sld_09','num':9,'type':'bullets',
   'title':'הטכנולוגיה כבר בשטח',
   'points':[
     'פיילוט פעיל עם חברת דן — תיקוף אוטומטי של נסיעות',
     'פריסה מבצעית עם צבא ההגנה לישראל וחיל הרפואה',
     'פיתוח מתקדם עם שותפים בתשתיות חכמות ומתקני תעשייה',
     'הפרוטוקול פותח מתוך דרישה מבצעית אמיתית — לא POC מעבדתי',
   ],
   'kicker':'הטכנולוגיה שעובדת בתוך אוטובוס צפוף — תעבוד גם במחסן שלכם.'},

  {'id':'sld_10','num':10,'type':'table',
   'title':'מודל עסקי',
   'header':('הערות','תמחור','רכיב'),
   'rows':[
     ('כולל תמיכה ורישיון','₪300K הטמעה + ₪500K/שנה','מערכת FiBo'),
     ('תחזוקה כלולה','₪2,500/שנה + ₪1,000 התקנה','אנטנת FiBo'),
     ('עצמאית, ללא אינטרנט','₪10,000/שנה','מערכת הדפסת תגים'),
   ],
   'kicker':'מחיר קבוע לפי נפח — ללא עמלה לסריקה, ללא הפתעות.'},

  {'id':'sld_11','num':11,'type':'team',
   'title':'הצוות',
   'members':[
     ('אור מרכוס','מנכ״ל ומייסד משותף',
      'קצין פעיל לשעבר, מנהל מוצר ביחידות 8200 וממר״ם.\nהוביל פרויקטים טכנולוגיים בקנה מידה ארצי.'),
     ('ניב בייביץ','סמנכ״ל טכנולוגיות ומייסד משותף',
      'מומחה ארכיטקטורת סייבר, יחידה 108.\nהקים בית תוכנה וניהל מאות עובדים.'),
   ],
   'note':'שיתוף פעולה עם תוכנית המצוינות "מפתחים" — כוח אדם איכותי וגמיש.'},

  {'id':'sld_12','num':12,'type':'cta',
   'title':'בואו נתחיל בפיילוט',
   'offer':[
     'הגדרת נקודות ביקורת — מחסן, רציף, רכב',
     'התקנת אנטנות ב-X מיקומים',
     '90 יום — נמדוד ביחד: כיסוי, דיוק, זמן תגובה',
     'לאחר הפיילוט: מסלול פריסה מלא',
   ],
   'kicker':'הטכנולוגיה מוכנה. השוק מוכן. השאלה היחידה: כמה חבילות אבדו לכם החודש?'},
]

BUILDERS = {
    'cover':      slide_cover,
    'bullets':    slide_bullets,
    'stats':      slide_stats,
    'steps':      slide_steps,
    'comparison': slide_comparison,
    'cards':      slide_cards,
    'table':      slide_table,
    'team':       slide_team,
    'cta':        slide_cta,
}

# ── Main ──────────────────────────────────────────────────────────────────────

def build():
    pres = svc.presentations().create(body={'title': 'Fibo – Logistics v3 | מעקב חבילות'}).execute()
    pid  = pres['presentationId']
    dsid = pres['slides'][0]['objectId']
    print(f'Created: https://docs.google.com/presentation/d/{pid}/edit')

    svc.presentations().batchUpdate(presentationId=pid,
        body={'requests': [{'deleteObject': {'objectId': dsid}}]}
    ).execute()
    time.sleep(0.3)

    for i, data in enumerate(SLIDES):
        sid = data['id']
        svc.presentations().batchUpdate(presentationId=pid, body={'requests': [
            {'createSlide': {
                'objectId': sid, 'insertionIndex': i,
                'slideLayoutReference': {'predefinedLayout': 'BLANK'}
            }}
        ]}).execute()
        time.sleep(0.25)

        R = []
        BUILDERS[data['type']](R, data)
        if R:
            svc.presentations().batchUpdate(presentationId=pid, body={'requests': R}).execute()
            time.sleep(0.3)

        print(f'  ✓ {i+1:02d}. {data["title"]}')

    # Share with Assaf
    drive = gapi_build('drive', 'v3', credentials=creds)
    drive.permissions().create(
        fileId=pid,
        body={'type': 'user', 'role': 'writer', 'emailAddress': 'assafdagan@gmail.com'},
        sendNotificationEmail=False
    ).execute()
    print('  → Shared with assafdagan@gmail.com')
    return pid

if __name__ == '__main__':
    pid = build()
    url = f'https://docs.google.com/presentation/d/{pid}/edit'
    print(f'\n✅  {url}')
    with open('/root/.openclaw/workspace/work/pitches/fibo/logistics-deck/deck_v3_url.txt', 'w') as f:
        f.write(url + '\n')
