Here's the complete refactored package structure. Each file is designed with clear single responsibilities, proper type hints, relative imports, and a clean public API.

### `queue/__init__.py`
```python
"""Job Queue Package - Clean public API."""

from .core import Job, JobQueue
from .priority import PriorityQueue
from .retry import process_with_retry

# Expose core components as requested
__all__ = ["Job", "JobQueue", "PriorityQueue", "process_with_retry"]
```

### `queue/core.py`
```python
"""Core data structures and basic FIFO queue operations."""

from dataclasses import dataclass, field
from datetime import datetime
from typing import Any, Optional
from collections import deque


@dataclass
class Job:
    """Represents a unit of work to be processed."""
    job_id: str
    payload: Any
    priority: int = 0  # Optional priority field for PriorityQueue
    status: str = "pending"
    created_at: datetime = field(default_factory=datetime.now)


class JobQueue:
    """A simple FIFO job queue."""
    
    def __init__(self):
        self._queue: deque[Job] = deque()

    def add_job(self, job: Job) -> None:
        """Add a job to the end of the queue."""
        self._queue.append(job)

    def get_next_job(self) -> Optional[Job]:
        """Retrieve and remove the next job in FIFO order."""
        if self.is_empty():
            return None
        return self._queue.popleft()

    def remove_job(self, job_id: str) -> bool:
        """Remove a specific job by ID. Returns True if found and removed."""
        for i, job in enumerate(self._queue):
            if job.job_id == job_id:
                self._queue.remove(job)
                return True
        return False

    def is_empty(self) -> bool:
        """Check if the queue contains no jobs."""
        return len(self._queue) == 0

    def size(self) -> int:
        """Return the number of jobs in the queue."""
        return len(self._queue)
```

### `queue/priority.py`
```python
"""Priority-based job queue using a min-heap."""

import heapq
from typing import Optional
from .core import Job


class PriorityQueue:
    """A priority queue that processes lower priority values first.
    
    Uses a counter as a tie-breaker to maintain FIFO order for jobs 
    with equal priority values.
    """
    
    def __init__(self):
        self._heap: list[tuple[int, int, Job]] = []
        self._counter: int = 0

    def add_job(self, job: Job) -> None:
        """Add a job to the priority queue."""
        entry = (job.priority, self._counter, job)
        self._counter += 1
        heapq.heappush(self._heap, entry)

    def get_next_job(self) -> Optional[Job]:
        """Retrieve and remove the highest priority job."""
        if self.is_empty():
            return None
        _, _, job = heapq.heappop(self._heap)
        return job

    def is_empty(self) -> bool:
        """Check if the queue contains no jobs."""
        return len(self._heap) == 0

    def size(self) -> int:
        """Return the number of jobs in the queue."""
        return len(self._heap)
```

### `queue/retry.py`
```python
"""Retry logic with exponential backoff."""

import time
import logging
from typing import Any, Callable
from .core import Job

logger = logging.getLogger(__name__)


def process_with_retry(
    job: Job,
    processor: Callable[[Job], Any],
    max_retries: int = 3,
    base_delay: float = 1.0
) -> Any:
    """Execute a processor function with exponential backoff on failure.
    
    Args:
        job: The job to process.
        processor: Callable that accepts a Job and returns a result.
        max_retries: Maximum number of retry attempts.
        base_delay: Base delay in seconds for exponential backoff.
        
    Returns:
        The result of the processor function.
        
    Raises:
        The last exception raised by the processor if all retries fail.
    """
    last_exception = None

    for attempt in range(max_retries + 1):
        try:
            result = processor(job)
            job.status = "completed"
            return result
        except Exception as e:
            last_exception = e
            job.status = "failed"
            if attempt == max_retries:
                break
                
            delay = base_delay * (2 ** attempt)
            logger.warning(
                f"Attempt {attempt + 1} failed for job {job.job_id}: {e}. "
                f"Retrying in {delay:.2f}s..."
            )
            time.sleep(delay)

    raise last_exception
```

### Key Design Decisions:
1. **Single Responsibility**: 
   - `core.py` handles data modeling & basic FIFO operations
   - `priority.py` handles heap-based ordering
   - `retry.py` handles failure recovery & timing logic
   - `__init__.py` acts as a facade for the public API
2. **Type Safety**: Full type hints with `Optional`, `Callable`, and modern `list/tuple` syntax (Python 3.9+)
3. **Priority Tie-Breaking**: The counter in `PriorityQueue` ensures stable sorting (FIFO for equal priorities)
4. **Relative Imports**: Prevents circular dependencies and works correctly when the package is installed or run as a module
5. **Extensible**: Each module can be tested independently, and new queue types or retry strategies can be added without touching existing code